UCAST Syntax
The Enterprise OPA platform makes use of the Universal Conditions AST (UCAST) project to represent (as JSON) a universal set of conditions that can be applied to filter data.
UCAST allows 3 types of nodes in the tree:
- Document-level Condition nodes: Used to apply an operator to the entire document/table, e.g. the EXISTS operator in SQL. These types of nodes are not used by any of our interpreters.
- Compound Condition nodes: Used to apply an operator across N-many child nodes.
- Field Condition nodes: Used to apply an operator to a field and an optional value.
Expanded Syntax
START := EXPRS
EXPRS := COMPOUND_EXPR | FIELD_EXPR
COMPOUND_EXPR := {"type": "compound", "operator": COMPOUND_OP_NAME, "value": [EXPRS]}
COMPOUND_OP_NAME := "and" | "or" | "not"
FIELD_EXPR := {"type": "field", "field": FIELD_NAME, "operator": FIELD_OP_NAME, "value": VALUE}
FIELD_NAME := <string>
VALUE := <string> | <number> | ... | {"field": COMPARED_FIELD_NAME}
COMPARED_FIELD_NAME := <string>
The FIELD_NAME
corresponds to the field being referenced in the database. COMPARED_FIELD_NAME
is used when comparing one column to another column.
Formal JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "UCAST expanded syntax",
"description": "UCAST expanded syntax that is compatible with Styra supported integrations",
"$defs": {
"compoundExpression": {
"type": "object",
"properties": {
"type": { "const": "compound" },
"operator": {
"anyOf": [
{ "const": "and" },
{ "const": "or" },
{ "const": "not" }
]
},
"value": {
"type": "array",
"items": { "$ref": "#" }
}
},
"required": ["type", "operator", "value"]
},
"fieldExpression": {
"type": "object",
"properties": {
"type": { "const": "field" },
"field": { "type": "string" },
"operator": { "enum": ["eq", "ne", "lt", "lte", "gt", "gte", "in", "nin", "contains", "startswith", "endswith"] }
},
"required": ["type", "field", "operator", "value"],
"allOf": [
{
"if": { "properties": { "operator": { "const": "eq" }} },
"then": { "properties": { "value": { "type": ["string", "number", "boolean", "null"] }}}
},
{
"if": { "properties": { "operator": { "const": "ne" }} },
"then": { "properties": { "value": { "type": ["string", "number", "boolean", "null"] }}}
},
{
"if": { "properties": { "operator": { "const": "lt" }} },
"then": { "properties": { "value": { "type": "number" }}}
},
{
"if": { "properties": { "operator": { "const": "lte" }} },
"then": { "properties": { "value": { "type": "number" }}}
},
{
"if": { "properties": { "operator": { "const": "gt" }} },
"then": { "properties": { "value": { "type": "number" }}}
},
{
"if": { "properties": { "operator": { "const": "gte" }} },
"then": { "properties": { "value": { "type": "number" }}}
},
{
"if": { "properties": { "operator": { "const": "in" }} },
"then": { "properties": { "value": { "type": ["array"] }}}
},
{
"if": { "properties": { "operator": { "const": "nin" }} },
"then": { "properties": { "value": { "type": ["array"] }}}
},
{
"if": { "properties": { "operator": { "const": "contains" }} },
"then": { "properties": { "value": { "type": ["string", "number", "boolean"] }}}
},
{
"if": { "properties": { "operator": { "const": "startswith" }} },
"then": { "properties": { "value": { "type": "string" }}}
},
{
"if": { "properties": { "operator": { "const": "endswith" }} },
"then": { "properties": { "value": { "type": "string" }}}
}
]
}
},
"type": "object",
"anyOf": [
{ "$ref": "#/$defs/compoundExpression" },
{ "$ref": "#/$defs/fieldExpression" }
]
}
Concise syntax
UCAST supports an abbreviated "concise" syntax, which allows leaving out the often redundant "type" and "operation" fields from the nodes. It uses some implicit construction rules to make common collections of conditions easier to write.
Two assumptions made in the concise format:
- The default compound operation is and.
- The default field operation is eq.
START := IMPLICIT_AND
EXPRS := IMPLICIT_AND | COMPOUND_EXPR | FIELD_EXPR
IMPLICIT_AND := '{' (FIELD_EXPR | COMPOUND_EXPR)... '}'
FIELD_EXPR := (FIELD_NAME ':' VALUE) | (FIELD_NAME ':' '{' FIELD_OP_NAME ':' VALUE '}')
FIELD_NAME := <string>
VALUE := <string> | <number> | ... | {"field": COMPARED_FIELD_NAME}
COMPARED_FIELD_NAME := <string>
COMPOUND_EXPR := COMPOUND_OP_NAME ':' [EXPRS...]
COMPOUND_OP_NAME := 'and' | 'or' | 'not'
The FIELD_NAME
corresponds to the field being referenced in the database.
Compound Operations
The not
operation is not generally supported by all clients.
| Operation | COMPOUND_OP_NAME
| Supported Types | @styra/ucast-prisma
| Styra.Ucast.Linq
|
| --------- | ------------- | ---------------- | --- | ------- | ------- |
| And | and
| array
| ✅ | ✅ |
| Or | or
| array
| ✅ | ✅ |
| Not | not
| array
with 1 entry | ✅ | ❌ |
Field Operations
Not all field operations are supported by every database integration. The following is a (non-comprehensive) compatibility matrix for the Styra supported UCAST interpreters.
| Operation | FIELD_OP_NAME
| Supported VALUE
Types | @styra/ucast-prisma
| Styra.Ucast.Linq
|
| --------- | ------------- | ---------------- | --- | ------- | ------- |
| Equals | eq
| string
, number
, boolean
, null
| ✅ | ✅ |
| Not Equals | ne
| string
, number
, boolean
, null
| ✅ | ✅ |
| Less Than | lt
| number
| ✅ | ✅ |
| Less Than or Equals | lte
| number
| ✅ | ✅ |
| Greater Than | gt
| number
| ✅ | ✅ |
| Greater Than or Equals | gte
| number
| ✅ | ✅ |
| In | in
| array
| ✅ | ✅ |
| Not In | nin
| array
| ✅ | ✅ |
| Contains | contains
| string
, number
, boolean
| ✅ | ❌ |
| Starts With | startswith
| string
| ✅ | ❌ |
| Ends With | endswith
| string
| ✅ | ❌ |
Examples
Simple: Product Prices
Use case: Show products with a price less than or equal to $500
Expanded Format:
{
"type": "field",
"field": "products.price",
"operator": "lte",
"value": 500
}
Concise Format:
{
"products.price": {
"lte": 500
}
}
Compound: Support Tickets
Use Case: Show tickets that are assigned to "Alice Zimmerman" and have a severity of 1 or 2.
Expanded Format:
{
"type": "compound",
"operator": "and",
"value": [
{
"type": "field",
"field": "tickets.assignee",
"operator": "eq",
"value": "Alice Zimmerman"
},
{
"type": "field",
"field": "tickets.severity",
"operator": "in",
"value": [1, 2]
}
]
}
Concise Format:
{
"tickets.assignee": "Alice Zimmerman",
"tickets.severity": { "in": [1, 2] },
}
Nots and Column Comparisons: Support Tickets
Use Case: Show tickets where the assignee is not the resolver.
Expanded Format:
{
"type": "compound",
"operator": "not",
"value": [
{
"type": "field",
"field": "tickets.assignee",
"operator": "eq",
"value": { "field": "tickets.resolver" }
}
]
}
Concise Format:
{
"not": [{"tickets.assignee": {"field": "tickets.resolver"}}]
}