Next steps: Atlas Search, time series and vector search

Add full-text relevance and vector similarity as pipeline stages, store measurements in time-series collections instead of hand-rolled buckets, and know when another store is the better answer.

db.orders.aggregate([
  { $search: {
      index: "default",
      compound: {
        must:   [ { text: { query: "lamp", path: "title",
                            fuzzy: { maxEdits: 1 } } } ],
        should: [ { text: { query: "priority", path: "tags",
                            score: { boost: { value: 2 } } } } ],
        filter: [ { range: { path: "total", gte: 10 } } ]
      }
  } },
  { $limit: 10 },
  { $project: { title: 1, score: { $meta: "searchScore" } } }
])
  • Atlas Search is Lucene-backed and runs as a separate process beside the database, but it is exposed as a $search stage, so it composes with the rest of the pipeline.
  • The index definition decides which fields are text, keyword, autocomplete or date. A field that is not mapped is not searchable, whatever the query says.
  • Relevance is tunable with boost, and $meta: "searchScore" exposes the score so later stages can sort or filter on it.
  • It is an Atlas feature. Self-managed MongoDB needs a separate search engine alongside it.
💡
Search indexes are eventually consistent with the collection. A document written a moment ago may not be findable yet, so do not use search as the read path immediately after a write within the same request.

Time-series collections

db.createCollection("readings", {
  timeseries: {
    timeField: "at",
    metaField: "sensorId",
    granularity: "minutes"
  },
  expireAfterSeconds: 60 * 60 * 24 * 365
});

db.readings.insertOne({ at: new Date(), sensorId: "s-12", value: 21.4 });

db.readings.aggregate([
  { $match: { sensorId: "s-12", at: { $gte: ISODate("2026-09-01") } } },
  { $group: {
      _id: { $dateTrunc: { date: "$at", unit: "hour" } },
      avg: { $avg: "$value" },
      n:   { $sum: 1 }
  } }
])
  • The server groups measurements into buckets automatically, so a time-series collection replaces the hand-rolled bucket pattern.
  • Set metaField to the field you filter by most often; measurements are grouped by it inside each bucket, which is what makes the compression effective.
  • granularity is a hint about the interval between measurements, not a limit. A wrong value costs compression rather than correctness.
  • Documents are effectively append-only. Updates and deletes work, but they are far less efficient than inserts, so treat the data as immutable.
  • Use expireAfterSeconds for retention instead of a scheduled cleanup job.

Vector search, and when to choose another store

db.products.aggregate([
  { $vectorSearch: {
      index: "vector_index",
      path: "embedding",
      queryVector: queryVec,      // same model and dimension as the index
      numCandidates: 200,
      limit: 10
  } },
  { $project: { title: 1, score: { $meta: "vectorSearchScore" } } }
])
NeedBetter fit
Exact relational constraints and joinsA relational database
Full-text relevance off AtlasA dedicated search engine
Sub-millisecond key lookupsA cache or a key-value store
Very large analytical scansA columnar warehouse
Millions of vectors with heavy filteringA dedicated vector database
  • The mechanism is $vectorSearch against an Atlas Search index whose definition declares the field, the number of dimensions and the similarity metric. It has to be the first stage of the pipeline.
  • Vector search compares embeddings by similarity, so the query vector must come from the same model and dimension as the indexed field — mixing models produces meaningless neighbours.
  • Normalise vectors the same way at index time and at query time, or the ranking is dominated by magnitude rather than by direction.
  • Hybrid search combines $vectorSearch with $search in a single pipeline, which usually beats either one alone for real queries.
  • A database is a tool, not a religion. Choose the store that matches the access pattern, and accept the cost of a second system only when the benefit is measurable.

FAQ

Should I move to Atlas for search?
If you are already on Atlas, search becomes a pipeline stage rather than a second system to operate, and that is a real win. If you are self-hosted and search matters, a dedicated engine is usually a better investment than migrating the database.
When is a time-series collection worth it?
When measurements arrive continuously and are queried by time plus a metadata field. Automatic bucketing improves both compression and query speed, and it removes bucket logic you would otherwise maintain by hand.

The aggregation pipeline Data modelling: embedding vs referencing and schema validation

Last refreshed 2026-09-18.