Local Atlas Search ($search)
AI-generated content
This document was generated by an AI assistant. Verify accuracy before relying on the details.
The packages/data search pipelines (person / part / sale / contract / ticket typed search) emit an Atlas Search $search stage. That stage is Atlas-only: against the default local mongo:7 community server it fails with "$search stage is only allowed on MongoDB Atlas". The adb-parts Worker maps that to an explicit 501 so it reads as a limitation, not a code bug; persons / contracts / tickets currently surface a raw 500 for the same reason.
This runbook makes $search work locally by swapping the plain mongod for MongoDB's all-in-one mongodb-atlas-local image (mongod + mongot) and creating the search indexes from the definitions already checked into the repo.
TL;DR
cd infra
make up-search # infra stack on mongodb-atlas-local (first run pulls ~670 MB)
make seed # only if switching from a fresh/plain-mongo run
make search-indexes # create the Atlas Search indexes (idempotent)
Then run the CF stack as usual (pnpm dev + adb-ui). Typed search now returns results instead of a 501/500.
Which image, and why
| Image | What it is | Use it? |
|---|---|---|
mongodb/mongodb-atlas-search | The bare mongot search process (a sidecar). Wiring it to a community mongod needs a shared keyfile + mongotHost/searchIndexManagementHostAndPort setParameters and is unsupported before MongoDB 8.2. | No |
mongodb/mongodb-atlas-local | All-in-one: mongod + mongot as one self-initialising single-node replica set. MongoDB's documented path for local Atlas Search. Publishes a 7.0 track, native arm64. | Yes |
We pin mongodb/mongodb-atlas-local:7.0.34 — same server major as the default mongo:7 (so a seeded /data/db stays compatible), and a stable tag rather than preview (whose Community mongot has an incomplete $listSearchIndexes, which the index bootstrap's idempotency check relies on).
How it is wired
infra/docker/compose.search.yaml— an overlay oncompose.yaml. It swaps themongoimage, pinshostname: mongo(so the replica-set member is advertised asmongo:27017, matching what the Spring services resolve), clears the basemongod --replSet rs0command so atlas-local's own entrypoint runs, and adds theconfigdb+mongotdata volumes atlas-local needs.make up-searchlayers it via-f compose.yaml -f compose.search.yaml(which disables the auto-loadedcompose.override.yaml).create-search-indexes.sh— a one-shot sidecar (gated behind theindexescompose profile so it never auto-runs). It reads the seven definitions inadb-infrastructure/mongodb-atlas/atlas-cli/search/indexes/*.json, reshapes each Atlas-CLI envelope{ name, database, collectionName, mappings, analyzers }into thecreateSearchIndex(name, { mappings, analyzers })mongosh helper, and applies it. It is idempotent: it skips an index that already exists and a collection not yet seeded, and retries per index (mongot can lag mongod readiness).
The seven indexes and their collections:
| Definition | Index | Collection | Used by the CF port? |
|---|---|---|---|
persons-search-index.json | adb-persons-… | adb-persons.persons | ✅ person search |
parts-search-index.json | adb-parts-… | adb-parts.parts | ✅ part search |
parts-sales-search-index.json | adb-parts-sales-… | adb-parts.sales | ✅ sale search |
contracts-search-index.json | adb-contracts-… | adb-contracts.contracts | ✅ contract search |
tickets-search-index.json | adb-tickets-… | adb-tickets.tickets | ✅ ticket search |
views-parts-search-index.json | adb-views-parts-… | adb-views.parts | ❌ CF port uses $regex, not $search |
invoices-search-index.json | adb-accounting-invoices-… | adb-accounting.invoices | ❌ CF invoice search drops the $search stage (TODO parity) |
The last two are created for fidelity with real Atlas but no ported code queries them yet.
The analyzer (what "case-insensitive-search-regex" means)
Every index uses one custom analyzer, search_keyword_lowercaser:
- tokenizer
keyword— the entire field value is a single token, so only wildcard-wrapped patterns (*term*) match a substring; lowercasetoken filter — case-insensitive;- a
mappingcharFilter — a hand-written accent-folding table (é→e,ç→c, …).
So it is a LIKE-style, accent- and case-insensitive match, not true full-text search — hence the name.
The CF broker needs no change
The broker's local services/mongo-broker/.dev.vars already connects with directConnection=true (mongodb://host.docker.internal:27018/?directConnection=true), which skips topology discovery — so it does not care how atlas-local advertises its replica-set member. atlas-local keeps the same 27018:27017 host port. Nothing to edit.
Caveats
- Not read-your-writes.
mongotrefreshes its Lucene index ~1s after a write, so a document created and immediately searched may not appear yet. - Parity is only as good as the checked-in definitions. The seven JSONs have a single commit in history (
6376e7e Convert submodules to monorepo) and have never been reconciled against the live Atlas cluster. If prod's indexes have since drifted, local results will differ, and nothing in the test suite or CI can catch it — thepackages/datasearch tests assert pipeline shape against a fake client, never a result. Treat local$searchas a good functional check, not a parity oracle. - Two searches still won't work locally even with this stack: the
adb-viewsparts search (the properties list — the CF port uses$regex) andadb-accountinginvoice search (the CF port drops the$searchstage). These are distinct gaps, not fixed here. - Image pull may hang behind Cloudflare WARP. If
make up-searchstalls on the image pull, a busy-looping WARP daemon is poisoning Docker's registry path — Quit WARP (not just Disconnect) and restart Docker Desktop, then retry. This is why the compose overlay was authored but could not be validated against a live pull. - If the container fails to boot, the most likely culprit is the
command: []reset in the overlay — remove that line so atlas-local uses its image-default CMD, and retry.
Making it the default (once validated)
make up-search is deliberately opt-in so the known-good mongo:7 path stays the default. Once the atlas-local image is confirmed working on your machine, promoting it to the default is a small change: fold the mongo service block from compose.search.yaml into compose.yaml and drop the now-redundant mongo-init replica-set bootstrap (atlas-local self-initialises its replica set).