From c57c07758bf9b6aefc3d129ba908fb57c4fdf9dd Mon Sep 17 00:00:00 2001 From: Rocky Shi <175696301+rockyshi1993@users.noreply.github.com> Date: Wed, 28 Oct 2026 03:33:22 +0000 Subject: [PATCH 1/2] docs: correct vector search index guidance and navigation Replace the outdated claim that no vector-index lifecycle API exists, group Search and vector pages, publish the missing user-facing sidebar entries, and add a Search-capable vectorSearch example. Co-authored-by: rocky --- docs/en/api-index.md | 4 +- docs/en/capability-index.md | 1 + docs/en/capability-traceability.md | 3 +- docs/en/error-codes.md | 2 +- docs/en/examples.md | 6 +- docs/en/recipes.md | 6 + docs/en/search-indexes.md | 101 +++++++++++----- docs/en/upgrade-3.3.2.md | 2 + docs/en/vector-search.md | 10 +- docs/zh/api-index.md | 4 +- docs/zh/capability-index.md | 1 + docs/zh/capability-traceability.md | 3 +- docs/zh/error-codes.md | 2 +- docs/zh/examples.md | 6 +- docs/zh/recipes.md | 6 + docs/zh/search-indexes.md | 101 +++++++++++----- docs/zh/upgrade-3.3.2.md | 2 + docs/zh/vector-search.md | 10 +- examples/README.md | 11 +- examples/docs/vector-search.ts | 133 +++++++++++++++++++++ scripts/run-examples.cjs | 1 + scripts/validation/doc-example-matrix.json | 12 +- website/rspress.config.ts | 26 ++-- 23 files changed, 363 insertions(+), 90 deletions(-) create mode 100644 examples/docs/vector-search.ts diff --git a/docs/en/api-index.md b/docs/en/api-index.md index d00ccd03..467d7804 100644 --- a/docs/en/api-index.md +++ b/docs/en/api-index.md @@ -96,7 +96,7 @@ This index documents the current stable MongoDB adapter APIs and shared runtime |----------|--------|-------------| | [Aggregation pipeline](aggregate.md) | `aggregate()` | Aggregation pipeline query | | [Vector search](vector-search.md) | Collection / Model `vectorSearch()` | MongoDB Vector Search query with Model soft-delete visibility when applicable | -| [Search indexes](search-indexes.md) | Collection / Model `createSearchIndex()` and related methods | Create, inspect, update, drop, and wait for Search / Vector Search indexes | +| [Search indexes](search-indexes.md) | Collection / Model `createSearchIndex()` and related methods | Create, inspect, update, drop, and wait for Search indexes (`search` and `vectorSearch` types) | | [GridFS](gridfs.md) | `msq.gridfs()` / Model `gridfs()` | Upload, download, and resume GridFS files | --- @@ -174,7 +174,7 @@ This index documents the current stable MongoDB adapter APIs and shared runtime - [Aggregation pipeline](aggregate.md) - Aggregation pipelines - [Vector search](vector-search.md) - MongoDB Vector Search queries -- [Search indexes](search-indexes.md) - Search / Vector Search index lifecycle +- [Search indexes](search-indexes.md) - Search index lifecycle (`search` and `vectorSearch` types) - [GridFS](gridfs.md) - GridFS upload, download, and resumable upload - [Query plans](explain.md) - Query plans - [Chain queries](chaining-api.md) - Chain queries diff --git a/docs/en/capability-index.md b/docs/en/capability-index.md index 76625f6e..dfeaeff1 100644 --- a/docs/en/capability-index.md +++ b/docs/en/capability-index.md @@ -23,6 +23,7 @@ Principles: | Model | `msq.model()` / `MonSQLize.Model` | Supports model registration, relations, virtuals, populate, and common query entries | | write-path-policy | `writePathPolicy` | Optional runtime guard for Model-only write namespaces, including the `gridfs` category | | GridFS | `msq.gridfs()` / `model.gridfs()` | Upload, download, and phase-1 resumable GridFS upload. See [GridFS](./gridfs.md) | +| Search index and vector search | `createSearchIndex()` / `vectorSearch()` | Search index lifecycle for types `search` and `vectorSearch`, then vector queries. See [Search indexes](./search-indexes.md) and [vectorSearch()](./vector-search.md) | | cache | Query `cache` options / `msq.cache` | Database query cache, multi-layer cache access, invalidation, and statistics | | transaction | `startSession()` / `withTransaction()` | Supports MongoDB transaction wrappers and transaction statistics | | pool | `pools` / `pool()` / `ConnectionPoolManager` | Declare `pools: PoolConfig[]` in the constructor, route with `pool()`, and use the low-level manager for advanced inspection or manual management | diff --git a/docs/en/capability-traceability.md b/docs/en/capability-traceability.md index 0d6cdbc5..95512fe3 100644 --- a/docs/en/capability-traceability.md +++ b/docs/en/capability-traceability.md @@ -10,7 +10,8 @@ | Query (find / findOne / count / distinct) | `find.md` / `findOne.md` / `count.md` / `distinct.md` | `examples/docs/find.ts` / `find-one.ts` / `count.ts` / `distinct.ts` | `test/integration/mongodb/queries.test.ts` | | Paging / Chain query | `findPage.md` / `chaining-api.md` | `examples/docs/find-page.ts` / `chaining-api.ts` | `test/integration/model/model-features.test.ts` + `npm run test:examples` | | Aggregation / Expression | `aggregate.md` / `expression-functions.md` | `examples/docs/aggregate.ts` / `aggregate-advanced.ts` / `expression-functions.ts` | `test/unit/expression/*.test.ts` | -| Collection / Model vector search | `vector-search.md` | Documented API example; requires an external Vector Search deployment and index | `test/unit/runtime/vector-search.test.ts` / `test/unit/runtime/model-relations-p0.test.ts` | +| Collection / Model vector search | `vector-search.md` | `examples/docs/vector-search.ts` (Atlas, atlas-local, or Community 8.2+ with mongot; skips when no Search URI is set) | `test/unit/runtime/vector-search.test.ts` / `test/unit/runtime/model-relations-p0.test.ts` | +| Search index lifecycle (`search` and `vectorSearch`) | `search-indexes.md` | `examples/docs/vector-search.ts` | `test/unit/search-indexes/` / `test/integration/search-indexes/` / `test/types/search-index-usage.test-d.ts` | | Write extension / Batch | `write-operations.md` / `updateBatch.md` / `deleteBatch.md` | `examples/docs/update.ts` / `batch-operations.ts` | `test/unit/writes/batch.test.ts` + `test/integration/mongodb/writes-batch.test.ts` | | Collection management capabilities | `collection-management.md` / `create-index.md` / `database-ops.md` | `examples/docs/collection-management.ts` | `test/integration/mongodb/management.test.ts` | | Production data tasks / index rollout | `production-data-migration.md` / `data-tasks.md` / `production-rollout.md` | `examples/docs/data-tasks.ts` | `test/unit/data-tasks/data-task-job-*.test.ts` / `test/integration/data-tasks/data-task-job-facade.test.ts` / `test/smoke/data-task-cli.test.ts` | diff --git a/docs/en/error-codes.md b/docs/en/error-codes.md index 1830de45..be14fa92 100644 --- a/docs/en/error-codes.md +++ b/docs/en/error-codes.md @@ -65,7 +65,7 @@ See [Data Tasks](./data-tasks.md) for the preview, approval, apply, backup, and | `POOL_NOT_FOUND` | The specified connection pool does not exist or the health checker cannot find the pool | Whether the pool name is registered, whether the case is consistent, and whether `pools` is passed in | | `INVALID_OPERATION` | The query is executed repeatedly, all connection pools are unavailable, and the transaction status is not allowed | Whether to repeat `toArray()`, pool health status, transaction life cycle | | `INVALID_ARGUMENT` | Query chain parameters, collection management parameters, function cache parameters | Parameter type, value range, non-empty string/object/array | -| `INVALID_VECTOR_SEARCH` | Local Vector Search option validation | Vector index/path, finite query vector, ANN/ENN combination, filter/projection shape | +| `INVALID_VECTOR_SEARCH` | Local Vector Search option validation | index name and path, finite query vector, ANN/ENN combination, filter/projection shape | | `INVALID_SEARCH_INDEX_DEFINITION` | Local Search index validation | Index name/type/definition, vectorSearch fields, option values | | `SEARCH_INDEX_EXISTS` | Search index name already exists | `ifExists` option, duplicate names in `createSearchIndexes` | | `SEARCH_INDEX_NOT_FOUND` | Search index target is missing | Index name, collection, `ignoreMissing` | diff --git a/docs/en/examples.md b/docs/en/examples.md index 58a4de63..2281d16e 100644 --- a/docs/en/examples.md +++ b/docs/en/examples.md @@ -2,7 +2,7 @@ Every core API in the documentation site should map, as much as possible, to an example that can run directly inside the current repository. -> See the [official examples index](https://github.com/devcodex-labs/monSQLize/blob/main/examples/README.md) for the full source list. `npm run test:examples` compiles and executes the current **58** TypeScript examples. [`examples/helpers/bootstrap.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/helpers/bootstrap.ts) is a helper module and is not executed on its own. +> See the [official examples index](https://github.com/devcodex-labs/monSQLize/blob/main/examples/README.md) for the full source list. `npm run test:examples` compiles and executes the current **59** TypeScript examples. [`examples/helpers/bootstrap.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/helpers/bootstrap.ts) is a helper module and is not executed on its own. [`examples/docs/vector-search.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/vector-search.ts) exits without calling Search when `MONSQLIZE_VECTOR_SEARCH_URI` and `MONSQLIZE_ATLAS_LOCAL_URI` are unset. ## How to Run @@ -34,12 +34,16 @@ node .generated/examples-dist/examples/docs/find.js |----------|---------| | [`find.md`](./find.md) | [`examples/docs/find.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find.ts) | | [`findOne.md`](./findOne.md) | [`examples/docs/find-one.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find-one.ts) | +| [`find-one-by-id.md`](./find-one-by-id.md) | [`examples/docs/find-one-by-id.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find-one-by-id.ts) | +| [`find-by-ids.md`](./find-by-ids.md) | [`examples/docs/find-by-ids.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find-by-ids.ts) | | [`findPage.md`](./findPage.md) | [`examples/docs/find-page.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find-page.ts) | | [`find-and-count.md`](./find-and-count.md) | [`examples/docs/find-and-count.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find-and-count.ts) | | [`count.md`](./count.md) | [`examples/docs/count.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/count.ts) | | [`count-queue.md`](./count-queue.md) | [`examples/docs/count-queue.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/count-queue.ts) | | [`distinct.md`](./distinct.md) | [`examples/docs/distinct.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/distinct.ts) | | [`aggregate.md`](./aggregate.md) | [`examples/docs/aggregate.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/aggregate.ts) | +| [`vector-search.md`](./vector-search.md) | [`examples/docs/vector-search.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/vector-search.ts) | +| [`search-indexes.md`](./search-indexes.md) | [`examples/docs/vector-search.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/vector-search.ts) | | [`explain.md`](./explain.md) | [`examples/docs/explain.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/explain.ts) | | [`chaining-api.md`](./chaining-api.md) | [`examples/docs/chaining-api.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/chaining-api.ts) | diff --git a/docs/en/recipes.md b/docs/en/recipes.md index 4dcc766f..da3004bb 100644 --- a/docs/en/recipes.md +++ b/docs/en/recipes.md @@ -197,6 +197,12 @@ Model schema callbacks use the isolated `schema-dsl/runtime` owned by the `MonSQ Example source: [`examples/docs/model.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/model.ts) +## Query with vectorSearch + +`vectorSearch()` queries a Search index of type `vectorSearch`. Create and wait for that index with the [Search index lifecycle API](./search-indexes.md), then run the query described in [`vectorSearch()`](./vector-search.md). + +The runnable example is [`examples/docs/vector-search.ts`](../../examples/docs/vector-search.ts). It needs MongoDB Atlas, `mongodb/mongodb-atlas-local`, or Community/Enterprise 8.2+ with `mongot`. Set `MONSQLIZE_VECTOR_SEARCH_URI` or `MONSQLIZE_ATLAS_LOCAL_URI`. `mongodb-memory-server` cannot run the Search calls. When those variables are unset, the example exits without calling Search. + ## Troubleshoot by Error Code ```ts diff --git a/docs/en/search-indexes.md b/docs/en/search-indexes.md index 1f70794e..13a04e19 100644 --- a/docs/en/search-indexes.md +++ b/docs/en/search-indexes.md @@ -1,6 +1,6 @@ # Search Index Lifecycle -Since v3.4.0, collection accessors and Models can create, inspect, update, drop, and wait for MongoDB Search indexes. One API covers full-text `search` indexes and `vectorSearch` indexes; use [`vectorSearch()`](vector-search.md) to query a Vector Search index. +Since v3.4.0, collection accessors and Models can create, inspect, update, drop, and wait for MongoDB Search indexes. A **Search index** has type `search` (full text) or `vectorSearch`. Use [`vectorSearch()`](vector-search.md) to query a Search index of type `vectorSearch`. ## Requirements @@ -53,41 +53,82 @@ interface SearchIndexInfo { | Method | Behavior | |--------|----------| -| `createSearchIndex` | Validates locally, then checks for an index with the same name. With the default `ifExists: 'error'` an existing index rejects with `SEARCH_INDEX_EXISTS`. `'skip'` leaves it unchanged (`action: 'skipped'`). `'update'` replaces the definition (`action: 'updated'`), unless the type differs, which rejects with `INVALID_SEARCH_INDEX_DEFINITION` (drop the index first). | -| `createSearchIndexes` | Validates every spec before sending one command. It has no `ifExists` option; duplicate names reject with `SEARCH_INDEX_EXISTS`. | -| `listSearchIndexes` | Returns normalized entries. `type` filters by the resolved type. | -| `getSearchIndex` | A missing index resolves to `status: 'DOES_NOT_EXIST'`; it does not throw. | -| `updateSearchIndex` | Replaces the definition and the server rebuilds the index. While it rebuilds, the previous version can keep serving queries. The reported `queryable` flag varies: Community 8.2.12 kept it `true`, while atlas-local 8.3.13 reported `false` during a `search` rebuild even though `$search` still returned results (both measured). A missing index rejects with `SEARCH_INDEX_NOT_FOUND`. | -| `dropSearchIndex` | A missing index rejects with `SEARCH_INDEX_NOT_FOUND` unless `ignoreMissing: true`. | -| `waitForSearchIndex` | Polls once per `intervalMs`. See the table below. | - -> **Updating a `vectorSearch` index depends on the deployment.** -> -> | Deployment | In-place `updateSearchIndex` on `vectorSearch` | -> |---|---| -> | MongoDB Atlas | Supported according to the MongoDB documentation; the old index keeps serving queries while the new one builds. Not tested by monSQLize. | -> | `mongodb/mongodb-atlas-local` (mongod 8.3.13) | Rejected by the server: `BadValue` (2) `"mappings" is required`. Measured. | -> | Community 8.2.12 + `mongodb/mongodb-community-search` | Accepted. During the rebuild (about 30 s for 20,000 documents × 64 dimensions) `$vectorSearch` kept returning results. Measured. | -> -> On every measured deployment the server rejects a `type` field in the `updateSearchIndex` command (`IDLUnknownField`), so sending one does not help. monSQLize passes a rejection through as `MONGODB_ERROR` with `serverCode` and never falls back on its own. -> -> Where an in-place update is not available, change the definition without downtime like this: create an index under a new name, `waitForSearchIndex(newName, { until: 'queryable' })`, switch your queries to the new name, then drop the old index. Both indexes exist for a while, which uses extra resources and counts against index limits (Atlas Free tier: 3 Search indexes, Flex: 10). If a short outage is acceptable, you can instead drop the index, wait for `dropped`, and create it again. Searches fail until it is queryable again, and that gap grows with data size (measured on atlas-local: about 1–2 s for 50,000 documents × 64 dimensions). +| `createSearchIndex` | Validates the spec, then applies `ifExists`. | +| `createSearchIndexes` | Validates every spec, then sends one command. | +| `listSearchIndexes` | Returns normalized entries. Optional `type` filter. | +| `getSearchIndex` | Missing name returns `DOES_NOT_EXIST`. | +| `updateSearchIndex` | Replaces the definition. The server rebuilds it. | +| `dropSearchIndex` | Drops the index, or returns when `ignoreMissing` is set. | +| `waitForSearchIndex` | Polls until the requested condition, timeout, or abort. | + +### createSearchIndex + +The call validates locally, then looks for an index with the same name. + +- Default `ifExists: 'error'` rejects an existing index with `SEARCH_INDEX_EXISTS`. +- `'skip'` leaves the existing index unchanged and returns `action: 'skipped'`. +- `'update'` replaces the definition and returns `action: 'updated'`. +- A type change rejects with `INVALID_SEARCH_INDEX_DEFINITION`. Drop the index first. + +### createSearchIndexes + +Every spec is validated before one command is sent. There is no `ifExists` option. Duplicate names reject with `SEARCH_INDEX_EXISTS`. + +### listSearchIndexes and getSearchIndex + +`listSearchIndexes` returns normalized entries. `type` filters by the resolved type. Entries with `typeSource: 'unknown'` never match that filter. + +`getSearchIndex` resolves a missing index to `status: 'DOES_NOT_EXIST'` and does not throw. + +### updateSearchIndex + +The call replaces the definition. The server rebuilds the index asynchronously. While it rebuilds, the previous version can keep serving queries. + +The reported `queryable` flag is not the same on every deployment. Community 8.2.12 kept it `true`. atlas-local 8.3.13 reported `false` during a `search` rebuild even though `$search` still returned results. Both results were measured. A missing index rejects with `SEARCH_INDEX_NOT_FOUND`. + +In-place `updateSearchIndex` on a Search index of type `vectorSearch` depends on the deployment: + +| Deployment | In-place update | +|------------|-----------------| +| MongoDB Atlas | Documented as supported. Not measured by monSQLize. | +| atlas-local 8.3.13 | Rejected. Server `BadValue` (2). Measured. | +| Community 8.2.12 + community-search | Accepted. Queries continued during rebuild. Measured. | + +MongoDB Atlas documentation says the previous index keeps serving queries while the new definition builds. monSQLize has not measured that path. + +`mongodb/mongodb-atlas-local` (mongod 8.3.13) rejects the update with `BadValue` (2) and the message `"mappings" is required`. + +Community 8.2.12 with `mongodb/mongodb-community-search` accepts the update. During a measured rebuild of about 30 seconds for 20,000 documents with 64 dimensions, `$vectorSearch` kept returning results. + +On every measured deployment the server rejects a `type` field on the `updateSearchIndex` command (`IDLUnknownField`). Sending one does not help. monSQLize passes that rejection through as `MONGODB_ERROR` with `serverCode` and does not fall back on its own. + +When an in-place update is not available, change the definition without downtime: create an index under a new name, call `waitForSearchIndex(newName, { until: 'queryable' })`, switch queries to the new name, then drop the old index. Both indexes exist for a while. That uses extra resources and counts against index limits (Atlas Free tier: 3 Search indexes, Flex: 10). + +If a short outage is acceptable, drop the index, wait for `dropped`, and create it again. Searches fail until it is queryable again. The gap grows with data size. On atlas-local, 50,000 documents with 64 dimensions took about 1–2 seconds. `status` passes the server value through unchanged. Use `queryable` to decide whether searches can run and `status` to show progress. +### dropSearchIndex + +A missing index rejects with `SEARCH_INDEX_NOT_FOUND` unless `ignoreMissing: true`. + ### Index type -The type comes from the server's top-level `type` field (`typeSource: 'server'`). Self-managed `mongot` does not return that field ([MongoDB limitations](https://www.mongodb.com/docs/search/self-managed/current/limitations/)). In that case the type is inferred from `latestDefinition` (`typeSource: 'inferred'`): a `fields` array with a `type: 'vector'` entry means `vectorSearch`, and a `mappings` key means `search`. Otherwise `type` is omitted and `typeSource` is `'unknown'`; such entries never match a `listSearchIndexes({ type })` filter. +The type comes from the server's top-level `type` field (`typeSource: 'server'`). Self-managed `mongot` does not return that field ([MongoDB limitations](https://www.mongodb.com/docs/search/self-managed/current/limitations/)). In that case the type is inferred from `latestDefinition` (`typeSource: 'inferred'`): a `fields` array with a `type: 'vector'` entry means `vectorSearch`, and a `mappings` key means `search`. Otherwise `type` is omitted and `typeSource` is `'unknown'`. ### waitForSearchIndex | `until` | Resolves when | `FAILED` | Missing index | |---------|---------------|----------|---------------| -| `queryable` | `queryable === true` | Rejects with `SEARCH_INDEX_BUILD_FAILED` only when not queryable | Keeps waiting for up to 30 seconds, then rejects with `SEARCH_INDEX_NOT_FOUND` | -| `ready` | `status === 'READY'` | Rejects with `SEARCH_INDEX_BUILD_FAILED` | Same as above | -| `dropped` | `status === 'DOES_NOT_EXIST'` | Keeps waiting | Resolves | +| `queryable` | `queryable` is true | Rejects if not queryable | 30s grace, then not found | +| `ready` | `status` is `READY` | Rejects | Same 30s grace | +| `dropped` | `status` is `DOES_NOT_EXIST` | Keeps waiting | Resolves | + +For `until: 'queryable'`, `FAILED` rejects with `SEARCH_INDEX_BUILD_FAILED` only when the index is not queryable. For `until: 'ready'`, `FAILED` rejects with `SEARCH_INDEX_BUILD_FAILED`. For `until: 'dropped'`, `FAILED` keeps waiting. -Timeouts reject with `SEARCH_INDEX_WAIT_TIMEOUT`, and `details[0].last` holds the last observed status. Aborting `signal` rejects with `signal.reason` unchanged. +A missing index on `queryable` or `ready` keeps waiting for up to 30 seconds, then rejects with `SEARCH_INDEX_NOT_FOUND`. A missing index on `dropped` resolves immediately. + +The method polls once per `intervalMs`. Timeouts reject with `SEARCH_INDEX_WAIT_TIMEOUT`, and `details[0].last` holds the last observed status. Aborting `signal` rejects with `signal.reason` unchanged. ## writePathPolicy @@ -97,13 +138,15 @@ Timeouts reject with `SEARCH_INDEX_WAIT_TIMEOUT`, and `details[0].last` holds th | Code | When | |------|------| -| `INVALID_SEARCH_INDEX_DEFINITION` | Local validation failed (name, type, definition, options), or `ifExists: 'update'` targets an index of another type | -| `SEARCH_INDEX_EXISTS` | Same-name index exists with `ifExists: 'error'`, or duplicate names in `createSearchIndexes` | -| `SEARCH_INDEX_NOT_FOUND` | `updateSearchIndex` / `dropSearchIndex` target is missing; `waitForSearchIndex` saw no index after the grace period | +| `INVALID_SEARCH_INDEX_DEFINITION` | Local validation failed, or `update` changes type | +| `SEARCH_INDEX_EXISTS` | Same name exists, or a batch repeats a name | +| `SEARCH_INDEX_NOT_FOUND` | Update, drop, or wait target is missing | | `SEARCH_INDEX_UNSUPPORTED` | The deployment does not support Search index commands | | `SEARCH_INDEX_BUILD_FAILED` | `waitForSearchIndex` observed `FAILED` | | `SEARCH_INDEX_WAIT_TIMEOUT` | `waitForSearchIndex` exceeded `timeoutMs` | +Local validation covers the name, type, definition, and options. `ifExists: 'update'` also rejects a type change. `SEARCH_INDEX_NOT_FOUND` includes a `waitForSearchIndex` miss after the grace period. + Every error raised from a server response keeps the driver error as `cause`. Server errors without a dedicated code reject with `MONGODB_ERROR`; `details[0]` carries `serverCode`, `serverCodeName`, and `serverMessage`, and `error.monsqlize = { operation, namespace, indexName }` is attached. See [Error codes](error-codes.md). ## Examples @@ -130,3 +173,5 @@ console.log(info.status, info.queryable); await docs.dropSearchIndex('text_idx', { ignoreMissing: true }); await docs.waitForSearchIndex('text_idx', { until: 'dropped' }); ``` + +Query the `vectorSearch` index with [`vectorSearch()`](vector-search.md). A runnable create, wait, and query walkthrough is [`examples/docs/vector-search.ts`](../../examples/docs/vector-search.ts). It needs MongoDB Atlas, `mongodb/mongodb-atlas-local`, or Community/Enterprise 8.2+ with `mongot`, via `MONSQLIZE_VECTOR_SEARCH_URI` or `MONSQLIZE_ATLAS_LOCAL_URI`. diff --git a/docs/en/upgrade-3.3.2.md b/docs/en/upgrade-3.3.2.md index bd823c2d..404329c4 100644 --- a/docs/en/upgrade-3.3.2.md +++ b/docs/en/upgrade-3.3.2.md @@ -1,5 +1,7 @@ # Upgrade from 3.3.1 to 3.3.2 +> **Later releases.** 3.4.0 adds the [Search index lifecycle API](./search-indexes.md) and 3.5.0 adds [GridFS](./gridfs.md). Those releases add capabilities and do not have a separate migration guide. This page covers only the 3.3.1 to 3.3.2 upgrade. + Use this guide to upgrade an application from **3.3.1 to 3.3.2**. Treat an untagged checkout as a release candidate. Check `npm view monsqlize@3.3.2 version --registry=https://registry.npmjs.org/` and the immutable tag before installing that exact version; until it exists, qualify the candidate's packed artifact in staging. Preparing a candidate does not publish npm or update the stable site. The patch corrects BSON query/cache identity, Model cache invalidation, pagination, transactions, sync and resource cleanup. It retains public package entry points, Node.js `>=18`, direct runtime dependencies and build formats. Automatic cache invalidation remains disabled by default. diff --git a/docs/en/vector-search.md b/docs/en/vector-search.md index 246e704b..9923e17c 100644 --- a/docs/en/vector-search.md +++ b/docs/en/vector-search.md @@ -4,9 +4,9 @@ ## Prerequisites -This API requires a MongoDB deployment that supports Vector Search and a Vector Search index that already exists. Since v3.4.0 you can manage the index with the [Search index lifecycle API](search-indexes.md). Configure the index and make its vector dimensions, numeric representation, and indexed path match the embeddings you send to the API. See the [MongoDB `$vectorSearch` reference](https://www.mongodb.com/docs/vector-search/query/aggregation-stages/vector-search-stage/) for deployment and index requirements. +This API requires a MongoDB deployment that supports Vector Search and a Search index of type `vectorSearch` that already exists. A **Search index** is a MongoDB Search index whose type is full-text `search` or `vectorSearch`. Since v3.4.0 you can manage that index with the [Search index lifecycle API](search-indexes.md). Configure the index and make its vector dimensions, numeric representation, and indexed path match the embeddings you send to the API. See the [MongoDB `$vectorSearch` reference](https://www.mongodb.com/docs/vector-search/query/aggregation-stages/vector-search-stage/) for deployment and index requirements. -`$vectorSearch` must be the first aggregation stage. It is also not supported inside a `$lookup` sub-pipeline or `$facet`, so use this collection-level API as the search entry point and enrich the resulting documents afterwards when needed. +`$vectorSearch` must be the first stage of any pipeline it appears in. Current MongoDB documentation still does not allow it inside a `$lookup` sub-pipeline or a `$facet` stage ([manual](https://www.mongodb.com/docs/manual/reference/operator/aggregation/vectorsearch/), [Vector Search stage](https://www.mongodb.com/docs/vector-search/query/aggregation-stages/vector-search-stage/)). Use `vectorSearch()` as the search entry point and enrich the resulting documents afterwards when needed. ## API @@ -78,7 +78,7 @@ for (const { document, score } of hits) { } ``` -`filter` is a Vector Search pre-filter, not a later `$match` stage. Create the relevant filter fields in the Vector Search index before relying on them. +`filter` is a Vector Search pre-filter, not a later `$match` stage. Create the relevant filter fields on the Search index (`vectorSearch` type) before relying on them. ## Exact nearest-neighbor search (ENN) @@ -129,10 +129,10 @@ Invalid vector options throw `INVALID_VECTOR_SEARCH`, with field/reason details. Deployment, index, and other MongoDB execution failures are rethrown with their original driver code and message. When the error object is writable, monSQLize adds `error.monsqlize.operation === 'vectorSearch'` and the target namespace for diagnosis. -This release exposes vector search on both `Collection` and `Model`; it does not add a vector-index lifecycle API. +Index management for a Search index (including type `vectorSearch`) is on the [Search index lifecycle](search-indexes.md) page: `createSearchIndex`, `updateSearchIndex`, `dropSearchIndex`, `listSearchIndexes`, and `waitForSearchIndex`. ## Operational notes -- A default local `mongodb-memory-server` instance does not provide Vector Search. Unit coverage verifies pipeline construction and validation; end-to-end verification needs a compatible externally provisioned deployment and index. +- A default local `mongodb-memory-server` instance does not provide Vector Search. Unit coverage verifies pipeline construction and validation. End-to-end verification needs Atlas, `mongodb/mongodb-atlas-local`, or Community/Enterprise 8.2+ with `mongot`. The runnable walkthrough is [`examples/docs/vector-search.ts`](../../examples/docs/vector-search.ts). Set `MONSQLIZE_VECTOR_SEARCH_URI` or `MONSQLIZE_ATLAS_LOCAL_URI`. When neither is set, that example exits without calling Search so `npm run test:examples` can still invoke it. - Vector Search ranking is produced by MongoDB. monSQLize returns that score and does not re-rank results locally. - Keep the embedding model, vector dimensions, and index configuration synchronized across writers and readers. diff --git a/docs/zh/api-index.md b/docs/zh/api-index.md index b70c1119..6880e675 100644 --- a/docs/zh/api-index.md +++ b/docs/zh/api-index.md @@ -96,7 +96,7 @@ |------|------|------| | [聚合管道查询](aggregate.md) | `aggregate()` | 聚合管道查询 | | [向量查询](vector-search.md) | Collection / Model `vectorSearch()` | MongoDB Vector Search 向量查询;Model 会应用软删除可见性 | -| [Search 索引](search-indexes.md) | Collection / Model `createSearchIndex()` 等 | 创建、查看、更新、删除并等待 Search / Vector Search 索引 | +| [Search 索引](search-indexes.md) | Collection / Model `createSearchIndex()` 等 | 创建、查看、更新、删除并等待 Search 索引(含 `vectorSearch` 类型) | | [GridFS](gridfs.md) | `msq.gridfs()` / Model `gridfs()` | 上传、下载和恢复 GridFS 文件 | --- @@ -172,7 +172,7 @@ - [聚合管道查询](aggregate.md) - 聚合管道 - [向量查询](vector-search.md) - MongoDB Vector Search 向量查询 -- [Search 索引](search-indexes.md) - Search / Vector Search 索引生命周期 +- [Search 索引](search-indexes.md) - Search 索引生命周期(含 `vectorSearch` 类型) - [GridFS](gridfs.md) - GridFS 上传、下载与可恢复上传 - [查询计划分析](explain.md) - 查询计划 - [链式调用](chaining-api.md) - 链式调用 diff --git a/docs/zh/capability-index.md b/docs/zh/capability-index.md index e219fd83..7e0a7a13 100644 --- a/docs/zh/capability-index.md +++ b/docs/zh/capability-index.md @@ -22,6 +22,7 @@ monSQLize 是数据库原生的生产数据运行时增强层。当前稳定适 | Model | `msq.model()` / `MonSQLize.Model` | 支持 Model 注册、关系、虚拟字段、populate 与常用查询入口 | | write-path-policy | `writePathPolicy` | 可选的 Model-only 写入命名空间运行时护栏,包含 `gridfs` 类别 | | GridFS | `msq.gridfs()` / `model.gridfs()` | 上传、下载和第一阶段可恢复 GridFS 上传。见 [GridFS](./gridfs.md) | +| Search 索引与向量查询 | `createSearchIndex()` / `vectorSearch()` | Search 索引生命周期(类型 `search` 与 `vectorSearch`),以及向量查询。见 [Search 索引](./search-indexes.md) 与 [vectorSearch()](./vector-search.md) | | cache | 查询 `cache` 选项 / `msq.cache` | 数据库查询缓存、多层缓存接入、缓存失效与统计 | | transaction | `startSession()` / `withTransaction()` | 支持 MongoDB 事务封装与事务统计 | | pool | `pools` / `pool()` / `ConnectionPoolManager` | 在构造函数中声明 `pools: PoolConfig[]`,通过 `pool()` 路由,底层管理器用于进阶状态查看与手动管理 | diff --git a/docs/zh/capability-traceability.md b/docs/zh/capability-traceability.md index 1d691903..f20f446a 100644 --- a/docs/zh/capability-traceability.md +++ b/docs/zh/capability-traceability.md @@ -10,7 +10,8 @@ | Query(find / findOne / count / distinct) | `find.md` / `findOne.md` / `count.md` / `distinct.md` | `examples/docs/find.ts` / `find-one.ts` / `count.ts` / `distinct.ts` | `test/integration/mongodb/queries.test.ts` | | 分页 / 链式查询 | `findPage.md` / `chaining-api.md` | `examples/docs/find-page.ts` / `chaining-api.ts` | `test/integration/model/model-features.test.ts` + `npm run test:examples` | | 聚合 / 表达式 | `aggregate.md` / `expression-functions.md` | `examples/docs/aggregate.ts` / `aggregate-advanced.ts` / `expression-functions.ts` | `test/unit/expression/*.test.ts` | -| Collection / Model 向量查询 | `vector-search.md` | 文档 API 示例;需要外部 Vector Search 部署和索引 | `test/unit/runtime/vector-search.test.ts` / `test/unit/runtime/model-relations-p0.test.ts` | +| Collection / Model 向量查询 | `vector-search.md` | `examples/docs/vector-search.ts`(Atlas、atlas-local,或带 mongot 的 Community 8.2+;未设置 Search URI 时跳过) | `test/unit/runtime/vector-search.test.ts` / `test/unit/runtime/model-relations-p0.test.ts` | +| Search 索引生命周期(`search` 与 `vectorSearch`) | `search-indexes.md` | `examples/docs/vector-search.ts` | `test/unit/search-indexes/` / `test/integration/search-indexes/` / `test/types/search-index-usage.test-d.ts` | | 写入扩展 / Batch | `write-operations.md` / `updateBatch.md` / `deleteBatch.md` | `examples/docs/update.ts` / `batch-operations.ts` | `test/unit/writes/batch.test.ts` + `test/integration/mongodb/writes-batch.test.ts` | | Collection 管理能力 | `collection-management.md` / `create-index.md` / `database-ops.md` | `examples/docs/collection-management.ts` | `test/integration/mongodb/management.test.ts` | | 生产数据任务 / 索引发布 | `production-data-migration.md` / `data-tasks.md` / `production-rollout.md` | `examples/docs/data-tasks.ts` | `test/unit/data-tasks/data-task-job-*.test.ts` / `test/integration/data-tasks/data-task-job-facade.test.ts` / `test/smoke/data-task-cli.test.ts` | diff --git a/docs/zh/error-codes.md b/docs/zh/error-codes.md index 830c80cb..61507e84 100644 --- a/docs/zh/error-codes.md +++ b/docs/zh/error-codes.md @@ -62,7 +62,7 @@ try { | `POOL_NOT_FOUND` | 指定连接池不存在或 health checker 找不到池 | 池名称是否注册、大小写是否一致、`pools` 是否传入 | | `INVALID_OPERATION` | 查询重复执行、连接池全部不可用、事务状态不允许 | 是否重复 `toArray()`,池健康状态,事务生命周期 | | `INVALID_ARGUMENT` | 查询链参数、集合管理参数、函数缓存参数 | 参数类型、取值范围、非空字符串/对象/数组 | -| `INVALID_VECTOR_SEARCH` | 本地 Vector Search 参数校验 | 向量索引/路径、有限 query vector、ANN/ENN 组合、filter/projection 形态 | +| `INVALID_VECTOR_SEARCH` | 本地 Vector Search 参数校验 | index 名称与 path、有限 query vector、ANN/ENN 组合、filter/projection 形态 | | `INVALID_SEARCH_INDEX_DEFINITION` | Search 索引本地校验失败 | 索引名/类型/定义、vectorSearch fields、选项取值 | | `SEARCH_INDEX_EXISTS` | Search 索引同名已存在 | `ifExists` 选项、`createSearchIndexes` 中的重复名称 | | `SEARCH_INDEX_NOT_FOUND` | Search 索引不存在 | 索引名、集合、`ignoreMissing` | diff --git a/docs/zh/examples.md b/docs/zh/examples.md index aa4ac005..c97f2586 100644 --- a/docs/zh/examples.md +++ b/docs/zh/examples.md @@ -2,7 +2,7 @@ 文档站中的每个核心 API,都应尽量对应到**当前仓库内可直接运行的示例**。 -> 当前完整示例源列表见 [官方示例总览](https://github.com/devcodex-labs/monSQLize/blob/main/examples/README.md),其中 `npm run test:examples` 会统一编译并执行当前 **58 个** TypeScript 示例;[`examples/helpers/bootstrap.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/helpers/bootstrap.ts) 是辅助模块,不单独执行。 +> 当前完整示例源列表见 [官方示例总览](https://github.com/devcodex-labs/monSQLize/blob/main/examples/README.md),其中 `npm run test:examples` 会统一编译并执行当前 **59 个** TypeScript 示例;[`examples/helpers/bootstrap.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/helpers/bootstrap.ts) 是辅助模块,不单独执行。[`examples/docs/vector-search.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/vector-search.ts) 在未设置 `MONSQLIZE_VECTOR_SEARCH_URI` 和 `MONSQLIZE_ATLAS_LOCAL_URI` 时不会调用 Search 并直接退出。 ## 运行方式 @@ -34,12 +34,16 @@ node .generated/examples-dist/examples/docs/find.js |------|------| | [`find.md`](./find.md) | [`examples/docs/find.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find.ts) | | [`findOne.md`](./findOne.md) | [`examples/docs/find-one.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find-one.ts) | +| [`find-one-by-id.md`](./find-one-by-id.md) | [`examples/docs/find-one-by-id.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find-one-by-id.ts) | +| [`find-by-ids.md`](./find-by-ids.md) | [`examples/docs/find-by-ids.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find-by-ids.ts) | | [`findPage.md`](./findPage.md) | [`examples/docs/find-page.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find-page.ts) | | [`find-and-count.md`](./find-and-count.md) | [`examples/docs/find-and-count.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/find-and-count.ts) | | [`count.md`](./count.md) | [`examples/docs/count.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/count.ts) | | [`count-queue.md`](./count-queue.md) | [`examples/docs/count-queue.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/count-queue.ts) | | [`distinct.md`](./distinct.md) | [`examples/docs/distinct.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/distinct.ts) | | [`aggregate.md`](./aggregate.md) | [`examples/docs/aggregate.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/aggregate.ts) | +| [`vector-search.md`](./vector-search.md) | [`examples/docs/vector-search.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/vector-search.ts) | +| [`search-indexes.md`](./search-indexes.md) | [`examples/docs/vector-search.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/vector-search.ts) | | [`explain.md`](./explain.md) | [`examples/docs/explain.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/explain.ts) | | [`chaining-api.md`](./chaining-api.md) | [`examples/docs/chaining-api.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/chaining-api.ts) | diff --git a/docs/zh/recipes.md b/docs/zh/recipes.md index 326fc26d..ff73e561 100644 --- a/docs/zh/recipes.md +++ b/docs/zh/recipes.md @@ -197,6 +197,12 @@ Model 的 schema 回调使用当前 `MonSQLize` 实例隔离的 `schema-dsl/runt 示例源码:[`examples/docs/model.ts`](https://github.com/devcodex-labs/monSQLize/blob/main/examples/docs/model.ts) +## 用 vectorSearch 查询 + +`vectorSearch()` 查询的是 `vectorSearch` 类型的 Search 索引。先用 [Search 索引生命周期 API](./search-indexes.md) 创建并等待该索引,再按 [`vectorSearch()`](./vector-search.md) 的说明发起查询。 + +可运行示例是 [`examples/docs/vector-search.ts`](../../examples/docs/vector-search.ts)。它需要 MongoDB Atlas、`mongodb/mongodb-atlas-local`,或带 `mongot` 的 Community / Enterprise 8.2+。请设置 `MONSQLIZE_VECTOR_SEARCH_URI` 或 `MONSQLIZE_ATLAS_LOCAL_URI`。`mongodb-memory-server` 不能执行 Search 调用。这两个变量都未设置时,示例不会调用 Search 并直接退出。 + ## 按错误码排障 ```ts diff --git a/docs/zh/search-indexes.md b/docs/zh/search-indexes.md index c33536a9..04ce214b 100644 --- a/docs/zh/search-indexes.md +++ b/docs/zh/search-indexes.md @@ -1,6 +1,6 @@ # Search 索引生命周期 -从 v3.4.0 起,集合访问器和 Model 可以创建、查看、更新、删除并等待 MongoDB Search 索引。同一套接口同时支持全文 `search` 索引和 `vectorSearch` 索引;查询向量索引请使用 [`vectorSearch()`](vector-search.md)。 +从 v3.4.0 起,集合访问器和 Model 可以创建、查看、更新、删除并等待 MongoDB Search 索引。**Search 索引**的类型为全文 `search` 或 `vectorSearch`。查询 `vectorSearch` 类型的 Search 索引请使用 [`vectorSearch()`](vector-search.md)。 ## 环境要求 @@ -53,41 +53,82 @@ interface SearchIndexInfo { | 方法 | 行为 | |------|------| -| `createSearchIndex` | 先做本地校验,再检查是否已有同名索引。默认 `ifExists: 'error'` 时以 `SEARCH_INDEX_EXISTS` 拒绝;`'skip'` 不做改动(`action: 'skipped'`);`'update'` 替换定义(`action: 'updated'`),但类型不同时以 `INVALID_SEARCH_INDEX_DEFINITION` 拒绝,需要先删除再创建。 | -| `createSearchIndexes` | 全部校验通过后一次提交;不支持 `ifExists`,名称重复时以 `SEARCH_INDEX_EXISTS` 拒绝。 | -| `listSearchIndexes` | 返回归一化后的条目;`type` 按解析出的类型过滤。 | -| `getSearchIndex` | 索引不存在时返回 `status: 'DOES_NOT_EXIST'`,不抛错。 | -| `updateSearchIndex` | 替换定义,服务端随后重建;重建期间旧版本可以继续提供查询。上报的 `queryable` 不一致:Community 8.2.12 保持 `true`;atlas-local 8.3.13 在 `search` 索引重建期间上报 `false`,但 `$search` 仍返回结果(均为实测)。索引不存在时以 `SEARCH_INDEX_NOT_FOUND` 拒绝。 | -| `dropSearchIndex` | 索引不存在时以 `SEARCH_INDEX_NOT_FOUND` 拒绝;传 `ignoreMissing: true` 则直接返回。 | -| `waitForSearchIndex` | 每隔 `intervalMs` 查询一次,规则见下表。 | - -> **`vectorSearch` 索引能否原地更新取决于部署环境。** -> -> | 部署 | 对 `vectorSearch` 执行 `updateSearchIndex` | -> |---|---| -> | MongoDB Atlas | 按 MongoDB 官方文档支持原地更新,重建期间旧索引继续提供查询。monSQLize 未实测。 | -> | `mongodb/mongodb-atlas-local`(mongod 8.3.13) | 服务端拒绝:`BadValue`(2)`"mappings" is required`。已实测。 | -> | Community 8.2.12 + `mongodb/mongodb-community-search` | 接受。重建期间(20,000 文档 × 64 维约 30 秒)`$vectorSearch` 持续返回结果。已实测。 | -> -> 在所有实测环境中,服务端都不接受 `updateSearchIndex` 命令带 `type` 字段(`IDLUnknownField`),带上也没有用。服务端拒绝时,monSQLize 原样透出为 `MONGODB_ERROR`(带 `serverCode`),不会自动降级。 -> -> 不能原地更新时,推荐用换名切换做到不中断:用新名字创建索引,`waitForSearchIndex(newName, { until: 'queryable' })`,把查询切到新名字,再删除旧索引。期间两个索引同时存在,会额外占用资源,并计入索引数量上限(Atlas Free 档 3 个 Search 索引,Flex 档 10 个)。如果能接受短暂中断,也可以先删除索引、等待 `dropped`、再重新创建;在它重新可查询之前检索会失败,中断时长随数据量增长(atlas-local 实测:50,000 文档 × 64 维约 1–2 秒)。 +| `createSearchIndex` | 校验定义,再按 `ifExists` 处理。 | +| `createSearchIndexes` | 全部校验后一次提交。 | +| `listSearchIndexes` | 返回归一化条目。可用 `type` 过滤。 | +| `getSearchIndex` | 名称不存在时返回 `DOES_NOT_EXIST`。 | +| `updateSearchIndex` | 替换定义,由服务端重建。 | +| `dropSearchIndex` | 删除索引;`ignoreMissing` 时直接返回。 | +| `waitForSearchIndex` | 轮询到目标条件、超时或中止。 | + +### createSearchIndex + +调用先做本地校验,再查找同名索引。 + +- 默认 `ifExists: 'error'`:已存在时以 `SEARCH_INDEX_EXISTS` 拒绝。 +- `'skip'`:不改动已有索引,返回 `action: 'skipped'`。 +- `'update'`:替换定义,返回 `action: 'updated'`。 +- 类型变化以 `INVALID_SEARCH_INDEX_DEFINITION` 拒绝。需要先删除再创建。 + +### createSearchIndexes + +全部 spec 校验通过后一次提交。不支持 `ifExists`。名称重复时以 `SEARCH_INDEX_EXISTS` 拒绝。 + +### listSearchIndexes 与 getSearchIndex + +`listSearchIndexes` 返回归一化后的条目。`type` 按解析出的类型过滤。`typeSource: 'unknown'` 的条目不会命中该过滤。 + +`getSearchIndex` 在索引不存在时返回 `status: 'DOES_NOT_EXIST'`,不抛错。 + +### updateSearchIndex + +调用替换定义。服务端异步重建索引。重建期间,上一版可以继续提供查询。 + +各部署上报的 `queryable` 并不相同。Community 8.2.12 保持 `true`。atlas-local 8.3.13 在 `search` 类型重建期间上报 `false`,但 `$search` 仍返回结果。两者均为实测。索引不存在时以 `SEARCH_INDEX_NOT_FOUND` 拒绝。 + +对 `vectorSearch` 类型的 Search 索引执行原地 `updateSearchIndex`,取决于部署环境: + +| 部署 | 原地更新 | +|------|----------| +| MongoDB Atlas | 文档写明支持。monSQLize 未实测。 | +| atlas-local 8.3.13 | 拒绝。服务端 `BadValue`(2)。已实测。 | +| Community 8.2.12 + community-search | 接受。重建期间查询继续返回。已实测。 | + +MongoDB Atlas 文档说明重建期间旧索引继续提供查询。monSQLize 没有实测这条路径。 + +`mongodb/mongodb-atlas-local`(mongod 8.3.13)以 `BadValue`(2)拒绝更新,消息为 `"mappings" is required`。 + +Community 8.2.12 配合 `mongodb/mongodb-community-search` 会接受更新。20,000 篇文档、64 维的一次实测重建约 30 秒,期间 `$vectorSearch` 持续返回结果。 + +在所有实测环境中,服务端都不接受 `updateSearchIndex` 命令带 `type` 字段(`IDLUnknownField`)。带上该字段也没有用。服务端拒绝时,monSQLize 原样透出为 `MONGODB_ERROR`(带 `serverCode`),不会自动降级。 + +不能原地更新时,用换名切换避免中断:用新名字创建索引,调用 `waitForSearchIndex(newName, { until: 'queryable' })`,把查询切到新名字,再删除旧索引。期间两个索引同时存在,会额外占用资源,并计入索引数量上限(Atlas Free 档 3 个 Search 索引,Flex 档 10 个)。 + +如果能接受短暂中断,可以先删除索引、等待 `dropped`、再重新创建。在它重新可查询之前,检索会失败。中断时长随数据量增长。atlas-local 上 50,000 篇文档、64 维约 1–2 秒。 `status` 原样透传服务端的值。判断能否检索看 `queryable`,展示进度看 `status`。 +### dropSearchIndex + +索引不存在时以 `SEARCH_INDEX_NOT_FOUND` 拒绝,除非 `ignoreMissing: true`。 + ### 索引类型 -类型优先取服务端返回的顶层 `type` 字段(`typeSource: 'server'`)。自建 `mongot` 不返回该字段(见 [MongoDB 限制说明](https://www.mongodb.com/docs/search/self-managed/current/limitations/)),此时根据 `latestDefinition` 推断(`typeSource: 'inferred'`):`fields` 数组中有 `type: 'vector'` 的项即为 `vectorSearch`,有 `mappings` 键即为 `search`。两者都没有时不返回 `type`,`typeSource` 为 `'unknown'`,这类条目不会被 `listSearchIndexes({ type })` 匹配。 +类型优先取服务端返回的顶层 `type` 字段(`typeSource: 'server'`)。自建 `mongot` 不返回该字段(见 [MongoDB 限制说明](https://www.mongodb.com/docs/search/self-managed/current/limitations/))。此时根据 `latestDefinition` 推断(`typeSource: 'inferred'`):`fields` 数组中有 `type: 'vector'` 的项即为 `vectorSearch`,有 `mappings` 键即为 `search`。两者都没有时不返回 `type`,`typeSource` 为 `'unknown'`。 ### waitForSearchIndex | `until` | 何时返回 | 遇到 `FAILED` | 索引不存在 | |---------|----------|---------------|------------| -| `queryable` | `queryable === true` | 仅在不可查询时以 `SEARCH_INDEX_BUILD_FAILED` 拒绝 | 最多再等 30 秒,之后以 `SEARCH_INDEX_NOT_FOUND` 拒绝 | -| `ready` | `status === 'READY'` | 以 `SEARCH_INDEX_BUILD_FAILED` 拒绝 | 同上 | -| `dropped` | `status === 'DOES_NOT_EXIST'` | 继续等待 | 直接返回 | +| `queryable` | `queryable` 为 true | 不可查询时拒绝 | 宽限 30 秒后未找到 | +| `ready` | `status` 为 `READY` | 拒绝 | 同样宽限 30 秒 | +| `dropped` | `status` 为 `DOES_NOT_EXIST` | 继续等待 | 直接返回 | + +`until: 'queryable'` 时,仅在索引不可查询的情况下,`FAILED` 以 `SEARCH_INDEX_BUILD_FAILED` 拒绝。`until: 'ready'` 时,`FAILED` 以 `SEARCH_INDEX_BUILD_FAILED` 拒绝。`until: 'dropped'` 时,`FAILED` 继续等待。 -超时以 `SEARCH_INDEX_WAIT_TIMEOUT` 拒绝,`details[0].last` 是最后一次观察到的状态。`signal` 中止时原样以 `signal.reason` 拒绝。 +`queryable` 或 `ready` 下索引不存在时,最多再等 30 秒,之后以 `SEARCH_INDEX_NOT_FOUND` 拒绝。`dropped` 下索引不存在时直接返回。 + +方法每隔 `intervalMs` 查询一次。超时以 `SEARCH_INDEX_WAIT_TIMEOUT` 拒绝,`details[0].last` 是最后一次观察到的状态。`signal` 中止时原样以 `signal.reason` 拒绝。 ## writePathPolicy @@ -97,13 +138,15 @@ interface SearchIndexInfo { | 码 | 何时出现 | |----|----------| -| `INVALID_SEARCH_INDEX_DEFINITION` | 本地校验失败(名称、类型、定义、选项),或 `ifExists: 'update'` 的目标是另一种类型的索引 | -| `SEARCH_INDEX_EXISTS` | 已有同名索引且 `ifExists: 'error'`,或 `createSearchIndexes` 中名称重复 | -| `SEARCH_INDEX_NOT_FOUND` | `updateSearchIndex` / `dropSearchIndex` 的目标不存在;`waitForSearchIndex` 在宽限期后仍未看到索引 | +| `INVALID_SEARCH_INDEX_DEFINITION` | 本地校验失败,或 `update` 改变了类型 | +| `SEARCH_INDEX_EXISTS` | 同名已存在,或批量创建里名称重复 | +| `SEARCH_INDEX_NOT_FOUND` | 更新、删除或等待的目标不存在 | | `SEARCH_INDEX_UNSUPPORTED` | 部署不支持 Search 索引命令 | | `SEARCH_INDEX_BUILD_FAILED` | `waitForSearchIndex` 观察到 `FAILED` | | `SEARCH_INDEX_WAIT_TIMEOUT` | `waitForSearchIndex` 超过 `timeoutMs` | +本地校验覆盖名称、类型、定义和选项。`ifExists: 'update'` 也会拒绝类型变化。`SEARCH_INDEX_NOT_FOUND` 包括 `waitForSearchIndex` 在宽限期后仍未看到索引。 + 由服务端返回的错误都把驱动原错误放在 `cause`。没有专用码的服务端错误以 `MONGODB_ERROR` 拒绝,`details[0]` 带 `serverCode`、`serverCodeName`、`serverMessage`,并附加 `error.monsqlize = { operation, namespace, indexName }`。详见[错误码参考](error-codes.md)。 ## 示例 @@ -130,3 +173,5 @@ console.log(info.status, info.queryable); await docs.dropSearchIndex('text_idx', { ignoreMissing: true }); await docs.waitForSearchIndex('text_idx', { until: 'dropped' }); ``` + +用 [`vectorSearch()`](vector-search.md) 查询 `vectorSearch` 类型的 Search 索引。可运行的创建、等待和查询示例是 [`examples/docs/vector-search.ts`](../../examples/docs/vector-search.ts)。它需要 MongoDB Atlas、`mongodb/mongodb-atlas-local`,或带 `mongot` 的 Community / Enterprise 8.2+,并通过 `MONSQLIZE_VECTOR_SEARCH_URI` 或 `MONSQLIZE_ATLAS_LOCAL_URI` 连接。 diff --git a/docs/zh/upgrade-3.3.2.md b/docs/zh/upgrade-3.3.2.md index 4dfd7c7f..b63b8b0f 100644 --- a/docs/zh/upgrade-3.3.2.md +++ b/docs/zh/upgrade-3.3.2.md @@ -1,5 +1,7 @@ # 从 3.3.1 升级到 3.3.2 +> **后续版本。** 3.4.0 增加 [Search 索引生命周期 API](./search-indexes.md),3.5.0 增加 [GridFS](./gridfs.md)。这两次发布是新增能力,没有单独的迁移指南。本页只覆盖从 3.3.1 升级到 3.3.2。 + 本页帮助应用从 **3.3.1 升级到 3.3.2**。未打 tag 的检出仍按发布候选处理。安装准确版本前,先执行 `npm view monsqlize@3.3.2 version --registry=https://registry.npmjs.org/` 并核实不可变 tag;尚未发布时,在预发布环境验证候选的打包产物。准备候选不会发布 npm,也不会更新稳定站。 本补丁修复 BSON 查询与缓存身份、Model 缓存失效、分页、事务、同步及资源清理。公开包入口、Node.js `>=18`、直接运行时依赖和构建格式保持不变。自动缓存失效仍默认关闭。 diff --git a/docs/zh/vector-search.md b/docs/zh/vector-search.md index 085a047f..da6ca4bf 100644 --- a/docs/zh/vector-search.md +++ b/docs/zh/vector-search.md @@ -4,9 +4,9 @@ ## 前置条件 -该 API 需要支持 Vector Search 的 MongoDB 部署,以及一个已经创建好的 Vector Search 索引。从 v3.4.0 起可以使用 [Search 索引生命周期 API](search-indexes.md) 管理该索引。请保证索引配置中的向量维度、数值表示和索引字段路径与传入 API 的 embedding 一致。部署和索引要求请参考 [MongoDB `$vectorSearch` 文档](https://www.mongodb.com/docs/vector-search/query/aggregation-stages/vector-search-stage/)。 +该 API 需要支持 Vector Search 的 MongoDB 部署,以及一个已经创建好的 Search 索引(`vectorSearch` 类型)。**Search 索引**指 MongoDB Search 索引,类型为全文 `search` 或 `vectorSearch`。从 v3.4.0 起可以使用 [Search 索引生命周期 API](search-indexes.md) 管理该索引。请保证索引配置中的向量维度、数值表示和索引字段路径与传入 API 的 embedding 一致。部署和索引要求请参考 [MongoDB `$vectorSearch` 文档](https://www.mongodb.com/docs/vector-search/query/aggregation-stages/vector-search-stage/)。 -`$vectorSearch` 必须是聚合管道的第一个阶段;它也不能放在 `$lookup` 子管道或 `$facet` 中。因此应以集合级 API 作为检索入口,如需补充关联信息,再对命中文档做后续处理。 +`$vectorSearch` 必须是它所在管道的第一个阶段。当前 MongoDB 文档仍然不允许把它放进 `$lookup` 子管道或 `$facet` 阶段([手册](https://www.mongodb.com/docs/manual/reference/operator/aggregation/vectorsearch/)、[Vector Search 阶段](https://www.mongodb.com/docs/vector-search/query/aggregation-stages/vector-search-stage/))。请用 `vectorSearch()` 作为检索入口,需要补充关联信息时再处理命中文档。 ## API @@ -78,7 +78,7 @@ for (const { document, score } of hits) { } ``` -`filter` 是 Vector Search 的预过滤条件,不是后置 `$match`。依赖该条件前,需要先在 Vector Search 索引中配置相应的过滤字段。 +`filter` 是 Vector Search 的预过滤条件,不是后置 `$match`。依赖该条件前,需要先在 Search 索引(`vectorSearch` 类型)中配置相应的过滤字段。 ## 精确最近邻查询(ENN) @@ -129,10 +129,10 @@ await hits[0]?.document.populate('author'); 部署、索引或其他 MongoDB 执行失败会保留原始 driver 的 code 和 message 后重新抛出。错误对象可写时,monSQLize 会附加 `error.monsqlize.operation === 'vectorSearch'` 和目标 namespace,便于诊断。 -本版本同时在 `Collection` 和 `Model` 上提供向量查询;不会提供向量索引生命周期 API。 +Search 索引(含 `vectorSearch` 类型)的管理见 [Search 索引生命周期](search-indexes.md):`createSearchIndex`、`updateSearchIndex`、`dropSearchIndex`、`listSearchIndexes`、`waitForSearchIndex`。 ## 运行注意事项 -- 默认本地 `mongodb-memory-server` 不支持 Vector Search。单元测试覆盖管道构造和参数校验;端到端验证需要外部准备的兼容部署和索引。 +- 默认本地 `mongodb-memory-server` 不支持 Vector Search。单元测试覆盖管道构造和参数校验。端到端验证需要 Atlas、`mongodb/mongodb-atlas-local`,或带 `mongot` 的 Community / Enterprise 8.2+。可运行示例是 [`examples/docs/vector-search.ts`](../../examples/docs/vector-search.ts)。请设置 `MONSQLIZE_VECTOR_SEARCH_URI` 或 `MONSQLIZE_ATLAS_LOCAL_URI`。两者都未设置时,该示例不会调用 Search 并直接退出,因此 `npm run test:examples` 仍可以执行它。 - 向量排序分数由 MongoDB 生成,monSQLize 只返回该分数,不会在本地二次排序。 - 请让写入端和查询端使用一致的 embedding 模型、向量维度和索引配置。 diff --git a/examples/README.md b/examples/README.md index bac1ce4c..e07e2867 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,7 +3,7 @@ Runnable TypeScript examples for every major monSQLize API. Each file uses an in-memory MongoDB server (`mongodb-memory-server`) — no real database needed. -> This repository currently includes **58** runnable TypeScript examples. `examples/helpers/bootstrap.ts` is a shared helper module, and `npm run test:examples` compiles and runs the executable examples. +> This repository currently includes **59** runnable TypeScript examples. `examples/helpers/bootstrap.ts` is a shared helper module, and `npm run test:examples` compiles and runs the executable examples. `examples/docs/vector-search.ts` exits without calling Search when `MONSQLIZE_VECTOR_SEARCH_URI` and `MONSQLIZE_ATLAS_LOCAL_URI` are unset. ## Prerequisites @@ -115,6 +115,14 @@ npm run test:examples | [`docs/sync.ts`](docs/sync.ts) | ChangeStreamSyncManager + ResumeTokenStore — resume-token-based change stream sync | | [`docs/sync-target-failure.ts`](docs/sync-target-failure.ts) | Sync target failure accounting and recovery path | +### Docs — Search and vectors + +`examples/docs/vector-search.ts` needs MongoDB Atlas, `mongodb/mongodb-atlas-local`, or Community/Enterprise 8.2+ with `mongot`. Set `MONSQLIZE_VECTOR_SEARCH_URI` or `MONSQLIZE_ATLAS_LOCAL_URI`. `npm run test:examples` still invokes the file; without those variables it prints a skip line and exits 0, because `mongodb-memory-server` cannot serve Search indexes. + +| File | Description | +|------|-------------| +| [`docs/vector-search.ts`](docs/vector-search.ts) | Create a `vectorSearch` Search index, wait until queryable, query with `vectorSearch()`, then drop it | + ### Docs — Legacy Compatibility These examples remain runnable for existing users, but they are not primary monSQLize capabilities. New non-database function caching, business locks, and Saga orchestration should live in the application or framework runtime. @@ -186,6 +194,7 @@ node .generated/examples-dist/examples/docs/pool-fallback.js node .generated/examples-dist/examples/docs/multi-pool-health-check.js node .generated/examples-dist/examples/docs/sync.js node .generated/examples-dist/examples/docs/sync-target-failure.js +node .generated/examples-dist/examples/docs/vector-search.js # Legacy compatibility examples node .generated/examples-dist/examples/docs/function-cache.js diff --git a/examples/docs/vector-search.ts b/examples/docs/vector-search.ts new file mode 100644 index 00000000..973ee5c2 --- /dev/null +++ b/examples/docs/vector-search.ts @@ -0,0 +1,133 @@ +/** + * Create a Search index of type vectorSearch, wait until it is queryable, + * and query it with collection.vectorSearch(). + * + * See: docs/vector-search.md, docs/search-indexes.md + * + * Required environment (either variable): + * MONSQLIZE_VECTOR_SEARCH_URI + * MONSQLIZE_ATLAS_LOCAL_URI + * + * Point the URI at MongoDB Atlas, the mongodb/mongodb-atlas-local image, or + * MongoDB Community/Enterprise 8.2+ with mongot. Plain mongod and the default + * mongodb-memory-server cannot serve Search indexes. + * + * When neither variable is set, this process prints a skip line and exits 0 + * so `npm run test:examples` can compile and invoke it without a Search server. + * + * Run after building the project: + * npm run build && tsc -p tsconfig.examples.json + * MONSQLIZE_VECTOR_SEARCH_URI='mongodb://127.0.0.1:27017/?directConnection=true' \ + * node .generated/examples-dist/examples/docs/vector-search.js + */ +import MonSQLize from 'monsqlize'; + +const DIMENSIONS = 8; +const INDEX_NAME = 'articles_embedding_index'; + +interface ArticleDoc { + title: string; + status: 'draft' | 'published'; + embedding: number[]; +} + +function searchUri(): string | undefined { + const raw = process.env.MONSQLIZE_VECTOR_SEARCH_URI || process.env.MONSQLIZE_ATLAS_LOCAL_URI; + const uri = raw?.trim(); + return uri ? uri : undefined; +} + +function embedding(seed: number): number[] { + return Array.from({ length: DIMENSIONS }, (_, index) => Math.sin(seed + index) / 2); +} + +async function main(): Promise { + const uri = searchUri(); + if (!uri) { + console.log('[vector-search] SKIPPED: set MONSQLIZE_VECTOR_SEARCH_URI or MONSQLIZE_ATLAS_LOCAL_URI (Atlas, mongodb/mongodb-atlas-local, or Community/Enterprise 8.2+ with mongot).'); + return; + } + + const databaseName = process.env.MONSQLIZE_VECTOR_SEARCH_DB?.trim() || 'monsqlize_vector_example'; + const collectionName = `articles_${Date.now().toString(36)}`; + const msq = new MonSQLize({ + type: 'mongodb', + databaseName, + config: { uri }, + }); + + await msq.connect(); + const articles = msq.collection(collectionName); + + try { + await articles.insertMany([ + { title: 'published-near', status: 'published', embedding: embedding(0.2) }, + { title: 'published-far', status: 'published', embedding: embedding(2.4) }, + { title: 'draft-near', status: 'draft', embedding: embedding(0.25) }, + ]); + + const created = await articles.createSearchIndex({ + name: INDEX_NAME, + type: 'vectorSearch', + definition: { + fields: [ + { + type: 'vector', + path: 'embedding', + numDimensions: DIMENSIONS, + similarity: 'cosine', + }, + { type: 'filter', path: 'status' }, + ], + }, + }); + console.log('createSearchIndex:', created.name, created.action); + + const ready = await articles.waitForSearchIndex(INDEX_NAME, { + until: 'queryable', + timeoutMs: 600_000, + }); + console.log('waitForSearchIndex:', ready.status, ready.queryable); + + const hits = await articles.vectorSearch({ + index: INDEX_NAME, + path: 'embedding', + queryVector: embedding(0.2), + limit: 2, + numCandidates: 10, + filter: { status: 'published' }, + }); + + if (hits.length === 0) { + throw new Error('vectorSearch returned no hits'); + } + + for (const hit of hits) { + if (!Number.isFinite(hit.score)) { + throw new Error('vectorSearch hit is missing a finite score'); + } + if (hit.document.status !== 'published') { + throw new Error(`vectorSearch filter leaked status ${String(hit.document.status)}`); + } + console.log(hit.score, hit.document.title); + } + + await articles.dropSearchIndex(INDEX_NAME); + const dropped = await articles.waitForSearchIndex(INDEX_NAME, { + until: 'dropped', + timeoutMs: 120_000, + }); + console.log('dropSearchIndex:', dropped.status); + console.log('Vector search example complete'); + } finally { + await articles.dropCollection().catch((error: unknown) => { + console.error('Collection cleanup failed:', error); + }); + await msq.close(); + } +} + +main().catch((err: unknown) => { + console.error('Example failed:', err); + process.exit(1); +}); diff --git a/scripts/run-examples.cjs b/scripts/run-examples.cjs index 3c71918a..28dc552f 100644 --- a/scripts/run-examples.cjs +++ b/scripts/run-examples.cjs @@ -74,6 +74,7 @@ const exampleScripts = [ '.generated/examples-dist/examples/docs/multi-pool-health-check.js', '.generated/examples-dist/examples/docs/sync.js', '.generated/examples-dist/examples/docs/sync-target-failure.js', + '.generated/examples-dist/examples/docs/vector-search.js', ]; function runExample(script, env) { diff --git a/scripts/validation/doc-example-matrix.json b/scripts/validation/doc-example-matrix.json index 527f813e..92ed8cb9 100644 --- a/scripts/validation/doc-example-matrix.json +++ b/scripts/validation/doc-example-matrix.json @@ -25,16 +25,16 @@ }, { "doc": "search-indexes", - "kind": "doc-check", - "target": "docs/en/search-indexes.md", - "runner": "check:docs-examples", + "kind": "shared-example", + "target": "examples/docs/vector-search.ts", + "runner": "test:examples", "status": "covered" }, { "doc": "vector-search", - "kind": "doc-check", - "target": "docs/en/vector-search.md", - "runner": "check:docs-examples", + "kind": "example", + "target": "examples/docs/vector-search.ts", + "runner": "test:examples", "status": "covered" }, { diff --git a/website/rspress.config.ts b/website/rspress.config.ts index 889483fe..e3dde970 100644 --- a/website/rspress.config.ts +++ b/website/rspress.config.ts @@ -228,12 +228,13 @@ const sidebarSource: SidebarGroupSource[] = [ items: [ { en: 'find()', zh: 'find()', link: '/find' }, { en: 'findOne()', zh: 'findOne()', link: '/findOne' }, + { en: 'findOneById()', zh: 'findOneById()', link: '/find-one-by-id' }, + { en: 'findByIds()', zh: 'findByIds()', link: '/find-by-ids' }, { en: 'findPage()', zh: 'findPage()', link: '/findPage' }, { en: 'findAndCount()', zh: 'findAndCount()', link: '/find-and-count' }, { en: 'count()', zh: 'count()', link: '/count' }, { en: 'distinct()', zh: 'distinct()', link: '/distinct' }, { en: 'aggregate()', zh: 'aggregate()', link: '/aggregate' }, - { en: 'vectorSearch()', zh: 'vectorSearch()', link: '/vector-search' }, { en: 'explain()', zh: 'explain()', link: '/explain' } ] }, @@ -287,6 +288,14 @@ const sidebarSource: SidebarGroupSource[] = [ { en: 'Expression Function Reference', zh: '表达式函数参考', link: '/expression-functions' } ] }, + { + en: 'Search and vectors', + zh: 'Search 与向量', + items: [ + { en: 'vectorSearch()', zh: 'vectorSearch()', link: '/vector-search' }, + { en: 'Search indexes', zh: 'Search 索引', link: '/search-indexes' } + ] + }, { en: 'Model and Schema', zh: 'Model 与 Schema', @@ -304,9 +313,11 @@ const sidebarSource: SidebarGroupSource[] = [ en: 'Cache', zh: '缓存', items: [ + { en: 'Cache API', zh: '缓存 API', link: '/cache' }, { en: 'Cache Configuration', zh: '缓存配置', link: '/cache-configuration' }, { en: 'Cache Creation', zh: '缓存创建', link: '/cache-creation' }, - { en: 'Cache Invalidation', zh: '缓存失效', link: '/cache-invalidation' } + { en: 'Cache Invalidation', zh: '缓存失效', link: '/cache-invalidation' }, + { en: 'Pagination bookmarks', zh: '分页书签', link: '/bookmarks' } ] }, { @@ -352,7 +363,8 @@ const sidebarSource: SidebarGroupSource[] = [ { en: 'Performance Evidence', zh: '性能证据', link: '/performance-evidence' }, { en: 'Upsert Guide', zh: 'Upsert 指南', link: '/upsert-guide' }, { en: 'Quick Upsert API', zh: 'Quick Upsert API', link: '/quick-upsert' }, - { en: 'Failure Recovery Examples', zh: '失败恢复示例', link: '/failure-recovery-examples' } + { en: 'Failure Recovery Examples', zh: '失败恢复示例', link: '/failure-recovery-examples' }, + { en: 'Runtime architecture', zh: '运行时架构', link: '/runtime-architecture' } ] }, { @@ -365,7 +377,6 @@ const sidebarSource: SidebarGroupSource[] = [ { en: 'Create Indexes in Bulk', zh: '批量创建索引', link: '/create-indexes' }, { en: 'Drop Index', zh: '删除索引', link: '/drop-index' }, { en: 'List Indexes', zh: '列出索引', link: '/list-indexes' }, - { en: 'Search Indexes', zh: 'Search 索引', link: '/search-indexes' }, { en: 'GridFS', zh: 'GridFS', link: '/gridfs' }, { en: 'Database Administration', zh: '数据库管理', link: '/admin' }, { en: 'Error Code Reference', zh: '错误码参考', link: '/error-codes' }, @@ -377,7 +388,8 @@ const sidebarSource: SidebarGroupSource[] = [ en: 'Compatibility and Migration', zh: '兼容与迁移', items: [ - { en: 'Upgrade to 3.3.2', zh: '升级到 3.3.2', link: '/upgrade-3.3.2' }, + { en: 'Upgrade guide', zh: '升级指南', link: '/upgrade-3.3.2' }, + { en: 'Roadmap boundaries', zh: '路线图边界', link: '/roadmap-boundaries' }, { en: 'MongoDB Driver Compatibility', zh: 'MongoDB Driver 兼容性', link: '/mongodb-driver-compatibility' }, { en: 'MongoDB Native vs Extensions', zh: 'MongoDB 原生 vs 扩展', link: '/mongodb-native-vs-extensions' }, { en: 'findOneAnd Return Value', zh: 'findOneAnd 返回值统一', link: '/findOneAnd-return-value-unified' }, @@ -601,12 +613,12 @@ const config: UserConfig = { }, }, route: { + // Kept off the public site: legacy compatibility pages and maintainer runbooks. + // find-one-by-id and find-by-ids are public query references and stay published. exclude: [ '**/function-cache.md', '**/cache-and-function-cache.md', '**/cache-hub-migration.md', - '**/find-one-by-id.md', - '**/find-by-ids.md', '**/file-dependency-governance.md', '**/capability-traceability.md', '**/release-preflight.md', From 0e1b256c749ad5b77bbe0256de66e067efcb0d91 Mon Sep 17 00:00:00 2001 From: Rocky Shi <175696301+rockyshi1993@users.noreply.github.com> Date: Wed, 28 Oct 2026 03:36:14 +0000 Subject: [PATCH 2/2] docs: raise the site JS budget for the new sidebar pages Publishing findOneById and findByIds grows the docs client bundle past 720000 bytes. The measured bundle is 727358 bytes, so the single-file limit is 740000. Co-authored-by: rocky --- website/site-budget.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/site-budget.json b/website/site-budget.json index 977b4197..fee32133 100644 --- a/website/site-budget.json +++ b/website/site-budget.json @@ -2,7 +2,7 @@ "minimumHtmlFiles": 197, "maximumFiles": 450, "maximumTotalBytes": 68000000, - "maximumSingleJavaScriptBytes": 720000, + "maximumSingleJavaScriptBytes": 740000, "maximumSingleHtmlBytes": 550000, "maximumSearchIndexBytesPerLocale": 1800000, "maximumCombinedSearchIndexBytes": 3500000,