Filter DSL
FilterDSL
Section titled “FilterDSL”namespace
Imported as a namespace, and re-exported member by member from a second entry point.
FilterDSL.CNode
Section titled “FilterDSL.CNode”type CNode = FieldNode | ConditionNode | ValueNode | ValuesNode | OperationNode | ComponentNode;FilterDSL.ComponentNode
Section titled “FilterDSL.ComponentNode”interface ComponentNodeComponent node. Wraps another condition. Think of it like a named term.
type: "Component"
id: string | null – Id of the component
condition: CNode | null – Actual condition, or null for a component that currently filters nothing. An empty component keeps its place in a composition; only the SQL transformation drops it.
FilterDSL.ComputedValue
Section titled “FilterDSL.ComputedValue”type ComputedValue = { name: string; args: RawValue[];};FilterDSL.Condition
Section titled “FilterDSL.Condition”type Condition = ConditionNode & { [K in keyof typeof CONDITION_METHODS]: CondFn<(typeof CONDITION_METHODS)[K]>; };FilterDSL.ConditionNode
Section titled “FilterDSL.ConditionNode”interface ConditionNodeNamed condition Node.
type: "Condition"
name: string – Name of the condition (e.g. “greater”)
operands: CNode[] – Operands of the condition
FilterDSL.FieldConditionName
Section titled “FilterDSL.FieldConditionName”type FieldConditionName = keyof typeof FIELD_CONDITIONS;Name of a condition a field has as a method, eq, containsIgnoreCase, between and so on.
FilterDSL.FieldExpression
Section titled “FilterDSL.FieldExpression”type FieldExpression = string | CNode;FilterDSL.FieldNode
Section titled “FilterDSL.FieldNode”interface FieldNodeField reference.
type: "FieldReference"
name: string – Name of the field
FilterDSL.FieldReference
Section titled “FilterDSL.FieldReference”type FieldReference = FieldNode & FieldConditions & FieldOperations;FilterDSL.FilterExpression
Section titled “FilterDSL.FilterExpression”type FilterExpression = ConditionNode | ComponentNode;A composed filter: a condition, or a component marker wrapping one. This is what a filter is - it never includes falsy members.
FilterDSL.LogicalOperand
Section titled “FilterDSL.LogicalOperand”type LogicalOperand = FilterExpression | null | undefined | false;What or() / and() accept. Falsy operands are dropped, which is the point of the logical composers: callers compose from helpers that may contribute nothing and let the final logical shape fall out of whatever survived.
Note that flag && cond only lands in this type when flag is a boolean.
For a truthy-narrowable value use !!flag && cond, or the equivalent
flag ? cond : null - "" | undefined | Condition is not an operand.
FilterDSL.OperationNode
Section titled “FilterDSL.OperationNode”interface OperationNodeOperation node. Addition, subtraction etc.
type: "Operation"
name: string – Name of the operation
operands: CNode[] – Operands of the operation
FilterDSL.RawValue
Section titled “FilterDSL.RawValue”type RawValue = null | string | number | boolean | RawValue[] | { [key: string]: RawValue;};JSON value equivalent
FilterDSL.SortDirection
Section titled “FilterDSL.SortDirection”type SortDirection = "asc" | "desc";FilterDSL.SortMatch
Section titled “FilterDSL.SortMatch”interface SortMatchWhere a sort key appears in a sort order.
direction: SortDirection – Direction the key is sorted in.
index: number – Index of the key in the sort order, 0 for the most significant.
FilterDSL.Typed
Section titled “FilterDSL.Typed”type Typed = { type: string;};FilterDSL.Value
Section titled “FilterDSL.Value”type Value = ValueNode & FieldConditions & FieldOperations;FilterDSL.ValueNode
Section titled “FilterDSL.ValueNode”interface ValueNodeValue node. Represents a single runtime-typed value.
type: "Value"
scalarType: string – Scalar type of the value
value: RawValue – Raw/json value
FilterDSL.ValuesNode
Section titled “FilterDSL.ValuesNode”interface ValuesNodeValues node. Represents an array of values.
type: "Values"
scalarType: string – Scalar type of the elements in the value array
values: RawValue[] – Array of raw values.
FilterDSL.and
Section titled “FilterDSL.and”declare const and: (...args: LogicalOperand[]) => (FilterExpression | null);Logical and condition. Will ignore falsy operands. An empty and collapses to null.
Parameters
{... |
CNode} operands |
Returns {CNode} ANDed condition
FilterDSL.component()
Section titled “FilterDSL.component()”declare function component(id: string | null, condition: CNode | null): ComponentNode;Component condition node. These nodes are just marker for which part of the condition originated from which component Logically they are evaluated as the condition they wrap.
Parameters
{String} |
id component id |
{CNode} |
condition actual condition for the component, null for an empty one |
Returns {CNode}
FilterDSL.computedValue()
Section titled “FilterDSL.computedValue()”declare function computedValue(name: string, args?: RawValue[]): ValueNode;FilterDSL.condition()
Section titled “FilterDSL.condition()”declare function condition(name: string, operands?: CNode[]): CNode;Generic condition node. Useful for programmatically instantiating conditions. Not needed for fluent style conditions.
Parameters
{String} |
name condition name |
{Array<CNode>} |
operands operands |
Returns {CNode}
FilterDSL.conditionsEqual()
Section titled “FilterDSL.conditionsEqual()”declare function conditionsEqual(a: CNode | null | undefined, b: CNode | null | undefined): boolean;Returns true if the two conditions are structurally equal: the same nodes in the same order, with equal names,
ids and values. Values compare by their JSON form, which is how they travel, so a Temporal value and the string the
server echoes for it are equal. null and undefined both mean “no condition” and equal each other.
A condition that comes back unchanged from an update() is a different object with the same structure; this is how to tell.
Parameters
a |
condition or null |
b |
condition or null |
FilterDSL.field()
Section titled “FilterDSL.field()”declare function field(name: string): FieldReference;Field / column reference.
Parameters
{String} |
name field name (e.g. “name”, “owner.name”) |
Returns {FieldReference}
FilterDSL.fieldConditionArity()
Section titled “FilterDSL.fieldConditionArity()”declare function fieldConditionArity(name: string): number | undefined;Number of operands a field condition takes besides the field, 1 for eq, 2 for between, undefined for a name
that isn’t one.
Parameters
name |
condition name |
FilterDSL.findComponentNode()
Section titled “FilterDSL.findComponentNode()”declare function findComponentNode(conditionNode: CNode | null, id: string): CNode | null;Finds a component node with the given id.
Parameters
{Object} |
conditionNode condition structure root |
{String} |
id component id |
Returns {Object|null} component node or null
FilterDSL.getConditionArgCount()
Section titled “FilterDSL.getConditionArgCount()”declare function getConditionArgCount(name: Function | string): number;Returns the number of expected arguments for the condition with the given name.
Parameters
{String} |
name condition name |
Returns {number} number of value arguments expected
FilterDSL.isComposedComponentExpression()
Section titled “FilterDSL.isComposedComponentExpression()”declare function isComposedComponentExpression(node: CNode): boolean;Returns true if the given condition node is a logical condition with all operands being of type Component
Parameters
node |
FilterDSL.isComputedValue()
Section titled “FilterDSL.isComputedValue()”declare function isComputedValue(raw: unknown): raw is ComputedValue;FilterDSL.isCondition()
Section titled “FilterDSL.isCondition()”declare function isCondition(value: Typed): value is CNode;Returns true if the given value is a condition node.
Parameters
value |
FilterDSL.isConditionObject()
Section titled “FilterDSL.isConditionObject()”declare function isConditionObject(value: any): boolean;FilterDSL.isLogicalCondition()
Section titled “FilterDSL.isLogicalCondition()”declare function isLogicalCondition(node: CNode): node is ConditionNode;Returns true if the given condition node is either a logical and or a logical or condition.
Parameters
{Object} |
node node |
Returns {boolean} true if the node is either an “and” or an “or”
FilterDSL.matchSort()
Section titled “FilterDSL.matchSort()”declare function matchSort(sortFields: readonly FieldExpression[], key: FieldExpression): SortMatch | null;Finds a column’s sort key in a sort order. The key is what the column sorts by – a field path, or an expression node for a computed column – and never carries a direction itself. It matches a sort field sorting by the same field or a structurally equal expression, in either direction, in any of the forms simplifySortField() accepts.
Parameters
sortFields |
sort order, as in a query config |
key |
field path or expression the column sorts by |
Returns direction and position of the key, or null if the sort order doesn’t contain it
FilterDSL.matchSortPart()
Section titled “FilterDSL.matchSortPart()”declare function matchSortPart(sortFields: readonly FieldExpression[], key: FieldExpression): SortMatch | null;Finds a sort key inside an expression the sort order sorts by, e.g. num in num % 10: the order doesn’t sort by
the key itself, but the key takes part in it. A sort field that is the key, which matchSort() finds, doesn’t count.
Parameters
sortFields |
sort order, as in a query config |
key |
field path or expression the column sorts by |
Returns direction of the expression and its position, the first one containing the key, or null if none does
FilterDSL.not()
Section titled “FilterDSL.not()”declare function not(operand: CNode): CNode;Logical not condition.
Parameters
{CNode} |
operand |
Returns {CNode} negated condition
FilterDSL.now()
Section titled “FilterDSL.now()”declare function now(): ValueNode;FilterDSL.operation()
Section titled “FilterDSL.operation()”declare function operation(name: string, operands?: CNode[]): CNode;Generic operation node. Useful for programmatically instantiating conditions. Not needed for fluent style conditions.
Parameters
name |
|
operands |
FilterDSL.or
Section titled “FilterDSL.or”declare const or: (...args: LogicalOperand[]) => (FilterExpression | null);Logical or condition. Will ignore falsy operands. An empty or collapses to null.
Parameters
{... |
CNode} operands |
Returns {CNode} ORed condition
FilterDSL.ownedPart()
Section titled “FilterDSL.ownedPart()”declare function ownedPart(condition: FilterExpression | null, id: string): FilterExpression | null;The part of a condition that belongs to the holder of the given component id:
- in a composition of components, the term of its component, or
nullif there is none; - in a lone component, its term if the component is its own,
nullif it belongs to someone else; - any other condition is owned whole.
Components exist on one level only: inside its component, an owner recognizes its terms by content, not by further markers.
Parameters
condition |
condition or null |
id |
component id of the owner reading |
Returns the owned part, null for “nothing”
FilterDSL.simplifySortField()
Section titled “FilterDSL.simplifySortField()”declare function simplifySortField(sortField: FieldExpression): FieldExpression;The simple string form of a sort field, where it has one: a field sorts as its name, descending with a ! prefix,
whether it came as a string, a field node or a field in asc or desc. An expression has no string form and comes
back as it is, unwrapped from an asc, which is the default direction anyway.
Parameters
sortField |
sort field, as in a query config |
FilterDSL.toJSON()
Section titled “FilterDSL.toJSON()”declare function toJSON(condition: CNode | null): RawValue;Converts the given condition graph into simple js objects.
The Filter DSL produces Filter nodes that are in fact instances of the Filter DSL types used to implement to conditions/operations. This works fine in many cases, but sometimes it doesn’t.
One example is that mobx will ignore the instances and not create observables for them. Making the FilterDSL in general observable would be possible, but would mean a huge overhead for a very exotic use-case.
Parameters
condition |
Input condition, potentially consisting of DSL instances |
Returns condition as graph of objects / arrays
FilterDSL.today()
Section titled “FilterDSL.today()”declare function today(): ValueNode;FilterDSL.toggleSort()
Section titled “FilterDSL.toggleSort()”declare function toggleSort(sortFields: readonly FieldExpression[], key: FieldExpression): FieldExpression[];The sort order a click on a column asks for: the key descending if it already is the whole sort order and ascending, the key ascending in every other case. The result replaces the whole sort order.
A field path comes out in its string form, "name" or "!name"; an expression as itself or wrapped in desc.
Parameters
sortFields |
current sort order |
key |
field path or expression the column sorts by |
FilterDSL.updateComponent()
Section titled “FilterDSL.updateComponent()”declare function updateComponent(condition: FilterExpression | null, id: string, term: FilterExpression | null): FilterExpression | null;Replaces the part of a condition that belongs to the holder of the given component id – the write side of ownedPart(), by the same rules.
This is how several owners share one condition – a grid’s filter row, a search form, a picker – each writing only its own component and leaving the others as they are:
- The component with the id gets the new term. A
nullterm keeps the component ascomponent(id, null), so the owner finds its place again later; only the SQL transformation drops an empty component. - A missing component is added as another operand of an
andcomposition, or joined withand()to a lone component, so the result stays one flat level. Owners narrow what is shown, so adding meansand. - An
orcomposition without the component is refused: joining it would widen the result, and wrapping theorwould produce a second level. Putcomponent(id, null)into theorwhere the owner’s term belongs. - No condition at all becomes the lone component, so the next owner to write joins it. A
nullterm leaves it at no condition. - Any other condition is owned whole and replaced by the plain term, without a marker.
Returns the very same condition object when the owned part is structurally equal to the new term, so an unchanged filter need not send an update().
Parameters
condition |
condition or null |
id |
component id of the owner writing |
term |
new term of that owner, null for “filters nothing” |
Throws Error for an or composition that lacks the component
FilterDSL.value()
Section titled “FilterDSL.value()”declare function value(value: RawValue, type?: string): ValueNode;Creates a new value node
Parameters
{Object} |
value scalar value of appropriate type |
{String} |
[type] scalar type name if not given the type will be selected based on value type |
Returns {ValueNode} value node
FilterDSL.values()
Section titled “FilterDSL.values()”declare function values(type: string, ...values: RawValue[]): ValuesNode;Creates a new values node that encapsulates a collection of scalar values (for e.g. the IN operator)
Parameters
{String} |
type scalar type name |
{Object} |
values var args of scalar value of appropriate type |
Returns {ValuesNode} values node
decompileFilter()
Section titled “decompileFilter()”function
declare function decompileFilter(condition: CNode | null, level?: number, match?: CNode | null, invert?: boolean): string;Converts the given FilterDSL condition graph into a pretty-formatted source form using the FilterDSL API (and DateTime.fromIso() expressions)
Parameters
{Object} |
condition condition graph |
{Number} |
level starting indentation level |
{Object} |
match Optional object to mark. The given node is assumed to an instance in the graph and the source output of that object is marked is enclosed in />>/ /<</ to mark the object |
{boolean} |
invert inverts condition output (mostly internal usage) |
Returns {string} pretty-printed source string. If match was used, the >> << might prevent it from being valid JavaScript
conditionPredicate()
Section titled “conditionPredicate()”function
declare function conditionPredicate<R extends object = any>(type: string, condition: FilterExpression | null): (row: R) => boolean;Compiles a condition into a test of rows: whether the server would return a row for a query with that condition.
const matches = conditionPredicate("Foo", doc.config.condition);const shown = created.filter(matches);Everything the condition decides by itself is checked here, once: a field the type doesn’t have, an operator that doesn’t exist or gets the wrong number of operands, a value its type can’t read. A row missing a field the condition reads is an error when it is tested – the query doesn’t select that field.
It follows the SQL path: three-valued logic, so a row matches only where the condition is true and a comparison
with null never is; a path through a to-many relation asks whether some element matches; values read as the type
of what they are compared to. Text orders by the user’s locale rather than the database’s collation, likeRegex
is a JavaScript regular expression, and now() and today() are the browser’s clock.
Parameters
type |
GraphQL type name of the rows |
condition |
FilterDSL condition, null for “all rows” |
Returns test of a row, in the live form a query result or a working set draft has
sortComparator()
Section titled “sortComparator()”function
declare function sortComparator<R extends object = any>(type: string, sortFields: readonly FieldExpression[]): (a: R, b: R) => number;Compiles sort fields into a comparison of rows, for Array.prototype.sort().
rows.sort(sortComparator("Foo", ["!num", "name"]))Sort fields come in every form a query config takes them: a field path, "!" in front for descending, a field or
expression node, or one in asc() or desc(). Nulls come last ascending and first descending, as in PostgreSQL.
Values order by scalarCompare(). Rows equal under every sort field keep their order, sort() being stable.
Parameters
type |
GraphQL type name of the rows |
sortFields |
sort fields, most significant first |
Returns comparison of two rows
Conditions on a field
Section titled “Conditions on a field”Called on a field or value node, and reachable as condition(name, operands) for building a graph programmatically. Operands are the ones passed besides the node it is called on, so isNull takes none and between takes two.
| Operator | Operands |
|---|---|
between |
2 |
betweenSymmetric |
2 |
contains |
1 |
containsIgnoreCase |
1 |
endsWith |
1 |
eq |
1 |
equal |
1 |
equalIgnoreCase |
1 |
ge |
1 |
greaterOrEqual |
1 |
greaterThan |
1 |
gt |
1 |
in |
1 |
isDistinctFrom |
1 |
isFalse |
0 |
isNotDistinctFrom |
1 |
isNotNull |
0 |
isNull |
0 |
isTrue |
0 |
le |
1 |
lessOrEqual |
1 |
lessThan |
1 |
likeRegex |
1 |
lt |
1 |
ne |
1 |
notBetween |
2 |
notBetweenSymmetric |
2 |
notContains |
1 |
notContainsIgnoreCase |
1 |
notEqual |
1 |
notEqualIgnoreCase |
1 |
notLikeRegex |
1 |
startsWith |
1 |
Combining conditions
Section titled “Combining conditions”Called on a condition. The functional forms and(), or() and not() build the same nodes.
| Operator | Operands |
|---|---|
and |
1 |
andNot |
1 |
not |
0 |
or |
1 |
orNot |
1 |
Operations on a field
Section titled “Operations on a field”Called on a field, producing an expression rather than a condition, and reachable as operation(name, operands). toString is translated into a cast to string; asc and desc are the sort directions.
| Operator | Operands |
|---|---|
add |
1 |
asc |
0 |
bitAnd |
1 |
bitNand |
1 |
bitNor |
1 |
bitNot |
0 |
bitOr |
1 |
bitXNor |
1 |
bitXor |
1 |
concat |
1 |
desc |
0 |
div |
1 |
divide |
1 |
lower |
0 |
minus |
1 |
mod |
1 |
modulo |
1 |
mul |
1 |
multiply |
1 |
neg |
0 |
plus |
1 |
pow |
1 |
power |
1 |
rem |
1 |
shl |
1 |
shr |
1 |
sub |
1 |
subtract |
1 |
times |
1 |
toString |
0 |
unaryMinus |
0 |
unaryPlus |
0 |
upper |
0 |