Salesforce Einstein Analytics is a bunch of intelligent tools that helps organizations gain insights from data and make better decisions. Salesforce Einstein Analytics provides visualization, data exploration, and predictive analytics capabilities for the business.

Why Do You Need Salesforce Einstein Analytics?

Salesforce CRM provides exceptional reporting capabilities compared to the other CRMs on the market, but it is not designed to analyze complex and large data. That’s where Salesforce Einstein Analytics comes in – with these data manipulation and visualization capabilities, we can explore large and complex datasets quickly and easily.

A Salesforce admin or reporting analyst must be familiar with all limitations of Salesforce reporting and dashboards, such as the maximum of four object relationships for a report type, or to build a dashboard using the same data sources needed to create reports. These limitations disappear when using Salesforce Einstein Analytics; instead, we can make this advanced logic into the data flow.

Salesforce Einstein Analytics-Data Sources

There are multiple options for loading data into Salesforce Einstein Analytics – directly from Salesforce objects, a Salesforce report, manually loading data using a CSV file, or external data sources, including other Salesforce Orgs.

We have the option to enable or disable data sync between Salesforce and Einstein Analytics. This sync is only for fields and objects used in Einstein Analytics; however, to fetch external data, we must enable sync. When the sync is enabled, data used in Salesforce Einstein Analytics will be synced from Salesforce (not all data, only the required Salesforce data).

Salesforce

Salesforce Einstein Analytics Components

App:

An app can be considered a folder, but an app can have multiple items, like datasets, lenses, and dashboards.

Like a Salesforce report or dashboard folder functions, an app can be shared by username, role, or group, and each one of them with a different level of access: manager, editor, or viewer.

Salesforce

Dashboard

A Salesforce Einstein Analytics dashboard is made up of multiple widgets, like a component on a Salesforce dashboard.

A dashboard is a place where users can explore and analyze widgets. One of the most valuable features of the Analytics dashboard is faceting. Widgets using the same or linked datasets are linked with facets by default. E.g., when a user selects a bar chart, the selected values will be broadcasted to other widgets. Suppose the recipient widgets are enabled with filters that can be used to apply filters from faceting. In that case, those widgets will be filtered, too, allowing the users to quickly analyze the data.

Salesforce

Lens:

The lens is like a Salesforce report that is present behind a dashboard, but the difference is that a dashboard does not use a Lens as a data source in Salesforce Einstein Analytics. Dashboard widgets can be explored as a lens by users and they can be stored in an app for future use, including the My Private app.

Salesforce

Dataflow:

A Dataflow is a chain of connected nodes for data transformation to build Datasets. Ideally, we should start with getting data for transformation from the existing dataset, any Salesforce object, or any external data. Each data transformation node has specific functions, from getting data, augmenting, computing, to registering, etc. As per the dashboard requirements, we can craft the dataflow to orchestrate data transformation and the result of dataflow creates Datasets for the dashboard.

Salesforce

Salesforce Einstein Analytics Use Cases

  • Einstein Commerce Insights: Salesforce Einstein Analytics provides user behavior-specific offerings on products, product bundles, and deals by leveraging Customer persona-specific Cart analysis and more.
  • Einstein Predictive Sort: Salesforce Einstein Analytics provides a customized view for web pages like search and category pages in the blink of an eye based on Customer behavior.
  • Einstein Recommendations: Einstein provides automated suggestions on connection opportunities based on member likes, behavior, and interests.

Einstein’s recommendations also enable an insight-driven approach to increase order values, convert visitors, and recommend the next best action.

  • Einstein Feed Insights: Einstein provides feed insights that lets you set the most preferred content on top for further promotion.
  • Einstein Expert: If we have a community where users look up answers to their questions, we can improve their experience by using Salesforce Einstein experts to connect users with experts based on issue type/subject/product, etc.
  • Einstein Engagement Scoring: It is a real-time track on your email and website-based communications to define the next stage of our customer journey with insights on why customers perform an action like clicks or purchases. It also aids while defining future customer segments, content, and journeys. 
  • Einstein Journey Insights: Artificial Intelligence-enabled analysis of Customer Journey touchpoints to determine which messages, channels, and events perform the best.
  • Einstein Lead and Opportunity Scoring: Lead and Opportunity scoring engine gives us a view of how strong a given lead or opportunity in our org is, so that we can make correct judgments before we start working on a Lead or Opportunity.
  • Einstein Account and Opportunity Insights: Einstein keeps real-time track of the developments on our opportunity and Customer account to guide us with Deal closure probability with details like customer sentiment, competitor involvement, and company updates.
  • Other Components: There are other predefined components such as automated contracts to reduce data entry efforts while creating contacts, Salesforce Sales Analytics to enable proactive insights on the opportunity pipeline, and Salesforce Sales Inbox to extend the functionalities of the Sales cloud to the email.

Salesforce Analytics Query Language (SAQL)

SAQL is being intensely used as a query language in Salesforce Einstein Analytics.

It is used to access data in CRM Analytics dataset and CRM Analytics uses SAQL behind the scenes in lenses and dashboards to gather data for visualizations, in addition to being used to access CRM Analytics data via CRM Analytics REST API directly.

Salesforce  Salesforce

Implementation of SAQL Outside Salesforce Einstein Analytics

When we need to access the analytics data outside the analytics (REST API), we must have two things mandatorily DatasetId and Dataset Version_Id.

Salesforce

Problem:

The challenge is that the Dataset Version_ID gets changed every-time data is refreshed by scheduled jobs and we can’t directly get this Dataset Version_ID from any query. A REST API call can retrieve this from the Workbench. This becomes a manual activity to get and set the Dataset Version_ID somewhere in Salesforce (custom setting or custom metadata) every-time data is being refreshed. We can use this inside our SAQL to fetch the data from the dataset.

Solution:

Salesforce provides a way to get the dataset details directly from analytics wave API (getDataset) inside a Lightning Web Component (LWC). We must pass the dataset API name and get the dataset and Dataset Version_Id. Using this, we can fetch the data directly from the dataset using another analytics wave API (executeQuery) inside LWC.

Pre-Requisites:

  • An Einstein Enabled Org is required.
  • Create a Dataset (In this case, we created a dataset with Account Object and elected a few fields - AccountId, Account Name, and Account Source).

Demonstration

Provided below is a simple LWC code and functionality that uses these two analytics wave API platforms to query the data from the dataset and display it in a tabular format. The best part of this is that no Apex class is involved.

Functionality

Users can see 20 records from the dataset. These records show the Account data, including AccountId, Account Name, and Account Source.

Users can type into the search field, and behind the scenes, SAQL will trigger to fetch the data matching the respective fields.

Salesforce

Functionality Video:

Video file

Code Base:

analyticsPOC.html

<template>
    <lightning-card>
        <div class="slds-p-around_small slds-p-top_none slds-scrollable" style="height:350px;">
            <h2 id="element-with-table-label" class="slds-text-heading_medium slds-m-bottom_x-small slds-p-left_small">Analytics Dataset with LWC</h2>
            <table class="slds-table slds-table_cell-buffer slds-table_bordered"
                aria-labelledby="element-with-table-label other-element-with-table-label">
                <thead>
                    <tr class="slds-line-height_reset">
                        <th class="" scope="col" style="width:250px;">
                            <div class="slds-truncate slds-p-left_x-small" title="Account Id">Account Id</div>
                        </th>
                        <th class="" scope="col" style="width:250px;">
                            <div class="slds-truncate slds-p-left_x-small" title="Account Name">Account Name</div>
                        </th>
                        <th class="" scope="col" style="width:250px;">
                            <div class="slds-truncate slds-p-left_x-small" title="Account Source">Account Source</div>
                        </th>
                    </tr>
                    <tr class="slds-line-height_reset">
                        <th class="" scope="col">
                            <lightning-input type="search" variant="label-hidden" data-name="Id" onchange={handleSearch} placeholder="Search Account Id"></lightning-input>
                        </th>
                        <th class="" scope="col">
                            <lightning-input type="search" variant="label-hidden" data-name="Name" onchange={handleSearch} placeholder="Search Account Name"></lightning-input>
                        </th>
                        <th class="" scope="col">
                            <lightning-input type="search" variant="label-hidden" data-name="AccountSource" onchange={handleSearch} placeholder="Search Account Source"></lightning-input>
                        </th>
                    </tr>
                </thead>
                <tbody class="slds-scrollable">
                    <template for:each={data} for:item="item">
                        <tr key={item.Id} class="slds-hint-parent">                        
                            <td data-label="Account Id">
                                <div class="slds-truncate slds-p-left_small" title={item.Id}>{item.Id}</div>
                            </td>
                            <td data-label="Account Name">
                                <div class="slds-truncate slds-p-left_small" title={item.Name}>{item.Name}</div>
                            </td>
                            <td data-label="Account Source">
                                <div class="slds-truncate slds-p-left_small" title={item.AccountSource}>{item.AccountSource}</div>
                            </td>
                        </tr>
                    </template>            
                </tbody>
            </table>
        </div>        
    </lightning-card>    
</template>

analyticsPOC.js:

import { LightningElement, wire, track } from 'lwc';
import { getDataset, executeQuery } from 'lightning/analyticsWaveApi';
const DATASET_NAME = 'Opportunity_Dataset';

export default class AnalyticsPOC extends LightningElement {
    @track datasetId;
    @track datasetVersionId;
    @track query;
    data;
    @track searchMap= [];
    @wire(getDataset, {
        datasetIdOrApiName: DATASET_NAME
    })
    onGetDataset({data, error}) {
        if (error) {
            console.log('ERROR:', error);
        } else if (data) {
            this.datasetId = data.id;
            this.datasetVersionId = data.currentVersionId;
        }
    }

    get computedQuery() {
        if(this.datasetId && this.datasetVersionId){
            let query = 'q = load "' + this.datasetId + '/' + this.datasetVersionId + '";';
            console.log(this.searchMap);
            console.log(this.searchMap.length);
            this.searchMap.forEach(item => {
                query += (item['field'] == 'Id') ? 'q = filter q by Id like "%'+ item['value'] +'%";' : '';
                query += (item['field'] == 'Name') ? 'q = filter q by Name like "%'+ item['value'] +'%";' : '';
                query += (item['field'] == 'AccountSource') ? 'q = filter q by AccountSource like "%'+ item['value'] +'%";' : '';
            });
            query += 'q = group q by Id;';
            query += 'q = group q by Name;';
            query += 'q = group q by AccountSource;';
            query += 'q = foreach q generate Id, Name, AccountSource;';
            query += 'q = limit q 20;';
            console.log(query);
            this.query = query;
        }
        
        if (!this.query) { return undefined; } 
        return {
            query: this.query
        }
    }
    
    @wire(executeQuery, {
        query: '$computedQuery'
    })
    onExecuteQuery({data, error}) {
       if (error) {
         console.log('Error:', JSON.parse(JSON.stringify(error)));
       } else if (data) {
        this.data = data.results.records;
         //console.log(JSON.parse(JSON.stringify(this.data)));
       }
    }

    handleSearch(event){
        let searchItem = this.searchMap.find((item) => {return item['field'] == event.target.dataset.name});
        if(searchItem){
            searchItem['value'] =  event.target.value;
        }else{
            this.searchMap.push({field:event.target.dataset.name, value: event.target.value});
        }       
    }
}

analyticsPOC.js-meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>55.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

Conclusion

Suppose the organization plans to implement this Salesforce Einstein Analytics dashboard in custom (Look and Feel), using SAQL to get better a Salesforce Customization. The dataset data gets updated promptly by some scheduled jobs. Then it is recommended to use Analytics wave API executeQuery, which is currently only available with LWC support, instead of using it in Apex classes.

About Jade as a Salesforce Consultant

Increase demand, improve overall pipeline, increase selling velocity, improve customer experience, and streamline business processes with Jade Global’s Salesforce offerings. We will help you receive assured business outcomes through our unique market position:

  • Jade is a premier Salesforce Partner, with more than 250+ global CRM consultants and 600+ certifications.
  • The team includes Salesforce experts, architects, developers, data analysts, process integration, and specialists.
  • Proven track record in complex CRM implementation, advisory, integration, and optimization services across the Salesforce CRM, Sales Cloud,Service Cloud,Experience Cloud, Revenue Cloud, Marketing Cloud, Field Service Lightning Cloud, Mulesoft, and AppExchange solutions.
  • Dedicated customer success management and program management team for every engagement resulting in 4.9+ CSAT rating out of 5.
  • Additional partnerships with OracleMicrosoft, Copado, Tableau, Mulesoft, and Boomi give us rich integration capabilities with leading ERP, HCM, Integration, Dev Ops, and Analytics solution
  • 20+ Industry and Technical Accelerators to implement solutions faster.
  • Industry-leading Revenue Cloud capabilities, including Approved Billing Cloud implementation partner.

Subscribe to our email Newsletter

Popular Posts

Tags

About the Author

profile image

Akash Biswas

Senior Technical Analyst, Enterprise Cloud Apps- Salesforce, Jade

Akash Biswas has 6 years of experience in Salesforce Development-Configuration and Customization (Salesforce Einstein Analytics). In the last 6 years, he has extensively worked on Salesforce Development in various clouds, including Sales and Community.

How Can We Help You?