vybekiit 0.7.34 → 0.7.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +520 -9
- package/dist/global-skills/eas-app-stores/SKILL.md +2 -2
- package/dist/global-skills/eas-hosting/SKILL.md +2 -2
- package/dist/global-skills/eas-observe/SKILL.md +7 -15
- package/dist/global-skills/eas-observe/references/queries.md +51 -10
- package/dist/global-skills/eas-observe/references/setup.md +60 -7
- package/dist/global-skills/eas-simulator/SKILL.md +2 -2
- package/dist/global-skills/eas-simulator/references/run-your-app.md +2 -0
- package/dist/global-skills/eas-update/SKILL.md +2 -2
- package/dist/global-skills/eas-update-insights/SKILL.md +2 -2
- package/dist/global-skills/eas-workflows/SKILL.md +2 -2
- package/dist/global-skills/expo-animation/SKILL.md +2 -2
- package/dist/global-skills/expo-app-clip/SKILL.md +2 -2
- package/dist/global-skills/expo-brownfield/SKILL.md +2 -2
- package/dist/global-skills/expo-data-fetching/SKILL.md +2 -2
- package/dist/global-skills/expo-design-system/SKILL.md +2 -2
- package/dist/global-skills/expo-dev-client/SKILL.md +4 -2
- package/dist/global-skills/expo-dom/SKILL.md +2 -2
- package/dist/global-skills/expo-examples/SKILL.md +2 -2
- package/dist/global-skills/expo-migrate-module/SKILL.md +2 -2
- package/dist/global-skills/expo-module/SKILL.md +2 -2
- package/dist/global-skills/expo-native-ui/SKILL.md +2 -2
- package/dist/global-skills/expo-overview/SKILL.md +9 -4
- package/dist/global-skills/expo-overview/references/launch-without-dev-menu.md +24 -0
- package/dist/global-skills/expo-project-structure/SKILL.md +2 -2
- package/dist/global-skills/expo-router/SKILL.md +2 -2
- package/dist/global-skills/expo-ui/SKILL.md +2 -2
- package/dist/global-skills/expo-upgrade/SKILL.md +2 -2
- package/dist/global-skills/expo-web-to-native/SKILL.md +2 -2
- package/dist/global-skills/expo-web-to-native/references/verify-on-device.md +1 -1
- package/dist/global-skills/firebase-app-hosting-basics/SKILL.md +16 -3
- package/dist/global-skills/firebase-app-hosting-basics/references/cli_commands.md +10 -12
- package/dist/global-skills/firebase-app-hosting-basics/references/configuration.md +2 -1
- package/dist/global-skills/firebase-basics/SKILL.md +6 -4
- package/dist/global-skills/firebase-basics/references/web_setup.md +3 -3
- package/dist/global-skills/instrument-feature-flags/references/adding-feature-flag-code.md +81 -333
- package/dist/global-skills/instrument-feature-flags/references/android.md +19 -13
- package/dist/global-skills/instrument-feature-flags/references/api.md +17 -13
- package/dist/global-skills/instrument-feature-flags/references/best-practices.md +35 -35
- package/dist/global-skills/instrument-feature-flags/references/django.md +120 -72
- package/dist/global-skills/instrument-feature-flags/references/dotnet.md +48 -102
- package/dist/global-skills/instrument-feature-flags/references/elixir.md +4 -10
- package/dist/global-skills/instrument-feature-flags/references/flask.md +55 -39
- package/dist/global-skills/instrument-feature-flags/references/flutter.md +19 -13
- package/dist/global-skills/instrument-feature-flags/references/go.md +21 -13
- package/dist/global-skills/instrument-feature-flags/references/ios.md +19 -13
- package/dist/global-skills/instrument-feature-flags/references/java.md +4 -12
- package/dist/global-skills/instrument-feature-flags/references/laravel.md +18 -16
- package/dist/global-skills/instrument-feature-flags/references/next-js.md +44 -54
- package/dist/global-skills/instrument-feature-flags/references/nodejs.md +21 -13
- package/dist/global-skills/instrument-feature-flags/references/php.md +21 -13
- package/dist/global-skills/instrument-feature-flags/references/python.md +22 -14
- package/dist/global-skills/instrument-feature-flags/references/react-native.md +18 -13
- package/dist/global-skills/instrument-feature-flags/references/react.md +21 -14
- package/dist/global-skills/instrument-feature-flags/references/ruby-on-rails.md +31 -68
- package/dist/global-skills/instrument-feature-flags/references/ruby.md +21 -13
- package/dist/global-skills/instrument-feature-flags/references/rust.md +10 -34
- package/dist/global-skills/instrument-feature-flags/references/usage.md +94 -114
- package/dist/global-skills/instrument-feature-flags/references/web.md +21 -13
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-android.md +7 -7
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-expo.md +2 -2
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-fastapi.md +1 -1
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-flask.md +1 -1
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-laravel.md +2 -2
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-php.md +1 -1
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-python.md +1 -1
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-ruby.md +1 -1
- package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-swift.md +4 -4
- package/dist/global-skills/instrument-product-analytics/references/android.md +64 -109
- package/dist/global-skills/instrument-product-analytics/references/angular.md +32 -28
- package/dist/global-skills/instrument-product-analytics/references/astro.md +29 -34
- package/dist/global-skills/instrument-product-analytics/references/configuration.md +27 -42
- package/dist/global-skills/instrument-product-analytics/references/django.md +120 -72
- package/dist/global-skills/instrument-product-analytics/references/dotnet.md +48 -102
- package/dist/global-skills/instrument-product-analytics/references/elixir.md +25 -50
- package/dist/global-skills/instrument-product-analytics/references/flask.md +55 -39
- package/dist/global-skills/instrument-product-analytics/references/flutter.md +94 -96
- package/dist/global-skills/instrument-product-analytics/references/go.md +39 -77
- package/dist/global-skills/instrument-product-analytics/references/identify-users.md +24 -26
- package/dist/global-skills/instrument-product-analytics/references/ios.md +22 -22
- package/dist/global-skills/instrument-product-analytics/references/laravel.md +18 -16
- package/dist/global-skills/instrument-product-analytics/references/next-js.md +44 -54
- package/dist/global-skills/instrument-product-analytics/references/nuxt-js-3-6.md +32 -34
- package/dist/global-skills/instrument-product-analytics/references/nuxt-js.md +36 -36
- package/dist/global-skills/instrument-product-analytics/references/php.md +42 -87
- package/dist/global-skills/instrument-product-analytics/references/posthog-python.md +15 -12
- package/dist/global-skills/instrument-product-analytics/references/python.md +106 -154
- package/dist/global-skills/instrument-product-analytics/references/react-native.md +97 -176
- package/dist/global-skills/instrument-product-analytics/references/react-router-v6.md +29 -40
- package/dist/global-skills/instrument-product-analytics/references/react-router-v7-data-mode.md +29 -38
- package/dist/global-skills/instrument-product-analytics/references/react-router-v7-declarative-mode.md +29 -40
- package/dist/global-skills/instrument-product-analytics/references/react-router-v7-framework-mode.md +32 -37
- package/dist/global-skills/instrument-product-analytics/references/ruby-on-rails.md +31 -68
- package/dist/global-skills/instrument-product-analytics/references/ruby.md +40 -96
- package/dist/global-skills/instrument-product-analytics/references/svelte.md +34 -34
- package/dist/global-skills/instrument-product-analytics/references/tanstack-start.md +19 -19
- package/dist/global-skills/instrument-product-analytics/references/usage.md +94 -114
- package/dist/global-skills/instrument-product-analytics/references/vue-js.md +33 -45
- package/dist/global-skills/mongodb-search-and-ai/SKILL.md +40 -22
- package/dist/global-skills/mongodb-search-and-ai/references/automated-embedding.md +8 -2
- package/dist/global-skills/mongodb-search-and-ai/references/quick-start.md +725 -0
- package/dist/global-skills/mongodb-search-and-ai/scripts/quickstart_complete.py +386 -0
- package/dist/global-skills/neon/references/claimable-neon.md +2 -0
- package/dist/global-skills/neon-realtime/references/queries.md +2 -2
- package/dist/global-skills/resend/SKILL.md +1 -1
- package/dist/global-skills/resend/references/inboxes.md +49 -21
- package/dist/global-skills/resend/references/webhooks.md +1 -1
- package/dist/global-skills/stripe-best-practices/SKILL.md +1 -1
- package/dist/global-skills/stripe-best-practices/references/connect.md +1 -1
- package/dist/global-skills/stripe-best-practices/references/security.md +9 -9
- package/dist/global-skills/vybekiitReferences/backend/AGENTS.md +1 -1
- package/dist/global-skills/vybekiitReferences/backend/agent/goal-routes.json +303 -18
- package/dist/global-skills/vybekiitReferences/extension/AGENTS.md +1 -1
- package/dist/global-skills/vybekiitReferences/extension/agent/goal-routes.json +352 -18
- package/dist/global-skills/vybekiitReferences/mobile/AGENTS.md +1 -1
- package/dist/global-skills/vybekiitReferences/mobile/agent/goal-routes.json +413 -22
- package/dist/global-skills/vybekiitReferences/spa/AGENTS.md +1 -1
- package/dist/global-skills/vybekiitReferences/spa/agent/goal-routes.json +391 -21
- package/dist/global-skills/vybekiitReferences/web/AGENTS.md +14 -2
- package/dist/global-skills/vybekiitReferences/web/agent/goal-routes.json +490 -32
- package/package.json +8 -8
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
# Quick Start
|
|
2
|
+
|
|
3
|
+
**Scope**: This guide is a step-by-step walkthrough that demonstrates semantic search (using Automated Embedding), keyword search (using MongoDB Search), and hybrid search against the `sample_mflix` sample dataset in the user's cluster. It prescribes an exact interaction sequence, index names, and queries. For the underlying reference material behind each step, see `automated-embedding.md`, `vector-search.md`, `lexical-search-indexing.md`, `lexical-search-querying.md`, and `hybrid-search.md`.
|
|
4
|
+
|
|
5
|
+
**Only run this once the user has chosen the sample-data tour.** It teaches how search works on a fixed dataset; it does not build search for the user's own collections. If you arrived here from a general request like "I'm new to search, help me get started" without the user picking the tour over working on their own data, go back to `SKILL.md` Step 0 and offer that choice first.
|
|
6
|
+
|
|
7
|
+
**This walkthrough requires an Atlas cloud cluster.** The sample dataset loads from the Atlas UI, and Automated Embedding works on every Atlas tier with no key management. Atlas Local and self-managed MongoDB are out of scope — Step 0.2 covers how they get detected and routed out.
|
|
8
|
+
|
|
9
|
+
## Interaction Principle
|
|
10
|
+
|
|
11
|
+
**Act first. Explain second. Then ask.**
|
|
12
|
+
|
|
13
|
+
Every step follows the same rhythm:
|
|
14
|
+
1. Do something automatically (create an index, run a query)
|
|
15
|
+
2. Explain what just happened in plain language
|
|
16
|
+
3. Ask if the user wants to go one step further
|
|
17
|
+
|
|
18
|
+
Never ask the user to decide something they haven't seen yet.
|
|
19
|
+
|
|
20
|
+
**Never end a turn without the next step.** Every turn closes with an AskUserQuestion or a completed action, and a step's question goes in the same turn as its explanation. The only valid stopping points are the user choosing to stop, or Wrap Up.
|
|
21
|
+
|
|
22
|
+
**"No" does not end the walkthrough, except at a bridge step.**
|
|
23
|
+
|
|
24
|
+
- **Enrichment questions** (6a, 7a, 5b, 6b, 9c) offer an optional extra inside the current path. A decline advances to that path's bridge step: 8a for Path A, 6a2 for Path A2, 7b for Path B, Wrap Up for Path C.
|
|
25
|
+
- **Bridge questions** offer the next search mode. A decline goes to Wrap Up, the only place an intermediate "No" ends the tour.
|
|
26
|
+
|
|
27
|
+
Never re-offer a declined branch, and never substitute one the user didn't ask for.
|
|
28
|
+
|
|
29
|
+
## Creating an Index
|
|
30
|
+
|
|
31
|
+
Four steps create one: Step 4a (`quickstart_semantic`), Step 4a2 (`quickstart_manual`), Step 3b (`quickstart_text`), and Step 6b (`quickstart_autocomplete`). Both rules below apply at every one of them.
|
|
32
|
+
|
|
33
|
+
**1 — Ask first.** `create-index` mutates the cluster, so it needs the explicit approval `SKILL.md` requires, in the turn immediately before the call. Steps 4a, 3b, and 6b each carry their own confirmation question; Step 4a2 is reached only by the user picking **Try with existing embeddings** or **Switch to manual embeddings**, which is itself the approval. Never treat a path choice as index approval: picking Hybrid at Step 2, or answering yes at Step 8a or Step 7b, selects a route, not consent to each index along it.
|
|
34
|
+
|
|
35
|
+
**2 — Check the name before using it.** Run `collection-indexes` on the target collection first, because re-running the tour on a cluster that already has these index names is the common case.
|
|
36
|
+
|
|
37
|
+
- **Name free** → create it.
|
|
38
|
+
- **Name taken, definition matches what the step would create** → create nothing. Tell the user you're reusing it and skip to that step's query if the status is `READY`. `Building` or `Pending` → poll per Step 3a.3 ②. `Stale` → handle per Step 3a.3 ②.
|
|
39
|
+
- **Name taken by a different definition** → do **not** drop it. Say the name is in use and create `<name>_2` instead. That name then replaces the original everywhere downstream: the step's own queries, Path C's pipelines, and the matching `SEMANTIC_INDEX_NAME`, `TEXT_INDEX_NAME`, or `AUTOCOMPLETE_INDEX_NAME` constant if the Wrap Up script is handed over. Carry any collision name you created to Wrap Up; left at its default, the script stops on the conflicting index instead of reusing yours.
|
|
40
|
+
|
|
41
|
+
## Step 0 — Check Connection and Deployment
|
|
42
|
+
|
|
43
|
+
**0.1 — Verify the MongoDB MCP is connected** by calling `list-databases`.
|
|
44
|
+
|
|
45
|
+
- If it **succeeds**: continue to 0.2.
|
|
46
|
+
- If it **fails**: get the connection working before continuing. If the `mongodb-mcp-setup` skill is available, invoke it inline. If it is not available (it does not ship in every plugin), tell the user:
|
|
47
|
+
> "I can't reach your cluster through the MongoDB MCP server. Check that the server is running and that `MDB_MCP_CONNECTION_STRING` (or your Atlas service account credentials) is set in your client config, then restart the client and tell me when it's ready."
|
|
48
|
+
|
|
49
|
+
Do not continue until `list-databases` succeeds.
|
|
50
|
+
|
|
51
|
+
**0.2 — Determine the deployment without asking (silent).** Do **not** ask the user what kind of deployment they have.
|
|
52
|
+
|
|
53
|
+
**Principle, used again in Step 3a: never block a beginner on a question they can't answer. Try the real operation and let its result tell you what the deployment supports.**
|
|
54
|
+
|
|
55
|
+
- **Opportunistic confirmation only.** If `atlas-local-list-deployments` exists *and* reports a running deployment you are connected to, that is positive evidence of **Atlas Local**: route out with the message below. Neither tool's absence or failure proves anything, because `atlas-local-list-deployments` ships only with the local MCP server and `atlas-inspect-cluster` is unregistered whenever the server has a connection string but no Atlas API service account.
|
|
56
|
+
- **Otherwise default to Atlas cloud** and proceed silently to Step 1. That is the overwhelmingly common case and the one this walkthrough is written for. Attempt no other probe: the MongoDB MCP server exposes no tool that returns cluster hostnames or raw command output.
|
|
57
|
+
|
|
58
|
+
**Where non-Atlas deployments actually get caught:** Step 1 cannot find or load `sample_mflix`, or Step 4a's `autoEmbed` index build fails. Both are real evidence; use the routing message below at that point.
|
|
59
|
+
|
|
60
|
+
**If there is real evidence of Atlas Local or self-managed MongoDB:** stop the walkthrough. Do not suggest starting a local deployment — they are already running MongoDB somewhere. Tell the user:
|
|
61
|
+
> "This guided walkthrough is only available for a cluster with the sample movies dataset on Atlas. Two options:
|
|
62
|
+
>
|
|
63
|
+
> 1. **Connect to a free Atlas cluster** (M0, no card required), load the sample dataset, and we'll run this tour end to end.
|
|
64
|
+
> 2. **Skip the tour and build search on your own data here.** Tell me what you want to search and I'll design the index and queries against your actual collections."
|
|
65
|
+
|
|
66
|
+
Use AskUserQuestion with those two options. If the user picks 1, wait for the Atlas cloud connection and restart at Step 0. If they pick 2, leave this walkthrough and use the main skill workflow in `SKILL.md`; without Voyage AI keys configured, semantic search there means manual embeddings (`vector-search.md`), not `autoEmbed`.
|
|
67
|
+
|
|
68
|
+
**0.3 — Confirm index creation is available.** Every path here creates at least one search index, so check for a `create-index` tool before starting one. If it is absent, the session is read-only in the sense `SKILL.md` defines (no `create`, `update`, or `delete` operation tools) and the walkthrough cannot complete. Do not enter a path and stop at its creation step. Tell the user:
|
|
69
|
+
> "I can read your cluster in this session but not create search indexes, so I can't run the guided tour for you. What I can do is design the indexes and queries and hand you the index JSON to create in the Atlas UI yourself."
|
|
70
|
+
|
|
71
|
+
Then leave this walkthrough and use the main skill workflow in `SKILL.md`, which handles read-only mode.
|
|
72
|
+
|
|
73
|
+
## Step 1 — Load Sample Data
|
|
74
|
+
|
|
75
|
+
Use `list-databases` to check if `sample_mflix` exists on the cluster.
|
|
76
|
+
|
|
77
|
+
**If it does not exist:** tell the user:
|
|
78
|
+
> "We'll use a sample movies dataset that Atlas provides. To load it: go to cloud.mongodb.com → your cluster → click the vertical ellipses → select **Load Sample Dataset**. Come back once it's loaded."
|
|
79
|
+
|
|
80
|
+
Wait for confirmation, then re-run `list-databases` to verify `sample_mflix` now appears. If it does not appear yet, tell the user:
|
|
81
|
+
> "It's not available yet. Give it another moment and let me know when it's loaded."
|
|
82
|
+
|
|
83
|
+
Repeat until `sample_mflix` is confirmed present before continuing.
|
|
84
|
+
|
|
85
|
+
**If the user reports there is no Load Sample Dataset option**, that is the real evidence Step 0.2 was waiting for: the deployment is not Atlas cloud. Stop here and use the routing message in Step 0.2. Do not ask which kind of non-Atlas deployment it is — the routing message is the same either way.
|
|
86
|
+
|
|
87
|
+
**For everyone (whether dataset was just loaded or already present):**
|
|
88
|
+
|
|
89
|
+
**Confirm the collection, not just the database name.** A cluster can carry a `sample_mflix` database that is not the Atlas sample dataset, so run `list-collections` on it before anything else. Only `movies` matters here; `embedded_movies` is checked at Step 3a2, and its absence does not block Path A, B, or C.
|
|
90
|
+
|
|
91
|
+
- **`movies` missing:** the sample dataset is not loaded, whatever the database name suggests. Repeat the Load Sample Dataset instructions above and re-verify, treating "there is no Load Sample Dataset option" exactly as described above — the Step 0.2 routing message.
|
|
92
|
+
- **`movies` present but `collection-schema` shows no `title`, `plot`, or `genres`:** this is the user's own collection, not the sample one. Do not index it and do not rename anything. Use the Step 0.2 routing message; its second option, building search on their own data through `SKILL.md`, is the fit here.
|
|
93
|
+
|
|
94
|
+
Otherwise use `collection-schema` on `sample_mflix.movies`, show a condensed example document (`title`, `plot`, `genres` only), and tell the user this is a real dataset of ~21,000 movies already on their cluster, one document per movie, with `title`, `plot`, and `genres` being the fields the walkthrough makes searchable — the same structure they would use for their own content.
|
|
95
|
+
|
|
96
|
+
## Step 2 — Choose Your Path
|
|
97
|
+
|
|
98
|
+
**Skip this step if the user already named a search type** when they asked ("I want to try vector search", "show me keyword search") — go straight to Path A, Path B, or, for hybrid, the hybrid route below. Do not re-ask a question they already answered. Skip only this question: Step 0's connection check and Step 1's `sample_mflix` check still have to pass before any path starts.
|
|
99
|
+
|
|
100
|
+
Otherwise use AskUserQuestion to ask:
|
|
101
|
+
|
|
102
|
+
> "What kind of search do you want to try?"
|
|
103
|
+
|
|
104
|
+
- **Semantic Search** *(Recommended)* — Find results by meaning, even when words don't match. Best for discovery, recommendations, and natural language queries.
|
|
105
|
+
- **Keyword Search** — Find results by exact words, with typo tolerance and autocomplete. Best for search boxes where users type specific terms.
|
|
106
|
+
- **Hybrid Search** — Run both at once and merge the rankings. Best when queries mix exact terms with open-ended intent.
|
|
107
|
+
- **I'm not sure** — routes to Semantic Search with an extra line of explanation.
|
|
108
|
+
|
|
109
|
+
**If the user picks Hybrid, or asked for hybrid by name:** hybrid needs a semantic index *and* a keyword index on `sample_mflix.movies`, so create both before Path C, in this order, confirming each.
|
|
110
|
+
|
|
111
|
+
1. Say so before creating anything:
|
|
112
|
+
> "Hybrid search runs keyword and semantic search together, so it needs both kinds of index on the movies collection. I'll build the semantic one first, then the keyword one, and I'll check with you before each. Then we'll run the hybrid query."
|
|
113
|
+
2. **Semantic index.** Run Step 3a and Step 4a in full, Step 4a's AskUserQuestion confirmation included. Skip Steps 5a through 8a.
|
|
114
|
+
3. **Keyword index.** Run Step 3b only. Skip Steps 4b through 7b.
|
|
115
|
+
4. Go to Path C.
|
|
116
|
+
|
|
117
|
+
If at Step 4a the user takes **Try with existing embeddings** instead, hybrid on `movies` is off the table, because Path A2 builds on `embedded_movies`. Run Path A2 from Step 3a2 and let Step 6a2's disclosure offer the second index.
|
|
118
|
+
|
|
119
|
+
## Path A: Semantic Search (Automated Embedding)
|
|
120
|
+
|
|
121
|
+
### Step 3a — Cluster Readiness
|
|
122
|
+
|
|
123
|
+
Apply the Step 0.2 principle to cluster tier: try the real operation and let its result tell you what the tier supports. Do NOT ask the user "what tier are you on?" up front.
|
|
124
|
+
|
|
125
|
+
**3a.1 — Try to auto-detect the tier (silent, best-effort).**
|
|
126
|
+
|
|
127
|
+
Attempt `atlas-inspect-cluster`. It returns cluster metadata, of which two fields matter here:
|
|
128
|
+
|
|
129
|
+
- `instanceType`, one of `FREE`, `FLEX`, or `DEDICATED`
|
|
130
|
+
- `instanceSize`, populated only when `instanceType` is `DEDICATED` (`M10`, `M20`, and so on)
|
|
131
|
+
|
|
132
|
+
`instanceType` is what drives the index caps in 3a.3 ①.
|
|
133
|
+
|
|
134
|
+
**Storage auto-scaling is not readable.** No MCP tool returns it. Never gate on it, and never claim to have checked it. On `DEDICATED`, treat it as unknown, continue to the build, and rely on the `Stale` signal in 3a.3 ② as the only evidence that disk ran out.
|
|
135
|
+
|
|
136
|
+
- **If it succeeds:** note `instanceType` internally and apply the tier-specific guidance in 3a.3. Don't make the user do anything.
|
|
137
|
+
- **If it fails** (401 Unauthorized / no Atlas Admin API key / any error): do NOT stop and do NOT interrogate the user. Ask and continue:
|
|
138
|
+
> "I couldn't automatically read your cluster tier — that needs a separate Atlas management key, which isn't required for anything we're doing. No problem: I'll just build the index, and if your tier can't support it, the build itself will tell us and I'll walk you through the fix."
|
|
139
|
+
|
|
140
|
+
(Optional, offered as help — never required:)
|
|
141
|
+
> "If you're curious, your tier is shown right under your cluster's name in the Atlas UI — e.g. 'M0 Sandbox', 'Flex', or 'M10'."
|
|
142
|
+
|
|
143
|
+
**3a.2 — Proceed straight to the index build (the real test).** Go to Step 4a, which walks through the cost and creates the `quickstart_semantic` auto-embed index. The creation call is the source of truth — it either succeeds or returns one of the two catchable errors in 3a.3.
|
|
144
|
+
|
|
145
|
+
**3a.3 — Catch the only two real failures.**
|
|
146
|
+
|
|
147
|
+
**① Index-count cap (Free/Flex).** Current limits: **M0 = 3** search/vector indexes total, **Flex = 10** total — and these counts include indexes already on the collection. If `create-index` returns an error about exceeding the maximum number of indexes:
|
|
148
|
+
> "Heads up — you've hit your cluster's index limit. Free (M0) clusters allow **3 search/vector indexes total** and Flex allows **10**. These counts include any indexes already on the cluster.
|
|
149
|
+
>
|
|
150
|
+
> Two ways forward:
|
|
151
|
+
> - **Make room:** delete an unused search index, or
|
|
152
|
+
> - **Scale up:** move to Flex (10) or a dedicated tier for more headroom.
|
|
153
|
+
>
|
|
154
|
+
> Want me to list your existing indexes so we can see what's using the budget?"
|
|
155
|
+
|
|
156
|
+
If they agree, run `collection-indexes` and show the current indexes so they can choose which ones to remove. Never delete anything without explicit confirmation. After freeing room or scaling, retry the build.
|
|
157
|
+
|
|
158
|
+
**② Dedicated cluster without storage auto-scaling (auto-embed only).** Automated Embedding on dedicated (`M10+`) clusters **requires storage auto-scaling**. This is a *disk* requirement, not a compute/tier one: do **not** tell users to raise their max cluster tier.
|
|
159
|
+
|
|
160
|
+
The failure mode is **not** a failed build. Out of disk, MongoDB *pauses* embedding generation and the index goes **`Stale`**, resuming on its own once disk is freed. A `Stale` status from `collection-indexes` is the only signal; `create-index` never errors.
|
|
161
|
+
|
|
162
|
+
**Polling procedure.** Once `create-index` returns, poll `collection-indexes` on `sample_mflix.movies` roughly every 30 seconds, reporting progress instead of going quiet:
|
|
163
|
+
|
|
164
|
+
1. `READY` → continue to the next step in the current path.
|
|
165
|
+
2. `Building` or `Pending` → keep polling. Expect 2 to 5 minutes for ~21,000 movies. Past 10 minutes with no change, say so and keep polling.
|
|
166
|
+
3. `Stale` → stop polling and show the message below. Never drop or re-create the index, because the embeddings generated so far are kept.
|
|
167
|
+
|
|
168
|
+
On `Stale`:
|
|
169
|
+
> "Automated Embedding on a dedicated cluster **requires storage auto-scaling**. MongoDB stores the generated embeddings on your own cluster, so the index needs room to grow as it embeds your documents. Your cluster ran out of disk, so embedding generation paused and the index went into a `Stale` state. Nothing is lost, and it resumes on its own once there's space again.
|
|
170
|
+
>
|
|
171
|
+
> To enable it: Atlas UI → your cluster → **Edit Configuration** → **Storage** → turn on **storage auto-scaling**, then tell me when it's on.
|
|
172
|
+
>
|
|
173
|
+
> Prefer not to change cluster settings? We can switch to the manual-embeddings path instead. It uses pre-computed vectors already in the sample data, with no embedding generation and no token usage."
|
|
174
|
+
|
|
175
|
+
Then use AskUserQuestion with exactly these two options:
|
|
176
|
+
|
|
177
|
+
- **I enabled storage auto-scaling** → resume polling the *same* index from step 1 above. Do not call `create-index` again, and do not repeat Step 4a's explanation or confirmation.
|
|
178
|
+
- **Switch to manual embeddings** → go to Path A2.
|
|
179
|
+
|
|
180
|
+
Do not poll while waiting for that answer, and do not wait for the status to clear by itself. Nothing changes until the user acts.
|
|
181
|
+
|
|
182
|
+
**Note on tiers:** this requirement is scoped to dedicated clusters and does **not** mean autoEmbed requires `M10+`. Free and Flex can use Automated Embedding, subject to ①'s caps and Free Tier rate limits. Never tell a Free/Flex user it is unavailable to them.
|
|
183
|
+
|
|
184
|
+
### Step 4a — Recommend AutoEmbed Index
|
|
185
|
+
|
|
186
|
+
**Cost is an FYI, not a gate.** The token note below is part of explaining how Automated Embedding works. Never turn it into a separate decision the user has to settle first, and never ask them to approve spend before they have seen what the feature does.
|
|
187
|
+
|
|
188
|
+
Before creating anything, explain the value and cost:
|
|
189
|
+
|
|
190
|
+
> **Here's how Automated Embedding works:**
|
|
191
|
+
>
|
|
192
|
+
> MongoDB handles everything — you don't write any embedding code. When you create this index, MongoDB reads every movie plot and converts it into a **vector** (a list of numbers that captures its meaning) using a Voyage AI model called **voyage-4**. It does this automatically, in the background, every time you add or update a document.
|
|
193
|
+
>
|
|
194
|
+
> **Token allocation:**
|
|
195
|
+
> - You get **200 million free tokens** per model, per organization — one-time, shared across all your Atlas projects
|
|
196
|
+
> - After that, voyage-4 costs **$0.06 per million tokens**
|
|
197
|
+
> - For reference: ~21,000 movie plots is roughly 5–10M tokens total for the initial sync
|
|
198
|
+
>
|
|
199
|
+
> **If you're on M0:** Once your 200M free tokens are exhausted, MongoDB automatically invoices you for additional usage — index builds and queries don't stop. You can add a payment method to your Atlas account without upgrading your cluster (Atlas → Billing → Payment Method). Charges are only for embedding model usage.
|
|
200
|
+
>
|
|
201
|
+
> **Two more things worth knowing before you commit:**
|
|
202
|
+
> - **Where the embedding happens.** The embedding model runs on inference infrastructure that MongoDB operates — a multi-tenant service on Google Cloud in a **US region** — *regardless of which cloud provider or region your cluster is in*. Your text is sent there to be embedded. **Data transfer costs apply** on top of the token costs above. If you have data-residency requirements, this is the detail to check first.
|
|
203
|
+
> - **Where the embeddings are stored.** They live on *your* cluster, in a dedicated internal database (one generated-embeddings collection per autoEmbed index). So they consume your disk, which is why dedicated clusters need storage auto-scaling.
|
|
204
|
+
>
|
|
205
|
+
> **The index we'd create looks like this:**
|
|
206
|
+
> ```json
|
|
207
|
+
> {
|
|
208
|
+
> "fields": [
|
|
209
|
+
> { "type": "autoEmbed", "modality": "text", "path": "plot", "model": "voyage-4" },
|
|
210
|
+
> { "type": "filter", "path": "genres" }
|
|
211
|
+
> ]
|
|
212
|
+
> }
|
|
213
|
+
> ```
|
|
214
|
+
|
|
215
|
+
Use AskUserQuestion:
|
|
216
|
+
> "Want to create this index and try semantic search?"
|
|
217
|
+
|
|
218
|
+
- **Yes, create it** → proceed to create the index
|
|
219
|
+
- **Try with existing embeddings** → route to Path A2 (manual vector search using pre-embedded data, no auto embedding needed)
|
|
220
|
+
|
|
221
|
+
If yes, apply the name check in **Creating an Index** rule 2 to `sample_mflix.movies`, then use `create-index` (or `mcp__mongodb-mcp-server__create-index`) to create a Vector Search index with `autoEmbed` type on it using the definition above.
|
|
222
|
+
|
|
223
|
+
Name the index: `quickstart_semantic`, or the collision name rule 2 produced. If rule 2 found a matching `READY` index, create nothing and go straight to Step 5a — skip the build message below.
|
|
224
|
+
|
|
225
|
+
**While the index builds, tell the user:**
|
|
226
|
+
> "MongoDB is now generating embeddings for all ~21,000 movie plots. This runs in the background and typically takes a few minutes for a collection of this size. I'll check when it's ready."
|
|
227
|
+
|
|
228
|
+
Wait for status `READY` with `collection-indexes` before running any queries, following the polling procedure in Step 3a.3 ②.
|
|
229
|
+
|
|
230
|
+
### Step 5a — First Semantic Query
|
|
231
|
+
|
|
232
|
+
Tell the user:
|
|
233
|
+
> "Let's run your first semantic query: **'a story about growing up'**"
|
|
234
|
+
|
|
235
|
+
Run this aggregation with `aggregate`:
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
db.movies.aggregate([
|
|
239
|
+
{
|
|
240
|
+
$vectorSearch: {
|
|
241
|
+
index: "quickstart_semantic",
|
|
242
|
+
path: "plot",
|
|
243
|
+
query: "a story about growing up",
|
|
244
|
+
numCandidates: 100,
|
|
245
|
+
limit: 3
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
$project: {
|
|
250
|
+
_id: 0,
|
|
251
|
+
title: 1,
|
|
252
|
+
plot: 1,
|
|
253
|
+
score: { $meta: "vectorSearchScore" }
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
])
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Display results in a clean table: Title | Score | Plot (truncated to ~100 chars).
|
|
260
|
+
|
|
261
|
+
**Explain to the user:**
|
|
262
|
+
> "None of those movie titles or plots contain the words 'growing up', but MongoDB found them anyway. That's semantic search: it matched the **meaning** of your query, not the words. The score shows how similar each result is to what you asked for: closer to 1.0 means more similar."
|
|
263
|
+
|
|
264
|
+
Point out one detail of the query you just ran: `query` is a plain text string, not a vector. With auto embedding MongoDB converts it internally, so the application never generates query vectors.
|
|
265
|
+
|
|
266
|
+
### Step 6a — Explore Another Query
|
|
267
|
+
|
|
268
|
+
Use AskUserQuestion:
|
|
269
|
+
> "Want to try another query to see it work on a different theme?"
|
|
270
|
+
|
|
271
|
+
- **"unlikely friendship"** → run and show results
|
|
272
|
+
- **"revenge and justice"** → run and show results
|
|
273
|
+
- **Type your own** → run whatever they enter
|
|
274
|
+
- **Skip, move on** → proceed to Step 7a
|
|
275
|
+
|
|
276
|
+
After results: point out which movies surfaced and note whether any of the query words appear in the plot. If they don't — highlight it.
|
|
277
|
+
|
|
278
|
+
### Step 7a — Try Filtering
|
|
279
|
+
|
|
280
|
+
Use AskUserQuestion:
|
|
281
|
+
> "Want to narrow the results to a specific genre? Filters let you combine semantic similarity with exact criteria like 'find me something emotionally similar to this query, but only in the Drama genre.'"
|
|
282
|
+
|
|
283
|
+
- **Yes, show me filtered search** → proceed
|
|
284
|
+
- **No, move on** → skip to Step 8a
|
|
285
|
+
|
|
286
|
+
If yes, run the same query with a genre filter:
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
db.movies.aggregate([
|
|
290
|
+
{
|
|
291
|
+
$vectorSearch: {
|
|
292
|
+
index: "quickstart_semantic",
|
|
293
|
+
path: "plot",
|
|
294
|
+
query: "a story about growing up",
|
|
295
|
+
filter: { genres: { $eq: "Drama" } },
|
|
296
|
+
numCandidates: 150,
|
|
297
|
+
limit: 3
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
$project: {
|
|
302
|
+
_id: 0,
|
|
303
|
+
title: 1,
|
|
304
|
+
genres: 1,
|
|
305
|
+
score: { $meta: "vectorSearchScore" }
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
])
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**Explain:**
|
|
312
|
+
> "MongoDB applied the genre filter **before** computing similarity. This narrows the candidate pool first, then finds the most semantically similar ones within it. This is faster than filtering after the fact."
|
|
313
|
+
|
|
314
|
+
Note that `filter` takes standard MongoDB query syntax and works on any field indexed as `type: filter`. Raising `numCandidates` (150 here versus 100 unfiltered) compensates for the narrowed pool.
|
|
315
|
+
|
|
316
|
+
### Step 8a — Bridge to Keyword / Hybrid
|
|
317
|
+
|
|
318
|
+
Use AskUserQuestion:
|
|
319
|
+
> "Semantic search is great for open-ended queries. Keyword search is better when users type specific titles or names. Want to see them side by side, then combine them into hybrid search?"
|
|
320
|
+
|
|
321
|
+
- **Yes** → run the abbreviated Path B below, then go to Path C
|
|
322
|
+
- **No, I'm done** → skip to Wrap Up
|
|
323
|
+
|
|
324
|
+
**Abbreviated Path B, reached only from here.** Run Steps 3b, 4b, and 5b, then go straight to Path C. **Skip Step 6b** (autocomplete is a detour from the comparison the user just asked for; offer it in Wrap Up only if they raise it) and **skip Step 7b's question** (they answered it here).
|
|
325
|
+
|
|
326
|
+
## Path A2: Semantic Search (Bring Your Own Embeddings)
|
|
327
|
+
|
|
328
|
+
*This path is for users who prefer not to use Automated Embedding. Uses the `sample_mflix.embedded_movies` collection, which already has pre-computed 1536-dimensional plot embeddings from OpenAI's text-embedding-ada-002 model.*
|
|
329
|
+
|
|
330
|
+
**Path A2 replaces Path A. It is not a continuation of it.** Two entry points lead here: Step 4a's **Try with existing embeddings** option, before any autoEmbed index exists, and Step 3a.3 ②'s **Switch to manual embeddings** option, after one went `Stale`. Either way, Path A's remaining steps are skipped and `quickstart_semantic` does not exist on `sample_mflix.movies`. Returning to Path A later means running Step 4a in full, explanation and AskUserQuestion confirmation included, because it builds a different index on a different collection. There is no shortened return route.
|
|
331
|
+
|
|
332
|
+
### Step 3a2 — Explain Manual Vector Search
|
|
333
|
+
|
|
334
|
+
Tell the user the sample dataset already includes an `embedded_movies` collection with pre-computed vectors on every document, so no embedding API is needed. With manual vector search the application generates embeddings with a model of its choice (OpenAI, Cohere, Hugging Face) and stores them as a document field; MongoDB only does the similarity search. Show the document shape:
|
|
335
|
+
|
|
336
|
+
```json
|
|
337
|
+
{
|
|
338
|
+
"title": "Toy Story",
|
|
339
|
+
"plot": "A cowboy doll is profoundly threatened...",
|
|
340
|
+
"plot_embedding": [0.0007, -0.0268, 0.0135, ...] // 1536 numbers
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Because the vectors are supplied rather than generated, the index definition must declare the dimension count and similarity metric itself — both are shown in Step 4a2 and must match the model that produced the vectors.
|
|
345
|
+
|
|
346
|
+
### Step 4a2 — Create Manual Vector Index
|
|
347
|
+
|
|
348
|
+
Apply the name check in **Creating an Index** rule 2 to `sample_mflix.embedded_movies`, then use `create-index` to create a vectorSearch index on it:
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"fields": [
|
|
353
|
+
{
|
|
354
|
+
"type": "vector",
|
|
355
|
+
"path": "plot_embedding",
|
|
356
|
+
"numDimensions": 1536,
|
|
357
|
+
"similarity": "cosine"
|
|
358
|
+
}
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Name the index: `quickstart_manual`, or the collision name rule 2 produced.
|
|
364
|
+
|
|
365
|
+
**While it builds, explain:**
|
|
366
|
+
> "The index organizes the embeddings that are already stored in your documents into a structure that makes similarity lookups fast."
|
|
367
|
+
|
|
368
|
+
Wait for status `READY` with `collection-indexes`.
|
|
369
|
+
|
|
370
|
+
### Step 5a2 — Run a "Find Similar Movies" Query
|
|
371
|
+
|
|
372
|
+
Explain to the user:
|
|
373
|
+
|
|
374
|
+
> "To query a manual vector search index, you need to pass a vector, which is an array of numbers representing the query terms. Normally your app generates this by sending the user's search text to an embedding model. Because we don't have an embedding API connected here, we take a movie we already know and find others with similar plots. This is exactly how recommendation systems work."
|
|
375
|
+
|
|
376
|
+
Use `find` on `sample_mflix.embedded_movies` to fetch the `plot_embedding` for **"Toy Story"**: filter `{"title": "Toy Story"}`, projection `{"plot_embedding": 1, "title": 1}`, limit 1. Use this movie rather than picking one, so the results below are reproducible. If it returns nothing, fall back to `{"title": "The Matrix"}`; if that also returns nothing, take the first document that has a `plot_embedding` field and tell the user which movie you used.
|
|
377
|
+
|
|
378
|
+
Then run:
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
db.embedded_movies.aggregate([
|
|
382
|
+
{
|
|
383
|
+
$vectorSearch: {
|
|
384
|
+
index: "quickstart_manual",
|
|
385
|
+
path: "plot_embedding",
|
|
386
|
+
queryVector: <the plot_embedding array fetched for "Toy Story" above>,
|
|
387
|
+
numCandidates: 100,
|
|
388
|
+
limit: 5
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
$project: {
|
|
393
|
+
_id: 0,
|
|
394
|
+
title: 1,
|
|
395
|
+
plot: 1,
|
|
396
|
+
score: { $meta: "vectorSearchScore" }
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
])
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Display results in a table: Title | Score | Plot snippet. Exclude the source movie from the display.
|
|
403
|
+
|
|
404
|
+
**Explain:**
|
|
405
|
+
> "MongoDB compared the query vector against every stored embedding and returned the most similar ones. The top result is the source movie itself (score ~1.0), so skip that one. The rest are movies whose plots are mathematically closest in meaning. No keyword matching. Instead, it's pure similarity between vectors."
|
|
406
|
+
|
|
407
|
+
> **In your own app:** instead of using an existing document's embedding as the query, you'd call your embedding model with the user's search text and pass the result as `queryVector`. The query structure is identical.
|
|
408
|
+
|
|
409
|
+
### Step 6a2 — Bridge to Keyword / Hybrid
|
|
410
|
+
|
|
411
|
+
Use AskUserQuestion:
|
|
412
|
+
> "Do you want to also see keyword search and hybrid, where both approaches run together? One thing to flag first: keyword search runs on the `movies` collection, and hybrid needs a semantic index on that same collection. The one we just built is on `embedded_movies`, so hybrid would mean creating a second semantic index on `movies` with Automated Embedding. I'll ask before creating it."
|
|
413
|
+
|
|
414
|
+
- **Yes** → proceed to Path B, then Path C. Path C's Path A2 branch handles that second index, with its own confirmation.
|
|
415
|
+
- **No, I'm done** → skip to Wrap Up
|
|
416
|
+
|
|
417
|
+
## Path B: Keyword Search (Atlas Search)
|
|
418
|
+
|
|
419
|
+
### Step 3b — Create Text Search Index
|
|
420
|
+
|
|
421
|
+
Ask before creating anything, with AskUserQuestion:
|
|
422
|
+
> "Keyword search needs a MongoDB Search index on the movies collection — a separate structure MongoDB builds alongside your data, like the index at the back of a book. It doesn't change your documents, it just makes specific fields fast to search. Want me to create it?"
|
|
423
|
+
|
|
424
|
+
- **Yes, create it** → continue below
|
|
425
|
+
- **No** → Path B and Path C both need this index, so skip to Wrap Up and do not re-offer it
|
|
426
|
+
|
|
427
|
+
This question is required on every route in, including Step 2's hybrid route, Step 8a's abbreviated Path B, and Step 7b. Those answers chose a path; none of them approved this index.
|
|
428
|
+
|
|
429
|
+
Then apply the name check in **Creating an Index** rule 2 to `sample_mflix.movies` and use `create-index` to create an Atlas Search index on it:
|
|
430
|
+
|
|
431
|
+
```json
|
|
432
|
+
{
|
|
433
|
+
"mappings": {
|
|
434
|
+
"dynamic": false,
|
|
435
|
+
"fields": {
|
|
436
|
+
"title": { "type": "string" },
|
|
437
|
+
"plot": { "type": "string" },
|
|
438
|
+
"genres": { "type": "token" }
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Name the index: `quickstart_text`, or the collision name rule 2 produced — that name then goes in every `$search` stage below and in Path C's keyword pipeline.
|
|
445
|
+
|
|
446
|
+
**Explain while it builds:**
|
|
447
|
+
> "We're indexing title and plot as searchable text, plus genres as an exact-match field you can filter on. It builds in the background and your documents stay exactly as they are."
|
|
448
|
+
|
|
449
|
+
The queries in this path search `title` and `plot` only. `genres` is indexed as `token` so exact-match filtering is available, mirroring the `filter` field in Path A's vector index. Do not claim the queries below search `genres`.
|
|
450
|
+
|
|
451
|
+
### Step 4b — First Keyword Search
|
|
452
|
+
|
|
453
|
+
Run with `aggregate`:
|
|
454
|
+
|
|
455
|
+
```javascript
|
|
456
|
+
db.movies.aggregate([
|
|
457
|
+
{
|
|
458
|
+
$search: {
|
|
459
|
+
index: "quickstart_text",
|
|
460
|
+
text: {
|
|
461
|
+
query: "space adventure",
|
|
462
|
+
path: ["title", "plot"]
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
$project: {
|
|
468
|
+
_id: 0,
|
|
469
|
+
title: 1,
|
|
470
|
+
plot: 1,
|
|
471
|
+
score: { $meta: "searchScore" }
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
{ $limit: 3 }
|
|
475
|
+
])
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Display results in a table: Title | Score | Plot snippet.
|
|
479
|
+
|
|
480
|
+
**Explain:**
|
|
481
|
+
> "MongoDB Search found movies where 'space' and 'adventure' appear across the title or plot, and ranked them by how relevant they are. The score reflects relevance, not just whether the word appeared."
|
|
482
|
+
|
|
483
|
+
Contrast it with `$vectorSearch`: `$search` takes a `text` operator and matches words, `$vectorSearch` takes `query` and matches meaning. `path` accepts an array, so one `text` operator covers several fields at once.
|
|
484
|
+
|
|
485
|
+
### Step 5b — Try Fuzzy Matching
|
|
486
|
+
|
|
487
|
+
Use AskUserQuestion:
|
|
488
|
+
> "Want to see what happens with a typo? Fuzzy matching is one of the most practical features for real search boxes."
|
|
489
|
+
|
|
490
|
+
If yes, run with intentional typos (`"sapce adventre"`):
|
|
491
|
+
|
|
492
|
+
```javascript
|
|
493
|
+
db.movies.aggregate([
|
|
494
|
+
{
|
|
495
|
+
$search: {
|
|
496
|
+
index: "quickstart_text",
|
|
497
|
+
text: {
|
|
498
|
+
query: "sapce adventre",
|
|
499
|
+
path: ["title", "plot"],
|
|
500
|
+
fuzzy: { maxEdits: 1 }
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
$project: { _id: 0, title: 1, score: { $meta: "searchScore" } }
|
|
506
|
+
},
|
|
507
|
+
{ $limit: 3 }
|
|
508
|
+
])
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
**Explain:**
|
|
512
|
+
> "Both typos were tolerated. MongoDB allowed up to 1 character difference between the query and indexed text. This is what makes a search box feel forgiving and human."
|
|
513
|
+
|
|
514
|
+
`fuzzy: { maxEdits: 1 }` is the only addition to the previous query — one character of difference per word. `maxEdits: 2` is more forgiving but too loose in practice: short words start matching things they shouldn't.
|
|
515
|
+
|
|
516
|
+
### Step 6b — Try Autocomplete
|
|
517
|
+
|
|
518
|
+
Use AskUserQuestion:
|
|
519
|
+
> "Want to see search-as-you-type? This powers the dropdown suggestions that appear while someone is still typing."
|
|
520
|
+
|
|
521
|
+
Autocomplete needs `title` indexed as an `autocomplete` type, which `quickstart_text` does not have yet. Two routes get there. **Work through this in order, before telling the user anything:**
|
|
522
|
+
|
|
523
|
+
1. **Does an autocomplete index already exist?** Check the `collection-indexes` output. If `quickstart_autocomplete` is there with `title` typed as `autocomplete`, create nothing, tell the user you're reusing it, and go straight to the query. If that name is taken by a different definition, do **not** drop it: say the name is in use and create `quickstart_autocomplete_2` instead. That name then replaces `quickstart_autocomplete` in the Route 2 pipeline and in the Wrap Up script's `AUTOCOMPLETE_INDEX_NAME` constant.
|
|
524
|
+
2. **Can you execute mongosh or driver code in this session?** If yes, Route 1. If the MongoDB MCP server is your only path to the cluster, Route 2. Atlas UI access does not qualify, because it means handing the step to the user to do by hand, and this walkthrough never does that when a tool can do it.
|
|
525
|
+
3. **Did Route 1 fail?** If `updateSearchIndex` is missing or errors for any reason, fall back to Route 2 and say why. Never drop `quickstart_text` to work around it.
|
|
526
|
+
|
|
527
|
+
When unsure, take Route 2. It works in every setup and leaves the index already serving queries untouched.
|
|
528
|
+
|
|
529
|
+
**Route 1 — edit the existing index.** Requires mongosh or a driver. `db.collection.updateSearchIndex(<name>, {<definition>})` updates an index in place: the old definition keeps serving queries while the new one builds, then swaps over. Pass the **complete** new definition (it replaces, not merges), with `title` as a multi-type field:
|
|
530
|
+
|
|
531
|
+
```json
|
|
532
|
+
"title": [
|
|
533
|
+
{ "type": "string" },
|
|
534
|
+
{ "type": "autocomplete", "tokenization": "edgeGram" }
|
|
535
|
+
]
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
**⚠️ Route 2 — create a second index.** The default, and the only option when the MongoDB MCP server is your path to the cluster: it exposes `create-index` and `drop-index` but **no update-index tool**, so `quickstart_text` cannot be edited in place from here. Don't attempt it and don't drop the working index. Create a dedicated index named `quickstart_autocomplete`:
|
|
539
|
+
|
|
540
|
+
```json
|
|
541
|
+
{
|
|
542
|
+
"mappings": {
|
|
543
|
+
"dynamic": false,
|
|
544
|
+
"fields": {
|
|
545
|
+
"title": { "type": "autocomplete", "tokenization": "edgeGram" }
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Then query it with `index: "quickstart_autocomplete"`, or the collision name from step 1, instead of `quickstart_text`. It counts against the tier index caps in Step 3a.3 ①. The Wrap Up script takes this second-index route too, so its `AUTOCOMPLETE_INDEX_NAME` constant is covered by the handoff rule in **Creating an Index** rule 2.
|
|
552
|
+
|
|
553
|
+
Tell the user which route you took and why, then run that route's query. **The index name is not interchangeable: using the wrong one returns an error, because only one of the two indexes has `title` typed as `autocomplete`.**
|
|
554
|
+
|
|
555
|
+
**Route 2 (second index):**
|
|
556
|
+
```javascript
|
|
557
|
+
db.movies.aggregate([
|
|
558
|
+
{
|
|
559
|
+
$search: {
|
|
560
|
+
index: "quickstart_autocomplete",
|
|
561
|
+
autocomplete: { query: "inc", path: "title" }
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
$project: { _id: 0, title: 1 }
|
|
566
|
+
},
|
|
567
|
+
{ $limit: 5 }
|
|
568
|
+
])
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
**Route 1 (updated index):** same pipeline with `index: "quickstart_text"`.
|
|
572
|
+
|
|
573
|
+
**Explain:**
|
|
574
|
+
> "Three characters returned relevant title suggestions instantly. Autocomplete pre-indexes word fragments, so it's purpose-built for speed and can respond as fast as a user types."
|
|
575
|
+
|
|
576
|
+
Note the operator change: `autocomplete` replaces `text` inside `$search`, `query` holds the partial input, and `path` must point at a field indexed as `autocomplete` type — this is the call a search box makes on every keystroke.
|
|
577
|
+
|
|
578
|
+
### Step 7b — Bridge to Hybrid
|
|
579
|
+
|
|
580
|
+
Use AskUserQuestion:
|
|
581
|
+
> "Keyword search finds exact matches. Want to see hybrid search, where MongoDB runs both keyword and semantic search at once and merges the results into one ranked list?"
|
|
582
|
+
|
|
583
|
+
- **Yes** → proceed to Path C
|
|
584
|
+
- **No, just the script** → skip to Wrap Up
|
|
585
|
+
|
|
586
|
+
## Path C: Hybrid Search
|
|
587
|
+
|
|
588
|
+
*Prerequisites: the `quickstart_text` index (from Path B) and the `quickstart_semantic` index (from Path A), both on `sample_mflix.movies`.*
|
|
589
|
+
|
|
590
|
+
**How users get here.** Step 2's hybrid route, Step 8a (from Path A), or Step 7b (from Path B). All three have already created or confirmed the two indexes, and nothing below changes based on which one it was.
|
|
591
|
+
|
|
592
|
+
Run `collection-indexes` on `sample_mflix.movies` to confirm both exist. If either is missing, don't create it implicitly: re-enter only the step that creates it (Step 3b for `quickstart_text`, Step 4a for `quickstart_semantic`) as a **create-only re-entry**:
|
|
593
|
+
|
|
594
|
+
- Run that step's explanation and its confirmation question again. That is the user's consent to build an index, so it is never skipped or assumed.
|
|
595
|
+
- Run nothing else from that path: no repeating queries or explanations the user already saw, no re-offering branches they declined.
|
|
596
|
+
- Come back here, re-run `collection-indexes`, and continue to Step 8c once both report `READY`.
|
|
597
|
+
|
|
598
|
+
If the user arrived through Path A2, they have `quickstart_manual` on `sample_mflix.embedded_movies`, a different index on a different collection that this pipeline can't use. Tell the user:
|
|
599
|
+
|
|
600
|
+
> "Hybrid search needs both pipelines pointed at the same collection, so the semantic index has to be on `movies` alongside the keyword index. Yours is on `embedded_movies` from earlier. I can create an Automated Embedding index on `movies` instead. That generates embeddings for about 21,000 plots and counts against your tier's search index cap. Want me to?"
|
|
601
|
+
|
|
602
|
+
If the user agrees, run Step 4a as a create-only re-entry under the rules above, then continue. If they decline, skip to Wrap Up and do not ask again.
|
|
603
|
+
|
|
604
|
+
### Step 8c — Run Hybrid Search
|
|
605
|
+
|
|
606
|
+
Run with `aggregate`:
|
|
607
|
+
|
|
608
|
+
**Two things to flag to the user before running it — both look like mistakes and aren't:**
|
|
609
|
+
|
|
610
|
+
1. **The two pipelines search for different text on purpose.** Semantic gets `"a story about growing up"` (a natural-language description of a *theme*), keyword gets `"coming of age"` (the literal *phrase* a plot would actually contain). Each pipeline is fed the query form it's good at. Say this out loud, or a beginner could read it as a typo.
|
|
611
|
+
2. **`limit: 20` inside each pipeline is the merge window**, and it is load-bearing. It caps how many documents `$rankFusion` evaluates from each pipeline — so cross-pipeline agreement is only visible *inside* that window. Set it to 5 and a document ranked #7 by keyword becomes invisible to the fusion, even if it's the best overall result. A reasonable default is 3–5× the number of final results you want.
|
|
612
|
+
|
|
613
|
+
```javascript
|
|
614
|
+
db.movies.aggregate([
|
|
615
|
+
{
|
|
616
|
+
$rankFusion: {
|
|
617
|
+
input: {
|
|
618
|
+
pipelines: {
|
|
619
|
+
semanticPipeline: [
|
|
620
|
+
{
|
|
621
|
+
$vectorSearch: {
|
|
622
|
+
index: "quickstart_semantic",
|
|
623
|
+
path: "plot",
|
|
624
|
+
query: "a story about growing up",
|
|
625
|
+
numCandidates: 100,
|
|
626
|
+
limit: 20
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
],
|
|
630
|
+
keywordPipeline: [
|
|
631
|
+
{
|
|
632
|
+
$search: {
|
|
633
|
+
index: "quickstart_text",
|
|
634
|
+
text: { query: "coming of age", path: "plot" }
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
{ $limit: 20 }
|
|
638
|
+
]
|
|
639
|
+
}
|
|
640
|
+
},
|
|
641
|
+
combination: {
|
|
642
|
+
weights: {
|
|
643
|
+
semanticPipeline: 0.7,
|
|
644
|
+
keywordPipeline: 0.3
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
$project: { _id: 0, title: 1, plot: 1 }
|
|
651
|
+
},
|
|
652
|
+
{ $limit: 5 }
|
|
653
|
+
])
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Display results.
|
|
657
|
+
|
|
658
|
+
**Explain:**
|
|
659
|
+
> "MongoDB ran two searches, one by meaning and one by keywords, then merged them using an algorithm called **Reciprocal Rank Fusion**. Movies that ranked highly in **both** searches scored highest overall. The weights (70% semantic, 30% keyword) control how much each signal matters. You can tune these per query type."
|
|
660
|
+
|
|
661
|
+
Two facts about the pipeline worth stating: the sub-pipelines run one after another rather than in parallel, with the ranked results merged at the end, and `$rankFusion` is not limited to two — any number of pipelines can be weighted to fit the use case.
|
|
662
|
+
|
|
663
|
+
### Step 9c — Try Different Weights (Optional)
|
|
664
|
+
|
|
665
|
+
Use AskUserQuestion:
|
|
666
|
+
> "Want to flip the weights and see how results change? For example, trust keywords more when users type exact titles."
|
|
667
|
+
|
|
668
|
+
If yes, re-run with `keywordPipeline: 0.7, semanticPipeline: 0.3` and show the diff in results.
|
|
669
|
+
|
|
670
|
+
**Explain:**
|
|
671
|
+
> "Different weights, different results. In a real product you might boost keyword search for navigational queries (user knows what they want) and boost semantic search for exploratory queries (user is browsing)."
|
|
672
|
+
|
|
673
|
+
## Wrap Up
|
|
674
|
+
|
|
675
|
+
Congratulate the user. Use AskUserQuestion:
|
|
676
|
+
> "Want a standalone Python script with everything you just ran: index creation, semantic search, keyword search, and hybrid search?"
|
|
677
|
+
|
|
678
|
+
**Check the index state before asking that.** Run `collection-indexes` on `sample_mflix.movies` and check for the semantic index name carried from Step 4a (for example, `quickstart_semantic` or `quickstart_semantic_2`). The script creates that named index there as its first action and embeds about 21,000 movie plots, which uses tokens.
|
|
679
|
+
|
|
680
|
+
- **The carried semantic index is present:** the script reproduces what the user already ran and agreed to. Ask the question above as written.
|
|
681
|
+
- **Absent:** the script would do embedding work the user never approved, so say so instead of asking the question above. Gate on this index, not on which path was taken: the gap covers Path A2 ending at Step 6a2, Path B ending at Step 7b, Path A stopping at Step 4a's confirmation, and any path cut short by an error.
|
|
682
|
+
> "I have a standalone Python script covering all three search types, but it goes further than what you just ran: it creates an Automated Embedding index on `movies` and embeds about 21,000 movie plots, which uses tokens. Want it anyway?"
|
|
683
|
+
|
|
684
|
+
If they decline, do not copy the script. Otherwise copy `scripts/quickstart_complete.py` from this skill directory into the user's working directory.
|
|
685
|
+
|
|
686
|
+
**Then carry over any collision names.** The script's `SEMANTIC_INDEX_NAME`, `TEXT_INDEX_NAME`, and `AUTOCOMPLETE_INDEX_NAME` constants default to the three `quickstart_*` names. For every index you created under a collision name, edit the matching constant in the copy before handing it over, and say which ones you changed. Skip this and the script stops on the conflicting index rather than reusing the one the user watched you build.
|
|
687
|
+
|
|
688
|
+
Then tell the user:
|
|
689
|
+
> "Your script is at `<destination path>`. Set `MONGODB_URI` to your connection string and it runs against `sample_mflix.movies`. It reads `MDB_MCP_CONNECTION_STRING` too if that is already exported for the MCP server. To point it at your own data, change the `DB_NAME` and `COLLECTION_NAME` variables at the top. Because the script hard-codes the sample schema (`plot`, `genres`, and `title`), also update the index definitions, the `$project` stages, and the query strings to match your own field names."
|
|
690
|
+
|
|
691
|
+
## Troubleshooting
|
|
692
|
+
|
|
693
|
+
**`sample_mflix` not found**
|
|
694
|
+
- Load it from Atlas UI: cluster → three-dot menu → Load Sample Dataset
|
|
695
|
+
- If there is no Load Sample Dataset option, the cluster is not on Atlas cloud and this walkthrough does not apply. Return to Step 0.2.
|
|
696
|
+
|
|
697
|
+
**Deployment turns out not to be Atlas cloud mid-walkthrough**
|
|
698
|
+
- Stop rather than improvise a substitute. The Atlas UI sample-data loader is unavailable, and `autoEmbed` index creation fails without the right image or MongoDB version, `mongot`, and Voyage AI keys.
|
|
699
|
+
- Give the user the two options in Step 0.2: move to an Atlas cloud cluster, or leave the walkthrough and build search on their own data through the main `SKILL.md` workflow with manual embeddings. Do not offer to start a local deployment — they are already running MongoDB somewhere.
|
|
700
|
+
|
|
701
|
+
**Index stuck in Building state**
|
|
702
|
+
- Normal for large collections — check status with `collection-indexes`
|
|
703
|
+
- autoEmbed initial sync on 21K movies typically takes 2–5 minutes
|
|
704
|
+
|
|
705
|
+
**`$rankFusion` error**
|
|
706
|
+
- Run `db-stats` to check the MongoDB version.
|
|
707
|
+
- If below 8.0, tell the user: "Hybrid search requires MongoDB 8.0 or later — your cluster is on [version]. You can upgrade in Atlas, or we can wrap up here with what you've already built." Then proceed to Wrap Up.
|
|
708
|
+
|
|
709
|
+
**autoEmbed index creation fails**
|
|
710
|
+
- Do **not** tell the user they need an M10+ dedicated cluster — autoEmbed is not restricted to dedicated tiers. Free and Flex clusters can use it.
|
|
711
|
+
- Most likely cause is the tier index cap: **3** total search/vector indexes on Free, **10** on Flex, counting indexes already on the collection. See Step 3a.3 ①.
|
|
712
|
+
- Check `autoEmbed` isn't sharing an index definition with a `vector` type field — the two cannot coexist in the same index, and MongoDB throws an exception if both are present.
|
|
713
|
+
- Confirm the field `type` and `modality` weren't being edited on an existing index — those two are immutable after creation (other settings, and adding/deleting `autoEmbed` and `filter` fields, are fine).
|
|
714
|
+
|
|
715
|
+
**autoEmbed index shows `Stale` status**
|
|
716
|
+
- This is a disk-space pause, not a failure. Embedding generation resumes on its own once space is freed.
|
|
717
|
+
- On dedicated (`M10+`) clusters, enable **storage auto-scaling**: Atlas → cluster → Edit Configuration → Storage. See Step 3a.3 ②.
|
|
718
|
+
|
|
719
|
+
**No results returned**
|
|
720
|
+
- Check index name matches exactly — typos return no results silently
|
|
721
|
+
- Check index status is READY, not Building
|
|
722
|
+
|
|
723
|
+
**`$vectorSearch` returns an error (not just empty results)**
|
|
724
|
+
- Run `collection-indexes` to confirm the index exists
|
|
725
|
+
- If missing, the index needs to be created — return to the relevant creation step
|