Filtering

Overview 

Filters define which records a semantic query returns. Apply filters to dimensions, measurements, and calculated fields to narrow your result set. The Semantic Query API supports multiple filter types that operate at different stages of query execution: context filters run before all other filters, standard query filters apply to pre-aggregation data, and aggregate filters (HAVING) operate on post-aggregation results.

The Semantic Query API supports two operator representations:

  • PascalCase operators ("GreaterThan", "In", "StartsWith") in filters[] arrays within flatten_filter structures and model-defined filters.
  • SCREAMING_SNAKE operators (BINARY_OPERATOR_GREATER_THAN, BINARY_OPERATOR_IN) in structured binary_predicate filter objects.

Both forms are semantically equivalent; the wire representation differs by context.

Metadata in the model 

Filters can be defined at authoring time as part of the Semantic Data Model. Model-level filters apply globally to all queries against that model, while Semantic Data Object filters apply only when that object is queried. For details on defining filters in the model, see Semantic Data Object Field in the Authoring API.

Filters are evaluated in layers. Semantic Data Object filters apply first, followed by relationships, then model-level (global) filters. Context filters are applied as an independent pre-filter, and aggregate filters (HAVING) are applied last, after aggregation. Within a single layer, conditions can be combined with OR; between layers, the layers are combined with AND.

Query usage 

Comparison operators 

Use comparison operators to filter numeric, date, datetime, and text fields. Supported operators include equals, not equals, greater than, less than, greater than or equal, and less than or equal. Case-insensitive variants are available for text comparisons.

The structured predicate form uses BINARY_OPERATOR_* constants:

  • BINARY_OPERATOR_EQUALS
  • BINARY_OPERATOR_NOT_EQUAL_TO
  • BINARY_OPERATOR_GREATER_THAN
  • BINARY_OPERATOR_LESS_THAN
  • BINARY_OPERATOR_GREATER_THAN_OR_EQUAL_TO
  • BINARY_OPERATOR_LESS_THAN_OR_EQUAL_TO
  • BINARY_OPERATOR_EQUALS_IGNORE_CASE
  • BINARY_OPERATOR_NOT_EQUALS_IGNORE_CASE
1{
2  "structuredSemanticQuery": {
3    "fields": [
4      {
5        "expression": {
6          "table_field": {
7            "name": "Account Name",
8            "table_name": "AccountSemanticLayer__dll"
9          }
10        }
11      },
12      {
13        "expression": {
14          "table_field": {
15            "name": "Annual Revenue",
16            "table_name": "AccountSemanticLayer__dll"
17          }
18        }
19      }
20    ],
21    "filter": {
22      "logical_binary_predicate": {
23        "left_predicate": {
24          "binary_predicate": {
25            "left_expression": {
26              "table_field": {
27                "name": "Account Name",
28                "table_name": "AccountSemanticLayer__dll"
29              }
30            },
31            "binary_operator": "BINARY_OPERATOR_NOT_EQUAL_TO",
32            "right_expression": {
33              "string_expression": "Richard Schneider"
34            }
35          }
36        },
37        "logical_binary_operator": "LOGICAL_BINARY_OPERATOR_AND",
38        "right_predicate": {
39          "binary_predicate": {
40            "left_expression": {
41              "table_field": {
42                "name": "Annual Revenue",
43                "table_name": "AccountSemanticLayer__dll"
44              }
45            },
46            "binary_operator": "BINARY_OPERATOR_GREATER_THAN",
47            "right_expression": {
48              "int_expression": 0
49            }
50          }
51        }
52      }
53    },
54    "options": {
55      "detailed_rows": true
56    }
57  },
58  "semanticModelId": "2SMxx0000004CAeGAM"
59}

Text operators 

Filter text fields using pattern-matching operators. Text operators support case-sensitive and case-insensitive variants.

In flatten_filter PascalCase form:

  • Contains / ContainsIgnoreCase
  • NotContains / DoesNotContainIgnoreCase
  • StartsWith / StartsWithIgnoreCase
  • DoesNotStartWith / DoesNotStartWithIgnoreCase
  • EndsWith / EndsWithIgnoreCase
  • DoesNotEndWith / DoesNotEndWithIgnoreCase

In structured predicate SCREAMING_SNAKE form:

  • BINARY_OPERATOR_CONTAINS
  • BINARY_OPERATOR_STARTS_WITH
  • BINARY_OPERATOR_ENDS_WITH

(with corresponding _IGNORE_CASE and NOT_* variants)

1{
2  "structuredSemanticQuery": {
3    "fields": [
4      {
5        "expression": {
6          "table_field": {
7            "name": "semantic__Name__c",
8            "table_name": "SemanticAccount__dlm"
9          }
10        }
11      }
12      // ...
13    ]
14  },
15  "semanticModel": {
16    "apiName": "Sales",
17    "label": "Sales",
18    "semanticDataObjects": [
19      {
20        "apiName": "SemanticAccount__dlm",
21        "dataObjectName": "ssot__Account__dlm",
22        "dataObjectType": "Dmo",
23        "filterLogic": "1",
24        "filters": [
25          {
26            "fieldName": "SemanticAccount__dlm.semantic__Name__c",
27            "operator": "StartsWith",
28            "value": "Richard"
29          }
30        ],
31        "semanticDimensions": [
32          // ...
33        ]
34      }
35    ]
36  }
37}

Set-membership operators 

Use In and NotIn operators to filter against a list of values. The Values operator is deprecated; use In instead.

In flatten_filter PascalCase form: "operator": "In" or "operator": "NotIn" with a "values": [...] array.

In structured predicate form: BINARY_OPERATOR_IN or BINARY_OPERATOR_NOT_IN.

1{
2  "semanticModel": {
3    "apiName": "Sales",
4    "semanticDataObjects": [
5      {
6        "apiName": "SemanticAccount__dlm",
7        "filterLogic": "1",
8        "filters": [
9          {
10            "fieldName": "SemanticAccount__dlm.semantic__Name__c",
11            "operator": "NotIn",
12            "values": ["InvalidName", "InvalidName2"]
13          }
14        ],
15        "semanticDimensions": [
16          // ...
17        ]
18      }
19    ],
20    "semanticCalculatedMeasurements": [
21      {
22        "apiName": "inner_formula_measurement_example",
23        "filterLogic": "1",
24        "filters": [
25          {
26            "fieldName": "SemanticAccount__dlm.Semantic_KQ_Id__c",
27            "operator": "In",
28            "values": [50, 100]
29          }
30        ],
31        "expression": "100 + SemanticAccount__dlm.Annual_Revenue_Amount"
32      }
33    ]
34  }
35}

Performance: Use In and NotIn with a value list instead of chaining Equals conditions with OR.

Null and empty operators 

Distinguish between IsNull / IsNotNull (any data type) and IsEmpty / IsNotEmpty (text fields only). IsEmpty matches both NULL and empty string "", while IsNotEmpty excludes both.

In PascalCase form: "operator": "IsNull", "IsNotNull", "IsEmpty", "IsNotEmpty".

In SCREAMING_SNAKE form: BINARY_OPERATOR_IS_NULL, BINARY_OPERATOR_IS_NOT_NULL, BINARY_OPERATOR_IS_EMPTY, BINARY_OPERATOR_IS_NOT_EMPTY.

Between operator for ranges 

Use Between (or BINARY_OPERATOR_BETWEEN) to filter numeric, date, or datetime fields within an inclusive range. Provide both boundaries in rangeValues with lower_boundary and upper_boundary.

In PascalCase form (model filters): "operator": "Between", "value": "lower | upper" (pipe-delimited string).

In structured form: "binary_operator": "BINARY_OPERATOR_BETWEEN", "right_expression": { "range_values": { "lower_boundary": {...}, "upper_boundary": {...} } }.

1{
2  "semanticModel": {
3    "semanticCalculatedMeasurements": [
4      {
5        "apiName": "inner_formula_dimension_example",
6        "dataType": "Date",
7        "expression": "SemanticAccount__dlm.created_Date",
8        "filterLogic": "1",
9        "filters": [
10          {
11            "fieldName": "SemanticAccount__dlm.created_Date",
12            "operator": "Between",
13            "value": "2024-01-01T17:00:00.000-07:00 | 2023-10-10T17:00:00.000-07:00"
14          }
15        ]
16      }
17    ]
18  }
19}

Performance: Use Between for a range instead of separate GreaterThanOrEqual and LessThanOrEqual conditions; a single range predicate lets the engine prune partitions on date and numeric columns.

Date and time-range filters 

Filter date and datetime fields using BINARY_OPERATOR_BETWEEN with range_values. Specify ISO 8601 datetime strings in datetime_expression for boundaries.

1{
2  "structuredSemanticQuery": {
3    "fields": [
4      {
5        "expression": {
6          "table_field": {
7            "name": "Account ID",
8            "table_name": "ContactSemanticLayer__dll"
9          }
10        }
11      },
12      {
13        "expression": {
14          "table_field": {
15            "name": "Created Date",
16            "table_name": "ContactSemanticLayer__dll"
17          }
18        }
19      }
20    ],
21    "filter": {
22      "binary_predicate": {
23        "left_expression": {
24          "table_field": {
25            "name": "Created Date",
26            "table_name": "ContactSemanticLayer__dll"
27          }
28        },
29        "binary_operator": "BINARY_OPERATOR_BETWEEN",
30        "right_expression": {
31          "range_values": {
32            "upper_boundary": {
33              "datetime_expression": "2021-12-01T00:00:00Z"
34            },
35            "lower_boundary": {
36              "datetime_expression": "2021-01-31T00:00:00Z"
37            }
38          }
39        }
40      }
41    },
42    "options": {
43      "detailed_rows": true
44    }
45  },
46  "semanticModelId": "2SMxx0000004CAeGAM"
47}

The datetime_expression value must be an ISO 8601 formatted timestamp that includes a time zone offset (either Z for UTC or a ±hh:mm offset). Fractional seconds are optional and support up to nanosecond precision. Accepted formats include:

  • 2023-01-10T16:00:00.000-08:00
  • 2023-01-10T16:00:00Z
  • 2023-01-10T16:00:00.123456789+05:30
  • 2023-01-10T00:00:00-08:00

Relative date filters compute dynamic date ranges based on an anchor date (defaulting to the current date). Use relativeDateRange with datePart, startOffset, and endOffset to define the window.

Supported datePart values:

  • Time: Minute, Hour, Day, Week, Month, Quarter, Year
  • Fiscal: Fiscal_Week, Fiscal_Month, Fiscal_Quarter, Fiscal_Year
  • Localized: Localized_Week

Set useCurrentDateAsEndDate: true to anchor the range to the current date, or provide an explicit anchorDate as an ISO 8601 datetime string.

1{
2  "structuredSemanticQuery": {
3    "filter": {
4      "binaryPredicate": {
5        "leftExpression": {
6          "tableField": {
7            "name": "CreatedDate",
8            "tableName": "SemanticAccount_SDO"
9          }
10        },
11        "binaryOperator": "BINARY_OPERATOR_BETWEEN",
12        "rightExpression": {
13          "relativeDateRange": {
14            "datePart": "Day",
15            "useCurrentDateAsEndDate": true,
16            "startOffset": -30
17          }
18        }
19      }
20    },
21    "fields": [
22      // ...
23    ]
24  },
25  "semanticModelId": "2SMxx0000004CAeGAM"
26}

Filter logic with AND / OR / NOT 

Combine multiple filters using logical operators. In structured predicate form, use logical_binary_predicate with logical_binary_operator (LOGICAL_BINARY_OPERATOR_AND, LOGICAL_BINARY_OPERATOR_OR, LOGICAL_BINARY_OPERATOR_NOT). In flatten_filter form, use a filterLogic string with filter indices and parentheses (e.g., "(1 AND 2) OR 3").

1{
2  "structuredSemanticQuery": {
3    "filter": {
4      "logical_binary_predicate": {
5        "left_predicate": {
6          "binary_predicate": {
7            "left_expression": {
8              "table_field": {
9                "name": "Account Name",
10                "table_name": "AccountSemanticLayer__dll"
11              }
12            },
13            "binary_operator": "BINARY_OPERATOR_NOT_EQUAL_TO",
14            "right_expression": {
15              "string_expression": "Richard Schneider"
16            }
17          }
18        },
19        "logical_binary_operator": "LOGICAL_BINARY_OPERATOR_AND",
20        "right_predicate": {
21          "logical_binary_predicate": {
22            "left_predicate": {
23              "binary_predicate": {
24                "left_expression": {
25                  "table_field": {
26                    "name": "Annual Revenue",
27                    "table_name": "AccountSemanticLayer__dll"
28                  }
29                },
30                "binary_operator": "BINARY_OPERATOR_GREATER_THAN",
31                "right_expression": {
32                  "int_expression": 0
33                }
34              }
35            },
36            "logical_binary_operator": "LOGICAL_BINARY_OPERATOR_OR",
37            "right_predicate": {
38              "binary_predicate": {
39                "left_expression": {
40                  "table_field": {
41                    "name": "Account Name",
42                    "table_name": "AccountSemanticLayer__dll"
43                  }
44                },
45                "binary_operator": "BINARY_OPERATOR_NOT_EQUAL_TO",
46                "right_expression": {
47                  "string_expression": "Sherry Armstrong"
48                }
49              }
50            }
51          }
52        }
53      }
54    },
55    "fields": [
56      // ...
57    ],
58    "options": {
59      "detailed_rows": true
60    }
61  },
62  "semanticModelId": "2SMxx0000004CAeGAM"
63}

Context filters 

Context filters apply before all other query filters (except Semantic Data Model and Semantic Data Object filters). Use semantic_context.context_filter (or the deprecated top-level context_filter) to define an independent pre-filter that all subsequent filters build upon.

1{
2  "structuredSemanticQuery": {
3    "fields": [
4      {
5        "expression": {
6          "table_field": {
7            "name": "sdm__Name__c",
8            "table_name": "SemanticAccount__dlm"
9          }
10        },
11        "alias": "Account Name",
12        "row_grouping": true
13      }
14    ],
15    "context_filter": {
16      "simple_dimension_filter": {
17        "binary_predicate": {
18          "left_expression": {
19            "table_field": {
20              "name": "sdm__IsActive__c",
21              "table_name": "SemanticAccount__dlm"
22            }
23          },
24          "binary_operator": "BINARY_OPERATOR_EQUALS",
25          "right_expression": {
26            "bool_expression": true
27          }
28        }
29      }
30    }
31  },
32  "semanticModel": {
33    // ...
34  }
35}

Performance: Use a context_filter for high-selectivity baseline conditions. Context filters apply before other query filters, shrinking the working set for the whole query.

Flatten filters 

Flatten filters combine multiple filter conditions using a logical expression string. Define an array of filter objects in filters[], each with a PascalCase operator, fieldName, and value or values. Reference filters by 1-based index in the filterLogic string (e.g., "(1 AND 2) OR (3 AND 4)").

1{
2  "structuredSemanticQuery": {
3    "fields": [
4      {
5        "expression": {
6          "table_field": {
7            "name": "Name",
8            "table_name": "SemanticAccount_SDO"
9          }
10        },
11        "row_grouping": true,
12        "alias": "account"
13      },
14      {
15        "expression": {
16          "table_field": {
17            "name": "MailingCity",
18            "table_name": "SemanticContact_SDO"
19          }
20        },
21        "row_grouping": true
22      }
23    ],
24    "filter": {
25      "flattenFilter": {
26        "filters": [
27          {
28            "fieldName": "formula_measurement_example",
29            "operator": "GreaterThan",
30            "value": "80000"
31          },
32          {
33            "fieldName": "SemanticAccount_SDO.AnnualRevenue",
34            "operator": "GreaterThan",
35            "value": "90000"
36          },
37          {
38            "fieldName": "[SemanticContact_SDO].[MailingLatitude]",
39            "operator": "GreaterThan",
40            "value": "1",
41            "fieldType": "FORMULA"
42          }
43        ],
44        "filterLogic": "(1 AND 2) OR 3"
45      }
46    },
47    "options": {
48      "subtotals": true,
49      "grand_total": true
50    }
51  },
52  "semanticModel": {
53    // ...
54  }
55}

Aggregate filters (HAVING) 

Aggregate filters apply to post-aggregation results, equivalent to SQL HAVING clauses. Use aggregate_filter with a binary_predicate that references an aggregated expression (typically a calculated field with an aggregation function).

1{
2  "structuredSemanticQuery": {
3    "fields": [
4      {
5        "expression": {
6          "table_field": {
7            "name": "Account Name",
8            "table_name": "AccountSemanticLayer__dll"
9          }
10        },
11        "alias": "Account Name",
12        "row_grouping": true
13      },
14      {
15        "expression": {
16          "table_field": {
17            "name": "Annual Revenue",
18            "table_name": "AccountSemanticLayer__dll"
19          }
20        },
21        "semantic_aggregation_method": "SEMANTIC_AGGREGATION_METHOD_SUM",
22        "alias": "Annual Revenue"
23      }
24    ],
25    "aggregate_filter": {
26      "binary_predicate": {
27        "left_expression": {
28          "calculated_field": {
29            "name": "summary",
30            "expression": "1+sum(AccountSemanticLayer__dll.AnnualRevenue__c)",
31            "calculated_measure_expression": {
32              "measure_output_type": "SEMANTIC_MEASUREMENT_TYPE_NUMBER"
33            }
34          }
35        },
36        "binary_operator": "BINARY_OPERATOR_GREATER_THAN",
37        "right_expression": {
38          "int_expression": 5
39        }
40      }
41    }
42  },
43  "semanticModelApiName": "test_model"
44}

Performance: Apply row-level filter conditions rather than aggregate_filter (HAVING) whenever possible. Row-level filters reduce rows before aggregation; aggregate filters run after grouping and cannot reduce the amount of data scanned.

Advanced dimension filters and Top-N 

Advanced dimension filters enable Top-N and Bottom-N queries, returning only the top or bottom N dimension values ranked by an aggregated measure. Use advanced_dimension_filters_v2[] with the AdvancedDimension operator and a top_bottom_criteria object specifying top_bottom_limit, is_top, and a measure expression.

1{
2  "structuredSemanticQuery": {
3    "fields": [
4      {
5        "expression": {
6          "tableField": {
7            "name": "sdm__Name__c",
8            "tableName": "SemanticAccount__dlm"
9          }
10        },
11        "alias": "Account Name",
12        "rowGrouping": true
13      }
14    ],
15    "advanced_dimension_filters_v2": [
16      {
17        "fields": [
18          {
19            "model": "SemanticAccount__dlm.sdm__Name__c"
20          }
21        ],
22        "operator": "AdvancedDimension",
23        "top_bottom_criteria": {
24          "top_bottom_limit": 10,
25          "is_top": true,
26          "expression": "sum(SemanticAccount__dlm.sdm__AnnualRevenueAmount__c)"
27        }
28      }
29    ]
30  },
31  "semanticModel": {
32    // ...
33  }
34}

Performance: A filter that references an LOD expression disables the window-function optimization and forces a subquery-and-join plan, which is more expensive.

Reference 

Field (wire name)TypeRequiredDescription
filterPredicateNStandard query filter; applies before aggregation.
context_filterPredicateNContext filter; applies before query filter (deprecated at top level; use semantic_context.context_filter).
aggregate_filterPredicateNAggregate filter; applies to post-aggregation results (HAVING clause).
flatten_filterFlattenFilterNFlatten filter with logical expression indexing an array of PascalCase filter objects.
advanced_dimension_filters_v2AdvancedDimensionFilter[]NTop-N / Bottom-N filters on dimension values ranked by a measure.
binary_predicateBinaryPredicateNBinary comparison with left/right expressions and a binary_operator.
logical_binary_predicateLogicalBinaryPredicateNCombines two predicates with logical_binary_operator (AND / OR / NOT).
binary_operatorString (enum)Y*Operator constant (e.g., BINARY_OPERATOR_EQUALS, BINARY_OPERATOR_GREATER_THAN).
operatorString (PascalCase)Y*PascalCase operator in flatten_filter form (e.g., "GreaterThan", "In", "StartsWith").
range_valuesRangeValuesNInclusive range boundaries (lower_boundary, upper_boundary) for Between operator.
relativeDateRangeRelativeDateRangeNDynamic date range with datePart, startOffset, endOffset, anchorDate.

For the full request schema, see Request Reference.

Limitations 

  • IsEmpty / IsNotEmpty apply to text fields only. IsNull / IsNotNull apply to fields of any type. IsNotEmpty is stricter than IsNotNull: for a text field that can hold "", IsNotNull keeps empty-string records while IsNotEmpty filters them out.
  • Aggregate filters (HAVING) require an aggregated expression. The aggregate_filter predicate must reference a measure or calculated field with an aggregation function, not a raw dimension.

Related