Compare A Time Window Against The Immediately Preceding Time Window Of The Same Size.

Users could sometimes want to compare performance on a particular time period vs the immediately preceding period. Sometimes those periods are not of a common size like months or quarters. It could be a campaign that lasts a few days, or an arbitrary date range based on topics trending on social media. In those cases, you would want to select any time range and be able to compare to a previous period of the same arbitrary size, and be able to change that time range and see the comparison adjust dynamically.
Challenges
  • In Sisense, the RANGE function receives literal numbers, but it doesn’t accept numeric expressions (like Count([Days in Date]).
  • Sisense has last, next and offset operators for filters, but no out of the box ways to adjust them according to the number of members in a filter. 
Solution:
By using the Javascript API and in formula filters, we can make a formula go back the number of days in the filter, and also apply an offset of the same number of days, to get the same data for the preceding time window.
Example:
We want to be able to get the number of orders in an N day date range, and the growth vs the previous N days. For example, if the filter is 2/11/2020 to 2/20/2020, we want the growth vs the 2/1/2020 to 2/10/2020 period. We'll use an indicator widget for this, with the next formulas:
  • Value: Count([OrderID)
  • Secondary: (Count([OrderID]) - (Count([OrderID]),[Days in Date]))/(Count([OrderID]),[Days in Date])
And set the formula filters to one of the predefined “Last N” options:
To learn about filtering in formulas, check the documentation: https://documentation.sisense.com/latest/creating-dashboards/using-formulas/create-formulas.htm
 
Finally, open the Edit Script editor for the widget, and enter the following script. The script will search for formula filters that use the table and column specified at the beginning of the script (and use the last operator), and replace the count and offset with the number of members in the filter. If the calendar was used to set a days filter, it will calculate the difference in days to come up with the number of members:
If you change the filter to a different date range, the filters in the growth formula will adjust dynamically to the new time window.
Script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
var TABLE = "Date";

var COLUMN = "Date";

 

widget.on("beforequery", function(scope, widget) {

// getting item

 var item = widget.query.metadata.find(function(i) { return i.panel === "scope" && $$get(i, "jaql.table") === TABLE && $$get(i, "jaql.column") === COLUMN });

 if (!defined(item)) {

  console.log("DynamicLastX: Can't find a filter item with '" + TABLE + "." + COLUMN +"' dimension. Aborting.");

  return;

 }

 

var calFilter = false;

var calCount = 0;

var dateTo = null;

 // getting member

 var members = $$get(item, "jaql.filter.members");

 if (!defined(members)) {

  console.log("DynamicLastX: No item filter. Assuming calendar was used.");

  calFilter=true;

  var dateFrom = new Date(item.jaql.filter.from + "T00:00:00");

  var dateTo = new Date(item.jaql.filter.to + "T00:00:00");

  var diffTime = dateTo.getTime() - dateFrom.getTime();

  calCount = diffTime / (1000 * 3600 * 24) + 1;

 }

 

 var formulaFilters = [];

 

var filterMember = 0

 var filterCount = 0         

if (!calFilter) {

  filterMember = members[0];

  filterCount = members.length

} else {

   filterMember = item.jaql.filter.to + "T00:00:00";

   filterCount = calCount;

}

 

 widget.query.metadata.forEach(function(item) {

  if ((item.source === 'value' || item.source === 'secondary') && item.jaql && item.jaql.context) {

      for (var lmnt in item.jaql.context) {

    if (item.jaql.context[lmnt].table === TABLE && item.jaql.context[lmnt].column === COLUMN && item.jaql.context[lmnt].filter && item.jaql.context[lmnt].filter.last) {

        formulaFilters.push(item.jaql.context[lmnt].filter);

    }

      }

  }

 })

 if (formulaFilters && filterMember) {

         formulaFilters.forEach(formulaFilter => {

                                   formulaFilter.last = {

              offset: filterCount,

              count: filterCount,

              anchor: filterMember

          };

                               });

 }

});