Skip to main content
Version: Next ๐Ÿšง

filter

The filter object groups small builder functions that return structured Filter objects for query.filters. They produce plain objects matching the Filter model โ€” there is nothing to serialize yourself; the backend interprets the result.

For a conceptual walkthrough (building filters by hand vs. with these helpers), see Filters โ€” from scratch to helpers.

How it works: a stateless namespaceโ€‹

filter is an object (a namespace of functions), not a function and not a stateful builder. Two consequences follow.

1. You call its methods โ€” you never call filter itself.

filter.eq('authors', 'alice'); // โœ…
filter('authors', 'alice'); // โŒ TypeError: filter is not a function

2. Every method is pure: it returns a brand-new plain object and stores nothing.

There is no internal list that accumulates conditions. Calling a method twice produces two independent objects; filter itself never changes.

const a = filter.eq('authors', 'alice');
const b = filter.eq('authors', 'fred');

// a === { field: 'authors', operator: 'eq', value: 'alice' }
// b === { field: 'authors', operator: 'eq', value: 'fred' }
// `filter` retained nothing about either call.

The "current filter" lives in the variable you keep, not in filter. To combine conditions you assemble them explicitly with the combinators (or hold the leaves in your own array / state and rebuild the tree):

// authors = alice OR authors = fred โ†’ prefer `in` for one field:
filter.in('authors', ['alice', 'fred']);
// or, explicitly:
filter.or(filter.eq('authors', 'alice'), filter.eq('authors', 'fred'));

Why this design

  • Predictable โ€” the same call always yields the same object, with no side effects or shared mutable state to reason about.
  • Composable โ€” keep leaves in a variable or React state and rebuild the tree on demand (filter.and(...selection)); nothing to reset between renders.
  • Serializable as-is โ€” the result is a plain Filter object you pass straight to fetchQuery({ filters }); no .build() step, no class instances.

Leaf buildersโ€‹

Each returns a single-condition filter on one field.

MethodReturnsMeaning
filter.eq(field, value)SimpleFilterfield = value
filter.neq(field, value)SimpleFilterfield <> value
filter.gt(field, value)SimpleFilterfield > value
filter.gte(field, value)SimpleFilterfield >= value
filter.lt(field, value)SimpleFilterfield < value
filter.lte(field, value)SimpleFilterfield <= value
filter.like(field, value)SimpleFilterSQL LIKE pattern (*/? wildcards)
filter.contains(field, value)SimpleFiltersubstring containment
filter.regex(field, value)SimpleFilterregular-expression match
filter.isNull(field)NullFilterfield IS NULL (no value)
filter.isNotNull(field)NotNullFilterfield IS NOT NULL (no value)
filter.in(field, values)InFilterfield matches any of values (OR within a field)
filter.between(field, start, end)BetweenFilterinclusive range; numbers or ISO dates

value is string | number | boolean. For in, values is string[]. For between, start / end are string | number.

Example

leaves.ts
import { filter } from '@sinequa/atomic';

filter.eq('authors', 'alice'); // { field: 'authors', operator: 'eq', value: 'alice' }
filter.gt('size', 1000); // { field: 'size', operator: 'gt', value: 1000 }
filter.in('docformat', ['pdf', 'doc']); // { field: 'docformat', operator: 'in', values: ['pdf','doc'] }
filter.between('modified', '2024-01-01', '2024-12-31');
filter.isNotNull('company'); // { field: 'company', operator: 'notnull' }

Combinatorsโ€‹

Combine leaves and other expression nodes into an ExprFilter tree.

MethodReturnsNotes
filter.and(...filters)Filter | undefinedlogical AND
filter.or(...filters)Filter | undefinedlogical OR
filter.not(...filters)ExprFilter | undefinednegation

All combinators accept any number of arguments and ignore falsy operands (null, undefined, false) so conditional filters compose without if statements.

  • and / or collapse trivial cases: zero effective operands โ†’ undefined; exactly one โ†’ that operand returned as-is (no wrapper node).
  • not always keeps a not node when at least one operand remains; returns undefined if none do.

Example

combinators.ts
import { fetchQuery, filter } from '@sinequa/atomic';

const filters = filter.and(
filter.gt('size', 1000),
filter.or(
filter.eq('treepath', '/HR/*'),
filter.eq('authors', 'alice'),
),
);

await fetchQuery({ name: '_query', text: 'report', filters });

Conditional composition

conditional.ts
import { filter } from '@sinequa/atomic';

const filters = filter.and(
language && filter.eq('language', language),
onlyRecent && filter.gte('modified', '2024-01-01'),
formats?.length && filter.in('docformat', formats),
);
// none active โ†’ undefined ยท one active โ†’ that single leaf ยท several โ†’ an AND node
Pass the result straight to query.filters

query.filters accepts Filter | Filter[] | ExprFilter (plus the legacy types). Every filter.* result โ€” including undefined from a collapsed combinator โ€” is assignable as-is.

See alsoโ€‹