Skip to main content
Every Cindel query starts from a generated collection, builds up conditions and result options, then executes with a result method. This page walks through the three entry points — all(), where(), and filter() — along with sorting, pagination, distinct, dynamic modifiers, query deletes, and query updates.

Query Flow

1

Choose an entry point

Start with all() for the full collection, where() for an indexed-field lookup, or filter() for a predicate over any persisted field.
2

Chain modifiers

Add filters, sort helpers, offset, limit, or distinct calls as needed.
3

Execute

Call findAll(), findFirst(), or count() to run the query and receive results.
A minimal example with the Todo model below illustrates the full shape:

Entry Points

all() — Full Collection

Use all() when the query should consider every object in the collection. It is the right starting point for full lists, counts, sorted pages, and maintenance operations.
For large collections, always add sorting and pagination rather than loading everything at once.

where() — Indexed-Field Lookups

Use where() for equality, range, and multi-entry lookups. The field must be annotated with @Index in your modelwhere() helpers are only generated for indexed fields, and using a non-indexed field as a where() entry point is not supported. Generated helpers are named after your model fields and indexes.
Use where() for fields you query frequently — indexed lookups are far cheaper than full-collection scans. If a helper you expect does not exist, verify that the field is annotated with @Index and that generated code is up to date.

filter() — Predicate-Based Filtering

Use filter() to apply predicates over any persisted field, whether or not it is indexed. You can also chain filter() after where() to narrow an already-indexed candidate set.
Combining where() and filter() gives you the best of both worlds: the index narrows the candidate set cheaply, and the filter applies the remaining condition only to that smaller set.

Result Methods

Result methods execute the query. Until you call one, you are building — not running — the query.

Sorting

Generated sort helpers are available for every persisted field. Use sortBy… for the primary sort and chain thenBy… for secondary sorts. Pass order: CindelSortOrder.descending to reverse any sort.
When you need to sort by a field name that is only known at runtime, use the lower-level string API:
Prefer generated sort helpers whenever the field is known at compile time — the string API is for dynamic code that already works with persisted field names.

Pagination

Use offset and limit to page through results. Both values must be non-negative.
  • offset(n) — skip the first n results after filtering, sorting, and distinct.
  • limit(n) — return at most n results after the offset.

Distinct

Use distinct to keep only the first result for each unique value of a field, or for each unique combination of multiple fields. Distinct is applied before offset and limit.
When you care which record is kept for each distinct value, sort first:

Dynamic Query Modifiers

Dynamic modifiers let you build queries from optional user input without duplicating query logic across multiple branches.

optional — Conditional Filter

Apply a filter only when a condition is true. When the condition is false, the query passes through unchanged.

anyOf — OR Across a List

Match records that satisfy at least one condition from a list. An empty list matches nothing.

allOf — AND Across a List

Match records that satisfy every condition from a list. An empty list is a no-op.
The callback passed to anyOf or allOf should only add filters. Do not use it to change sorting, distinct, pagination, projection, or the query source.

Query Deletes

Use query deletes to remove objects by condition rather than by known ids. Both delete methods must run inside a writeTxn.
You can combine where() and filter() for targeted cleanup:
Use collection delete or deleteAll when you already have ids. Use query deletes when the removal is driven by a query condition.

Query Updates

Use query updates to mutate matching objects by persisted field name. Both update methods must run inside a writeTxn.
The map keys are persisted field names. The id field cannot be updated. Values must use Cindel-compatible stored shapes: null, bool, int, finite double, String, lists, and string-keyed maps.
Query updates are not supported while sync is enabled. When sync is active, read the objects, mutate them in Dart, and write them back with put or putAll:
For converted fields — DateTime, Duration, or enums — prefer typed object writes unless you intentionally want to supply the stored scalar representation. Generated helpers apply the correct conversions automatically; the update map does not.

Examples