Skip to main content

UCAST Syntax

The data filtering support 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 the 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 EOPA 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.

OperationCOMPOUND_OP_NAMESupported Types@open-policy-agent/ucast-prismaOpenPolicyAgent.Ucast.Linq
Andandarray✅✅
Ororarray✅✅
Notnotarray 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 EOPA supported UCAST interpreters.

OperationFIELD_OP_NAMESupported VALUE Types@open-policy-agent/ucast-prismaOpenPolicyAgent.Ucast.Linq
Equalseqstring, number, boolean, null✅✅
Not Equalsnestring, number, boolean, null✅✅
Less Thanltnumber✅✅
Less Than or Equalsltenumber✅✅
Greater Thangtnumber✅✅
Greater Than or Equalsgtenumber✅✅
Ininarray✅✅
Not Inninarray✅✅
Containscontainsstring, number, boolean✅❌
Starts Withstartswithstring✅❌
Ends Withendswithstring✅❌

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" } }]
}