Einstein Analytics: Demystifying Bindings – Part 2


In the first part of this blog series, we covered the anatomy of a binding by looking at each component. Now, to be honest, most bindings you will write, at least in the beginning, will be using the data serialization functions .asString() and .asObject(), as they cover the most common use cases and is all you need when your query is in compact form. But (yes there is a but here) when we use SAQL queries we have to start including other data serialization functions depending on what we are trying to do.

The Meaning of Data Serialization

Before going into the different functions and how they are used let’s take a step back. It’s important to remember why we use data serialization functions and what purpose they have in a binding. If you read the first part of this blog series you might remember that data serialization helps change the output of the binding to a format that is used in the query. And if you didn’t read the first part yet I would strongly recommend you do, since this builds on the learnings from that blog. Now when we use compact form the different parts of the query is similar regardless of it being a measure, grouping or filter, which is why we can just use .asString() and .asObject(). However, when we switch to SAQL the syntax of a query is very different and the query is expecting the bindings to deliver the input in a very specific format, hence why we have other data serialization functions we can leverage. If you are finding this blog post interesting I assume you have knowledge of SAQL but for clarity let’s have a look at a sample SAQL query. Notice I have used the standard DTC Opportunity dataset from the Trailhead org in case you want to generate the examples yourself.

q = load "DTC_Opportunity_SAMPLE";

q = filter q by date('Close_Date_Year', 'Close_Date_Month', 'Close_Date_Day') in ["1 year ago".."1 year ago"];

q = filter q by 'Account_Type' == "Customer";

q = filter q by 'Amount' >= 2910928 && 'Amount' <= 8577295;

q = group q by 'Industry';

q = foreach q generate 'Industry' as 'Industry', sum('Amount') as 'sum_Amount';

q = order q by 'Industry' asc;

q = limit q 2000;

As you can see it does have a specific syntax and in order for our binding to work we need to make sure the syntax is followed.

Now it’s important to note that the skeleton of the binding is the same as we covered in the first part of this blog series meaning our binding will still include a data selection function, step name of the source, binding function and details from the reference step. But to quickly refresh a binding is structured as follows:

"{{Data_Selection_Function(step_name.Binding_Function, Details_From_Reference_Step).Data_Serialization_Function()}}"


So which functions do we have available? Well, .asWhat() is definitely not one of them, but there is quite a few in order to support all part of a SAQL query. If you look at the documentation in addition to .asString() and .asObject() you’ll see that we can use:

  • .asDateRange()
  • .asRange()
  • .asEquality()
  • .asGrouping()
  • .asProjection()
  • .asOrder()

In this part let’s have a look at examples of how we can use bindings in our SAQL filters, we will save the rest for another blog.

Filter bindings

Most SAQL queries start with filters, so let’s have a look at the options we have there; .asDateRange(), .asRange() and .asEquality(). All bindings start with ‘q = filter q by’ followed by the filter logic, it is the filter logic which we will replace with a binding. Depending on the type of filter we can use one of the mentioned data serialization functions. Each one of my sample bindings is for simplicity using a static step as a source and for transparency, the values of the static step will be displayed in the sample. Hence the user needs to pick a value where the output for a selection is just one column and row, which means I can use the cell() as the data selection function and I, of course, need to use a selection binding and not a result binding.

The Date Binding

The date filter in SAQL looks something like below. As you can see we need to define the date components and the start and end period.

q = filter q by date('Close_Date_Year', 'Close_Date_Month', 'Close_Date_Day') in ["1 year ago".."1 year ago"];

In order to get that specific format we can use the .asDateRange(). As you can see below the static step that I am using will contain a start (min) and end (max) period, which will be used in the binding.



"display": "CY",


"current year",

"current year"




"display": "LY",


"1 year ago",

"1 year ago"




Let’s have a close look at how the binding would look like, as you can see the only change is in the data serialization, which is highlighted below.

q = filter q by {{cell(StaticFilterDate_1.selection, 0, "value").asDateRange("date('Close_Date_Year', 'Close_Date_Month', 'Close_Date_Day')")}};

In the parentheses after .asDateRange, we need to define the date field we need to match the time period with. In a date filter without a binding you will notice that we are using the date components Year, Month and Day and we are using date() to join these components together in a full date field. So we need to add that string of date(‘Year’, ‘Month’, ‘Day’) into the parentheses and of course, remember to escape the string like the example above.

The Range Binding

Adding a filter on a measure where we use the ‘between’ option would In SAQL look like below.

q = filter q by 'Amount' >= 2910928 && 'Amount' <= 8577295;

This type of filter we have a min and a max value just like with the .asDateRange() data serialization function. However, this is not a date but a measure so you can see the syntax is a little different, which also means we have a different data serialization function we can use, which is .asRange(). In the below static step, you will see we have a min and a max value for each selected value.



"display": "Low",






"display": "High",







Looking at the actual binding and data serialization it should look like below.

q = filter q by {{cell(StaticFilterRange_1.selection, 0, "value").asRange("Amount")}};

Similarly to the date binding we need to define the field we want to apply the range to by adding the API name of that field within the parentheses – in this case, ‘Amount’. Also in this example, we need to escape the string which is why we are using the backward slashes and double quotes.

The Equality Binding

The most commonly used filter is the equality filter where one field or column is equal to a specific value. If we look at a SAQL example that could look like below.

q = filter q by 'Account_Type' == "Customer";

The way we ensure the correct format for the binding is to use the .asEquality() data serialization. My static step only needs to contain the value I want to pass on in my binding. You can see the values below.



"display": "Account Type: Partner",

"value": "Partner"



"display": "Account Type: Customer",

"value": "Customer"



The binding we need to use is looking something like below.

q = filter q by {{cell(StaticFilterDim_1.selection, 0, "value").asEquality("Account_Type")}};

Just as the two previous examples you will notice that we are just adding the API name of the field we want to match our value with within the parentheses, which is called ‘Account_Type’. Like the other examples, we also need to escape this string.

Range Bindings Without a Static Step

As mentioned for simplicity I used static steps as my source values, however, you can, of course, use other step types as well. When you are looking at binding ranges like with dates and measures we are looking for a min and a max value in order for it to work. Now in the static step, we have combined min and max as one and called it value, which means we are able to use cell() as the data selection function. In other use cases, you may want to use the specific widgets for dates and ranges, in this case, the output would be two values or columns ‘min’ and ‘max’, which we would need to use in our binding. Since we need to use both columns we cannot use the cell() data selection function and instead we need to use the row() data selection function.

We didn’t cover the row() data selection function in the previous blog post, however, the components are somewhat the same. Below you will see how the binding would look like for both .asDateRange() and .asRange(). The highlighted parts are showing the difference from before where we used cell() as the data selection function and a static step as the source.

{{row(Close_Date_1.selection, [0], ["min", "max"]).asDateRange("date('Close_Date_Year', 'Close_Date_Month', 'Close_Date_Day')")}}
{{row(Amount_1.selection, [0], ["min", "max"]).asRange("Amount")}}

When we are using the row() data selection function we need to define which row(s) we want to take data from, which is done right after the binding function. In the example we are using [0], meaning we are taking the very first row. We could define multiple rows separating them with a comma or leave the array blank including all rows in the binding.

Following the row selection, we need to define the columns or fields we want to take. Since the output of a range and a date widget is ‘min’ and ‘max’ we are adding those values in the array and of course escaping the values. Other than that the binding stays as when we used static steps and the cell() data selection function.

This is how you can use the data serializations .asDateRange(), .asRange() and .asEquality() to make your SAQL query dynamic.

In the third part of this blog series, we will be looking at .asGrouping() and .asProjection().

How useful was this post?

Click on a star to rate useful the post is!

Written by

7 thoughts on “Einstein Analytics: Demystifying Bindings – Part 2”

  • 1
    Liam O'Connor on June 27, 2019 Reply

    Hi Rikke,

    Your blog is great – thanks for sharing your knowledge with the world!

    I’m wondering if I might ask you a question about bindings within a saql query. I’m struggling to find the answer I need.

    I have a number widget and I want this to re-calibrate based on the toggle filter on my dashboard.

    The number widget – formatted as a % – requires two datasets to be combined. Below is what I have added to the query but this is not working:

    && ‘OwnerId_Name’ in {{column(Global_Filter_1.selection,[\”Account__c.OwnerId.Name\”]).asObject()}

    Do you know what I’m doing wrong?

    Thanks in advance for any advise you can offer,


  • 2
    Cezary on January 26, 2021 Reply


    Thanks a lot for your posts about binding, they are very helpful. I wonder whether it is possible to create a filter binding with range of values defined by the user. So for instance I have the Revenue Growth field (Based on Amount field) and I’d like to create a filter where user can filter value to see positions only above and/or below some values: for instance user would type that wants to see only revenue growth > 50% but below 100%?

  • 3
    James Roberts on June 14, 2021 Reply

    Thanks for these blog posts, they have been really useful. Could you let me know if it is possible to use the Toggle option to swap between 3 different queries / steps? I have created 3 queries with different datasets and would like to display them in the same area on the dashboard, but toggle between them. Is there an easy way to do this?

    • 4
      Rikke on June 14, 2021 Reply

      I’ve done a swap of the whole SAQL query before. But with the new UI options may I suggest component widget with three pages? Much more simple.

  • 5
    Suyog Srivastava on June 8, 2022 Reply

    Hello Rikki,
    Nice Blog, learned a lot. Wanted to ask- Can we bind widgets on the dashboard with salesforce direct objects.? Cause I tried but it comes out as SOQL query. If it is possible then how do we achieve it.?

    • 6
      Rikke on June 8, 2022 Reply

      You can, you just need to use a different data serialization. Check this out.

  • 7
    Frank Primerano on April 14, 2024 Reply

    For the filter bindings I have a static step for all Picklist values that im trying to Filter by . The specific ones work but how do I do ALL Values

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.