Sunday, March 29, 2020

Salesforce: EmailMessage object

When Enhanced Email is enabled, Salesforce will create EmailMessage object. Emails sent from Salesforce are saved as Email Message records and Task records. There is a link from the Email Message record to a Task record, which is ActivityId field.

If you use the Outlook panel (and without enabling EAC), you can "Log Email" manually to Salesforce for email received and sent out. Both emails received and sent will be stored both as EmailMessage (prefix 02s) record and Task record (prefix 00T).

How to differentiate emails sent from Salesforce and manually logged from an email client?
You will not find any difference on the Task object, both Type and TaskSubType will populate with "Email". But there are some differences in the Email Message object. Check out this query:
SELECT Id, ActivityId, FromAddress, ToAddress, FromName, IsClientManaged, MessageIdentifier, Subject, TextBody FROM EmailMessage

Row 1,4,5 - email manual log from Outlook
Row 2,3 - email sent from Salesforce

As you can see, IsClientManaged and MessageIdentifier are different.

Note: using the legacy My Email to Salesforce service (BCC) will not create Email Message records, but only Task.

Email in Lightning activity component
All emails logged to Contact or Lead will be shown in the Lightning under the activity component.

For emails manually logged and sent from Salesforce, clicking the email will open the EmailMessage record, and you will be able to Reply All, Reply, or Forward the email from that panel. Hover mouse over the email will show the URL where it will be landed, for example,

For email from BCC email services, clicking the email will open a Task record, there is no option to reply, reply all, or forward as the record is a task. Hover mouse over the email will show the URL where it will be landed, for example,

For EAC email, clicking the email will open the email in that panel, you are also able to Reply All, Reply, or Forward the email from that panel. The system also shows if the email is shared with everyone or a group or not shared. Hovering the mouse over the email will not show any URL, click "View full email" will open the full email as an email stream.


Monday, March 23, 2020

Einstein Analytics: using Flatten node to get Account Parent

Here is our scenario, we have multi-level account hierarchy, the sample of accounts for this blog:

Use case 1: display all child accounts (including top parent account) and their opportunities when the top parent is selected.


We need to manually edit the Flatten node from JSON, by default, the Multi Field and Path Field fields are created as system fields, which aren’t visible in the user interface. To make the fields appear in the user interface and dataflow, add a schema section to the flatten transformation and set the IsSystemField metadata attribute to false for each field in the transformation.

"Flatten_UltimateParent": {
    "schema": {
      "objects": [
          "label": "Flatten_UltimateParent",
          "fields": [
              "name": "UltimateParentPath",
              "label": "UltimateParentPath",
              "isSystemField": false
              "name": "AccountParentIds",
              "label": "AccountParentIds",
              "isSystemField": false
    "action": "flatten",
    "parameters": {
      "include_self_id": true,
      "multi_field": "AccountParentIds",
      "path_field": "UltimateParentPath",
      "source": "getAccount",
      "self_field": "Id",
      "parent_field": "ParentId"

Unfortunately, we will not see the schema in the dataflow UI.

We also need to Connect Data Source between Account and Opportunity using the Ultimate Parent Name.

Here is the dashboard:


1) Multi_Field from the flatten nodes will contain self Account Id (we select "Include Self ID" in the flatten node) and all the parents' Id.

Notice that top Parent Id 0018000001BNnOPAA1 is stored in ALL hierarchy, while Account F Id only stored on itself Account F and it child Account G. Don't be trick when AccountParentIds only show 1 value, because this is a multi-values field.

2) Path_Field will show all hierarchy from self Id, up to parent Id, and all the way to the top level.

* I use "Image" (under Show Data As) to show the full length of field content of UltimateParentPath column

Use case 2: display all accounts (including the child accounts) and their opportunities (including from their child accounts) when an Account is selected, the account could be on the top, middle, or bottom level in the account hierarchy.

Dataflow: let us modify existing dataflow to below:

We just need to add an augment node Account Name from Multi_Field AccountParentIds.

You also need to Connect Data Source between Account and Opportunity using the Account Parent Name from the augment node.

Here is the dashboard:

When select Account F, the dashboard will filter to Account F and the child accounts.

Referenceflatten Parameters

Saturday, March 14, 2020

Einstein Analytics: Dataflow Performance Best Practice

Performance is critical for Einstein Analytics dataflow, e.g. an optimized dataflow may take only 10 minutes, while the same dataflow with a poor design may take 1 hour (this includes sync setup) to run. Therefore, without great architected dataflows, it will be hard to maintain and sustain Einstein Analytics as a whole, as the company evolved.

Here are a few items noted based on my personal findings/experience, if you have additional inputs or a different perspective, feel free to reach me.

1. Combine all computeExpression nodes whenever possible



calcURI node in image-1 contains 1 compute field return Numeric, the same for calURI2 node also contains 1 other compute field return Numeric, a total of calcURI1 + calURI2 = 3:41 sec.

In image-2, we combined both compute fields into calcURI node, and it only took 2:0 sec.

2. Do compute as early as possible, and augment as late as possible

The rationale behind this is, compute node will process lesser fields before augment (as augment always adding fields to the stream), unless you need the field from the augment node for computation.

3. Remove all unnecessary fields 

Remove all unnecessary fields with a slice node or not to include the unnecessary fields when do augment from the right source. The more fields are handled by each node, the system will need more power and time to process, so slice out unnecessary fields if they are not needed in the dashboard or lens. 

Register node usually takes much more time when you write lots of fields, so always clean up before registering to a dataset.


Notice that calcURI3 in image-1 and image-2 took around 2:08 sec. In image-3, we add a slice node before calcURI3 to remove unnecessary fields, this reduces the number of fields processed in calcURI3, therefore it took only 1:55 sec.

4. Combine all sfdcDigest nodes of the same object to a node, if sync is not enabled

For some reason, your org. maybe not enable for sync, this does not mean you "must" enable straight away, and please DO NOT enable it without a complete analysis, as this may cause data filtering issue.

You should combine all sfdcDigest nodes of the same object into a node, imagine if you have 10 millions row of opportunity, every sfdcDigest nodes take 10 minutes (as an example), and if the dataflow designer adds 3 sfdcDigest nodes of opportunity, the data retrieve itself will need 30 minutes.

5. Do not perform Null check on filter node
So instead of having something like 'Check.Id' is null in SAQL filter, create a computeExpression node to have a Yes/No compute field, then filter with CheckIdIsNull:EQ:Yes
Filter node with Null check will take a lot of time when the dataflow runs.

6. Remove unused Register node
Many times, we add Register nodes across dataflow for testing/debugging, but once deployed to Production, make sure Register nodes for testing are removed. Register nodes will take quite some time of the dataflow run, depending on the number of fields and rows.

7. Remove all nodes that are not related to a Register node
These nodes are simply useless.

8. Use Source Field in computeRelative node, not SAQL, whenever possible
Check out this blog.

Thursday, March 12, 2020

Einstein Analytics: Precision and Scale

Precision and Scale are important and required for computeExpression node that returns Numeric in Dataflow, otherwise, your dataflow rum will fail.

For numeric, as per this article External Data Metadata Format Reference
  • precision: the maximum number of digits in a numeric value, includes all numbers to the left and to the right of the decimal point (but excludes the decimal point character). Value can be up to 18.
  • scale: the number of digits to the right of the decimal point in a numeric value, must be less than the precision value.

But in short:
  precision: must be 1 - 18
  scale: must be 0 - 17 and less than the precision value

Let us see how this works in reality. I'll do a few same calculations on computeExpression, but with different precision and scale, the formula is A/B for all calculations, here is the result:

Calc_10_5 mean, precision = 10 and scale = 5, and etc. At a glance, you may think that all decimal points do not exist, this is incorrect as you need to "format numbers" on the widget or metadata.

For this blog testing, I set 5 digits decimal point:

Here is the result after all fields set with 5 decimal points:

From the above table, "scale" shows the difference in the calculation result, the result will be round up or round down based on the decimal point defined in the scale.

Notice that decimal point "below 0.5" will be round down, while "0.5 and above" will be round up. But, if scale = 0, all decimal points will be round down, see calc_10_0.

Reference: External Data Metadata Format Reference

Page-level ad