Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/mainser/cindel/llms.txt

Use this file to discover all available pages before exploring further.

Cindel indexes control how the code generator creates where() query helpers for your collections and how the underlying backend organizes data for fast lookups. Adding @Index to a field tells the generator to emit typed equality and range helpers for that field; choosing the right index type further shapes which query operations are available and how much storage space the index uses at write time.

Single-Field Indexes with @Index

Annotate any collection field with @Index() to mark it as indexed. The generator produces where() helpers bound to that field, and the backend stores an ordered index entry for every document write:
@collection
class User {
  Id dbId = autoIncrement;

  @Index()
  late String name;

  @Index(unique: true)
  late String email;

  bool active = true;
}
where() helpers use the index for efficient lookups rather than a full collection scan:
final ada = await db.users
    .where()
    .emailEqualTo('ada@example.com')
    .findFirst();

Uniqueness

Pass unique: true to require that no two documents share the same indexed value. Cindel enforces the constraint at write time and rejects inserts or updates that would create a duplicate:
@Index(unique: true)
late String email;

Unique-Replace Upserts

Combining unique: true with replace: true tells the generator to emit a named putByFieldName helper. When you call the helper, Cindel checks whether a document already holds that value and, if one exists, reuses its id instead of inserting a duplicate:
@collection
class Account {
  Id dbId = autoIncrement;

  @Index(unique: true, replace: true)
  late String username;
}
final account = Account()..username = 'ada';

// Inserts or silently replaces the existing row with username == 'ada'.
await db.accounts.putByUsername(account);

Case-Insensitive Lookups

For string fields, caseSensitive: false stores a lowercased form of the value in the index. Equality and range lookups against that index are then case-insensitive:
@Index(caseSensitive: false)
late String normalizedTitle;

Index Types

The type parameter of @Index selects the storage and query strategy for a field. The default is CindelIndexType.value.

CindelIndexType.value (default)

Stores the original sortable value in the index. Supports both equality helpers (equalTo, notEqualTo) and range helpers (greaterThan, lessThan, between). Use this type for any field you want to query with ranges or sort through where():
@Index()
late DateTime createdAt;
final recent = await db.events
    .where()
    .createdAtBetween(start, end)
    .findAll();

CindelIndexType.hash

Stores a compact, stable hash of the indexed value instead of the value itself. Hash indexes use less space and are suitable for long strings where you only ever need equality lookups. Range queries are not available on hash indexes:
@Index(type: CindelIndexType.hash, unique: true)
late String contentHash;

CindelIndexType.words

Splits a string field into individual word tokens and stores one index entry per token. The generator emits token-lookup and token-prefix helpers. Use this type for full-text style word searches:
@Index(type: CindelIndexType.words, caseSensitive: false)
late String body;

CindelIndexType.multiEntry

Stores one index entry for each element of a primitive list field. The generator emits list-membership helpers so you can find all documents that contain a given element:
@Index(type: CindelIndexType.multiEntry, caseSensitive: false)
List<String> tags = const [];
final flutterArticles = await db.articles
    .where()
    .tagsElementEqualTo('flutter')
    .findAll();

Complete Field-Index Example

The Article model below combines all four index types:
@collection
class Article {
  Id dbId = autoIncrement;

  @Index()
  late String title;

  @Index(caseSensitive: false)
  late String normalizedTitle;

  @Index(type: CindelIndexType.words, caseSensitive: false)
  late String body;

  @Index(type: CindelIndexType.multiEntry, caseSensitive: false)
  List<String> tags = const [];
}
  • title — value index; supports equality and range helpers
  • normalizedTitle — value index with case folding; lookup is case-insensitive
  • body — word-token index; supports per-word and word-prefix lookups
  • tags — multi-entry index; one entry per tag, supports element-equality helpers

Composite Indexes with CompositeIndex

Composite indexes span two or more fields and are declared on the collection itself using the indexes parameter of @Collection. They generate where() helpers that accept values for all participating fields together:
@Collection(
  name: 'events',
  indexes: [
    CompositeIndex(['accountId', 'createdAt']),
  ],
)
class Event {
  Id dbId = autoIncrement;

  @Index()
  late int accountId;

  @Index()
  late DateTime createdAt;
}
The fields listed in CompositeIndex must match the Dart field names (or the @Name override if present). The order of the list determines the index sort order: the backend sorts by accountId first, then by createdAt.

Composite Index Options

CompositeIndex accepts the same unique, replace, and caseSensitive options as @Index:
CompositeIndex(
  ['tenantId', 'slug'],
  unique: true,
  replace: true,
  caseSensitive: false,
)
  • unique: true — enforces uniqueness across the full composite key
  • replace: true — generates a putBy<IndexName> upsert helper that reuses an existing id when the composite key already exists
  • caseSensitive: false — folds string values in the composite key before comparison
Add @Index only to fields you actively query through where() or that are required for uniqueness enforcement. Every indexed field adds a write-time overhead because the backend must keep the index entries up to date on every insert and update. Fields used only in filter() expressions do not need an index — filter() operates over already-retrieved documents.

Build docs developers (and LLMs) love