ydb-qdrant 2.2.1 → 2.3.0
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/README.md +47 -0
- package/dist/config/env.d.ts +1 -1
- package/dist/config/env.js +15 -3
- package/dist/repositories/pointsRepo.js +24 -15
- package/dist/services/QdrantService.js +9 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -167,6 +167,53 @@ export async function searchDocuments(collection: string, queryEmbedding: number
|
|
|
167
167
|
|
|
168
168
|
This pattern avoids running a separate HTTP service: vector search is executed directly against YDB via the shared `createYdbQdrantClient` instance, while the rest of your code works with plain TypeScript functions.
|
|
169
169
|
|
|
170
|
+
## Recommended Vector Dimensions
|
|
171
|
+
|
|
172
|
+
When creating a collection, you must specify the vector `size` matching your embedding model. Below are popular models with their dimensions and typical use cases:
|
|
173
|
+
|
|
174
|
+
### Commercial API Models
|
|
175
|
+
|
|
176
|
+
| Provider | Model | Dimensions | Use Cases |
|
|
177
|
+
|----------|-------|------------|-----------|
|
|
178
|
+
| **OpenAI** | `text-embedding-3-small` | 1536 (default, can reduce to 256-1536) | RAG, semantic search, general-purpose embeddings |
|
|
179
|
+
| **OpenAI** | `text-embedding-3-large` | 3072 (default, can reduce to 256, 512, 1024, 1536, 3072) | High-accuracy RAG, multilingual tasks |
|
|
180
|
+
| **OpenAI** | `text-embedding-ada-002` | 1536 | Legacy model, widely adopted |
|
|
181
|
+
| **OpenAI** (Legacy) | `text-search-curie-doc-001` | 4096 | Legacy GPT-3 model, deprecated |
|
|
182
|
+
| **OpenAI** (Legacy) | `text-search-davinci-doc-001` | 12288 | Legacy GPT-3 model, deprecated |
|
|
183
|
+
| **Cohere** | `embed-v4.0` | 256, 512, 1024, 1536 (default) | Multimodal (text + image), RAG, enterprise search |
|
|
184
|
+
| **Cohere** | `embed-english-v3.0` | 1024 | English text, semantic search, classification |
|
|
185
|
+
| **Cohere** | `embed-multilingual-v3.0` | 1024 | 100+ languages, long-document retrieval, clustering |
|
|
186
|
+
| **Google** | `gemini-embedding-001` | 3072 (configurable) | Multilingual, general-purpose, RAG |
|
|
187
|
+
| **Google** | `text-embedding-004` | 768 | General-purpose text embeddings |
|
|
188
|
+
| **Google** | `text-embedding-005` | 768 | Improved version of text-embedding-004 |
|
|
189
|
+
| **Google** | `text-multilingual-embedding-002` | 768 | Multilingual text embeddings |
|
|
190
|
+
|
|
191
|
+
### Open-Source Models (HuggingFace)
|
|
192
|
+
|
|
193
|
+
| Model | Dimensions | Use Cases |
|
|
194
|
+
|-------|------------|-----------|
|
|
195
|
+
| `sentence-transformers/all-MiniLM-L6-v2` | 384 | Fast semantic search, low-resource environments |
|
|
196
|
+
| `BAAI/bge-base-en-v1.5` | 768 | RAG, retrieval, English text |
|
|
197
|
+
| `BAAI/bge-large-en-v1.5` | 1024 | High-accuracy RAG, English text |
|
|
198
|
+
| `BAAI/bge-m3` | 1024 | Multilingual, dense/sparse/multi-vector |
|
|
199
|
+
| `intfloat/e5-base-v2` | 768 | General retrieval, English text |
|
|
200
|
+
| `intfloat/e5-large-v2` | 1024 | High-accuracy retrieval, English text |
|
|
201
|
+
| `intfloat/e5-mistral-7b-instruct` | 4096 | High-dimensional embeddings, advanced RAG |
|
|
202
|
+
| `nomic-ai/nomic-embed-text-v1` | 768 | General-purpose, open weights |
|
|
203
|
+
|
|
204
|
+
### Choosing Dimensions
|
|
205
|
+
|
|
206
|
+
- **Higher dimensions (1024-4096)**: Better semantic fidelity, higher storage/compute costs
|
|
207
|
+
- **Lower dimensions (384-768)**: Faster queries, lower costs, suitable for many use cases
|
|
208
|
+
- **Variable dimensions**: Some models (OpenAI v3, Cohere v4) allow dimension reduction with minimal accuracy loss
|
|
209
|
+
- **Legacy models**: Older OpenAI GPT-3 models (Curie: 4096, Davinci: 12288) are deprecated but may still be in use
|
|
210
|
+
|
|
211
|
+
**References:**
|
|
212
|
+
- [OpenAI Embeddings Guide](https://platform.openai.com/docs/guides/embeddings)
|
|
213
|
+
- [Cohere Embed Models](https://docs.cohere.com/docs/cohere-embed)
|
|
214
|
+
- [Google Gemini Embeddings](https://ai.google.dev/gemini-api/docs/embeddings)
|
|
215
|
+
- [HuggingFace Sentence Transformers](https://huggingface.co/sentence-transformers)
|
|
216
|
+
|
|
170
217
|
## Quick Start
|
|
171
218
|
|
|
172
219
|
### Use with IDE agents (Roo Code, Cline)
|
package/dist/config/env.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export declare const YDB_ENDPOINT: string;
|
|
|
3
3
|
export declare const YDB_DATABASE: string;
|
|
4
4
|
export declare const PORT: number;
|
|
5
5
|
export declare const LOG_LEVEL: string;
|
|
6
|
-
export declare const
|
|
6
|
+
export declare const VECTOR_INDEX_BUILD_ENABLED: boolean;
|
package/dist/config/env.js
CHANGED
|
@@ -3,6 +3,18 @@ export const YDB_ENDPOINT = process.env.YDB_ENDPOINT ?? "";
|
|
|
3
3
|
export const YDB_DATABASE = process.env.YDB_DATABASE ?? "";
|
|
4
4
|
export const PORT = process.env.PORT ? Number(process.env.PORT) : 8080;
|
|
5
5
|
export const LOG_LEVEL = process.env.LOG_LEVEL ?? "info";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
function parseBooleanEnv(value, defaultValue) {
|
|
7
|
+
if (value === undefined) {
|
|
8
|
+
return defaultValue;
|
|
9
|
+
}
|
|
10
|
+
const normalized = value.trim().toLowerCase();
|
|
11
|
+
if (normalized === "" ||
|
|
12
|
+
normalized === "0" ||
|
|
13
|
+
normalized === "false" ||
|
|
14
|
+
normalized === "no" ||
|
|
15
|
+
normalized === "off") {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
export const VECTOR_INDEX_BUILD_ENABLED = parseBooleanEnv(process.env.VECTOR_INDEX_BUILD_ENABLED, false);
|
|
@@ -2,6 +2,7 @@ import { TypedValues, withSession } from "../ydb/client.js";
|
|
|
2
2
|
import { buildJsonOrEmpty, buildVectorParam } from "../ydb/helpers.js";
|
|
3
3
|
import { logger } from "../logging/logger.js";
|
|
4
4
|
import { notifyUpsert } from "../indexing/IndexScheduler.js";
|
|
5
|
+
import { VECTOR_INDEX_BUILD_ENABLED } from "../config/env.js";
|
|
5
6
|
export async function upsertPoints(tableName, points, vectorType, dimension) {
|
|
6
7
|
let upserted = 0;
|
|
7
8
|
await withSession(async (s) => {
|
|
@@ -76,26 +77,34 @@ export async function searchPoints(tableName, queryVector, top, withPayload, dis
|
|
|
76
77
|
LIMIT $k2;
|
|
77
78
|
`;
|
|
78
79
|
let rs;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return await s.executeQuery(buildQuery(true), params);
|
|
83
|
-
});
|
|
84
|
-
logger.info({ tableName }, "vector index found; using index for search");
|
|
85
|
-
}
|
|
86
|
-
catch (e) {
|
|
87
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
88
|
-
// Fallback to table scan if index not found or not ready
|
|
89
|
-
if (/not found|does not exist|no such index|no global index|is not ready to use/i.test(msg)) {
|
|
90
|
-
logger.info({ tableName }, "vector index not available (missing or building); falling back to table scan");
|
|
80
|
+
if (VECTOR_INDEX_BUILD_ENABLED) {
|
|
81
|
+
try {
|
|
82
|
+
// Try with vector index first
|
|
91
83
|
rs = await withSession(async (s) => {
|
|
92
|
-
return await s.executeQuery(buildQuery(
|
|
84
|
+
return await s.executeQuery(buildQuery(true), params);
|
|
93
85
|
});
|
|
86
|
+
logger.info({ tableName }, "vector index found; using index for search");
|
|
94
87
|
}
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
catch (e) {
|
|
89
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
90
|
+
const indexUnavailable = /not found|does not exist|no such index|no global index|is not ready to use/i.test(msg);
|
|
91
|
+
if (indexUnavailable) {
|
|
92
|
+
logger.info({ tableName }, "vector index not available (missing or building); falling back to table scan");
|
|
93
|
+
rs = await withSession(async (s) => {
|
|
94
|
+
return await s.executeQuery(buildQuery(false), params);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
throw e;
|
|
99
|
+
}
|
|
97
100
|
}
|
|
98
101
|
}
|
|
102
|
+
else {
|
|
103
|
+
// Vector index usage disabled: always use table scan
|
|
104
|
+
rs = await withSession(async (s) => {
|
|
105
|
+
return await s.executeQuery(buildQuery(false), params);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
99
108
|
const rowset = rs.resultSets?.[0];
|
|
100
109
|
const rows = (rowset?.rows ?? []);
|
|
101
110
|
return rows.map((row) => {
|
|
@@ -5,6 +5,7 @@ import { createCollection as repoCreateCollection, deleteCollection as repoDelet
|
|
|
5
5
|
import { deletePoints as repoDeletePoints, searchPoints as repoSearchPoints, upsertPoints as repoUpsertPoints, } from "../repositories/pointsRepo.js";
|
|
6
6
|
import { requestIndexBuild } from "../indexing/IndexScheduler.js";
|
|
7
7
|
import { logger } from "../logging/logger.js";
|
|
8
|
+
import { VECTOR_INDEX_BUILD_ENABLED } from "../config/env.js";
|
|
8
9
|
export class QdrantServiceError extends Error {
|
|
9
10
|
statusCode;
|
|
10
11
|
payload;
|
|
@@ -87,6 +88,7 @@ export async function deleteCollection(ctx) {
|
|
|
87
88
|
await repoDeleteCollection(normalized.metaKey);
|
|
88
89
|
return { acknowledged: true };
|
|
89
90
|
}
|
|
91
|
+
let loggedIndexBuildDisabled = false;
|
|
90
92
|
function isNumberArray(value) {
|
|
91
93
|
return Array.isArray(value) && value.every((x) => typeof x === "number");
|
|
92
94
|
}
|
|
@@ -213,7 +215,13 @@ export async function upsertPoints(ctx, body) {
|
|
|
213
215
|
});
|
|
214
216
|
}
|
|
215
217
|
const upserted = await repoUpsertPoints(meta.table, parsed.data.points, meta.vectorType, meta.dimension);
|
|
216
|
-
|
|
218
|
+
if (VECTOR_INDEX_BUILD_ENABLED) {
|
|
219
|
+
requestIndexBuild(meta.table, meta.dimension, meta.distance, meta.vectorType);
|
|
220
|
+
}
|
|
221
|
+
else if (!loggedIndexBuildDisabled) {
|
|
222
|
+
logger.info({ table: meta.table }, "vector index building disabled by env; skipping automatic emb_idx rebuilds");
|
|
223
|
+
loggedIndexBuildDisabled = true;
|
|
224
|
+
}
|
|
217
225
|
return { upserted };
|
|
218
226
|
}
|
|
219
227
|
async function executeSearch(ctx, normalizedSearch, source) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ydb-qdrant",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"main": "dist/package/Api.js",
|
|
5
5
|
"types": "dist/package/Api.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"test": "vitest run --exclude \"test/integration/**\"",
|
|
18
18
|
"test:coverage": "vitest run --coverage --exclude \"test/integration/**\"",
|
|
19
|
-
"test:integration": "vitest run test/integration/YdbRealIntegration.test.ts",
|
|
19
|
+
"test:integration": "VECTOR_INDEX_BUILD_ENABLED=false vitest run test/integration/YdbRealIntegration.index-disabled.test.ts && VECTOR_INDEX_BUILD_ENABLED=true vitest run test/integration/YdbRealIntegration.test.ts",
|
|
20
20
|
"build": "tsc -p tsconfig.json",
|
|
21
21
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
22
22
|
"dev": "tsx watch src/index.ts",
|
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@bufbuild/protobuf": "^2.10.0",
|
|
56
56
|
"@grpc/grpc-js": "^1.14.0",
|
|
57
|
+
"@yandex-cloud/nodejs-sdk": "^2.9.0",
|
|
57
58
|
"@ydbjs/api": "^6.0.4",
|
|
58
59
|
"@ydbjs/core": "^6.0.4",
|
|
59
60
|
"@ydbjs/query": "^6.0.4",
|
|
@@ -79,4 +80,4 @@
|
|
|
79
80
|
"typescript-eslint": "^8.47.0",
|
|
80
81
|
"vitest": "^4.0.12"
|
|
81
82
|
}
|
|
82
|
-
}
|
|
83
|
+
}
|