Skip to main content
Filters describe conditions that stored objects must satisfy. Cindel gives you two complementary approaches: generated type-safe helpers that follow your Dart model fields, and a lower-level CindelFilter builder for constructing predicates dynamically at runtime. Use generated helpers for everyday application code; reach for CindelFilter when filter logic must be assembled from runtime data.

Generated Filters

Generated filters start from a typed collection’s .filter() builder. Every helper is derived from a persisted model field — named after it, typed to it, and aware of any custom conversions applied to it.
Given that model, the generated API exposes field-level predicates directly:
Chain multiple conditions to combine them with AND semantics:
Generated helpers are compile-time checked, follow Dart naming conventions, and automatically apply any conversions defined in your model. They are the right choice whenever the field and predicate are known at compile time.

CindelFilter — Dynamic Filters

CindelFilter builds predicates from persisted field names, making it suitable for scenarios where the field or condition is only known at runtime. Apply a dynamic predicate to a query with whereMatches(...).
This is especially useful for saved search definitions, configurable admin screens, and filter builders driven by user input:
When using CindelFilter with converted types — enums, DateTime, or Duration — you must supply the stored scalar representation yourself. Generated filter helpers apply the correct conversions automatically, so prefer them for these types. Using raw CindelFilter predicates with converted fields can silently produce incorrect results.
When a model uses @Name, the persisted field name may differ from the Dart field name. Pass the persisted name to CindelFilter.field(...). Generated helpers avoid this issue entirely.

Field Predicates

The following predicates are available on CindelFilter.field(...) (and have generated equivalents for each persisted model field): Examples using CindelFilter:
When the field is known at compile time, use the generated equivalent instead:

Embedded Object Paths

Generated Embedded Filters

When data is stored inside embedded objects, the generated API exposes nested filter callbacks that traverse the embedded structure in a type-safe way:
For list-typed embedded fields, use the generated element helper:

Dynamic Paths with CindelFilter.path

Use CindelFilter.path([...]) when an embedded field path must be assembled at runtime. Provide the path as a list of persisted field name segments.
When the path reaches a list, Cindel evaluates the remaining path against each element and matches the document if any element satisfies the predicate:
Use CindelFilter.path(...) only when the path cannot be determined at compile time. For known paths, the generated nested filter callbacks are safer and more readable.

Boolean Composition

Compose multiple CindelFilter predicates with all, any, and not.

CindelFilter.all — AND

Every predicate in the list must match:

CindelFilter.any — OR

At least one predicate in the list must match:

CindelFilter.not — Negation

Invert a predicate:

Composition with Generated Helpers

When the field helpers are known at compile time, the anyOf and allOf query modifiers are often more readable than manual CindelFilter composition:
Use CindelFilter boolean composition when you need to build a predicate tree directly — for example, when the structure itself is determined at runtime.

Choosing the Right Approach

Use generated filters for all normal application code. They are compile-time checked, follow your Dart model names, and apply conversions automatically.
Combine where() for indexed lookups with filter() for additional conditions:
Avoid using filters as a substitute for indexes when lookup performance matters. Add @Index to fields that frequently narrow large collections, start the query with where(), and add filters only for the remaining conditions.