Skip to content

Indexes

Mirko Da Corte edited this page Aug 25, 2026 · 8 revisions

You declare a repository's indexes on its RepositoryOptions; Scrinium builds and rebuilds them during a migration run (drop old → migrate → build new) — the first seeding executes one, which is what creates the indexes on a fresh database.

Declaring indexes

Build the repository from a RepositoryOptions<TModel> and set IndexBuilders — a list of (keys, options) pairs using the driver's Builders<TModel>.IndexKeys and CreateIndexOptions<TModel>:

public IRepository<Vote, string> Votes { get; } = new Repository<Vote, string>(
    new RepositoryOptions<Vote>("votes")
    {
        IndexBuilders =
        [
            // one vote per (owner, subject)
            (Builders<Vote>.IndexKeys.Ascending(v => v.Owner).Ascending(v => v.Subject),
             new CreateIndexOptions<Vote> { Unique = true }),
        ]
    });

Common shapes

// Unique single-field
(Builders<User>.IndexKeys.Ascending(u => u.ExternalId),
 new CreateIndexOptions<User> { Unique = true })

// Unique + sparse (only index documents that have the field)
(Builders<User>.IndexKeys.Ascending(u => u.Email),
 new CreateIndexOptions<User> { Unique = true, Sparse = true })

// TTL — expire documents at a per-document time (ExpireAfter = zero from the indexed date)
(Builders<Session>.IndexKeys.Ascending(s => s.ExpiresAt),
 new CreateIndexOptions<Session> { ExpireAfter = TimeSpan.Zero })

// Keys on a subtype member (cast in the key selector)
(Builders<User>.IndexKeys.Ascending(u => ((PremiumUser)u).SubscriptionId),
 new CreateIndexOptions<User> { Unique = true, Sparse = true })

Geospatial indexes

Pair a 2dsphere index with the GeoPointSerializer, which stores a model's lon/lat as a GeoJSON point:

(Builders<Place>.IndexKeys.Geo2DSphere(p => p.Location),
 new CreateIndexOptions<Place>())

Automatic reference indexes

Each reference id path of the model map also gets an index, without declaring it: a sparse ascending index on the path, named ref_<path>ref_Customer._id for a Customer reference member, ref_Items._id for a collection of references. It backs the lookups by reference id, the dependency update task included: propagating the summary of an updated model filters the referencing documents on that path.

Declare a custom index on the same field to replace the automatic one with your own configuration:

IndexBuilders =
[
    // replaces ref_Customer._id
    (Builders<Order>.IndexKeys.Descending(o => o.Customer.Id),
     new CreateIndexOptions<Order>()),
]

An index serves the queries on any left prefix of its keys, and the automatic index has a single key: your index replaces it when that key opens it, whatever its following keys and its options. The key must carry a sort order — a hashed, text or geospatial key doesn't serve every query on its field, so the automatic index stays next to it.

Custom index keys ref_Customer._id
{ "Customer._id": 1 } replaced
{ "Customer._id": -1, "CreationDate": 1 } replaced
{ "CreationDate": 1, "Customer._id": 1 } kept — the path doesn't open the index
{ "Customer._id": "hashed" } kept — no sort order

On an already-seeded database the automatic index is already there: the next migration drops it, with every other index outside the definition.

Building and inspecting

On an already-seeded database, a change to IndexBuilders takes effect at the next migration. You can also drive the indexes directly on a repository:

Member Does
GetDefinedIndexModelsAsync() The CreateIndexModels derived from IndexBuilders, plus the automatic ref_<path> indexes of the reference id paths a custom index doesn't already open.
BuildNewIndexesAsync() Create the defined indexes.
DeleteOldIndexesAsync() Drop indexes no longer among the defined ones (_id_ is kept).

Note. On a read-only repository or context, index management is denied like any other write (listing stays available), and migrations skip the repository's index steps: the indexes of a shared collection belong to the collection owner.


Next: Repositories for the rest of RepositoryOptions, or Migrations for when indexes are rebuilt.

Clone this wiki locally