ydb-qdrant 6.0.0 → 8.1.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/dist/config/env.d.ts +0 -3
- package/dist/config/env.js +0 -17
- package/dist/package/api.d.ts +3 -0
- package/dist/qdrant/QdrantRestTypes.d.ts +35 -0
- package/dist/qdrant/QdrantRestTypes.js +1 -0
- package/dist/repositories/collectionsRepo.one-table.js +37 -63
- package/dist/repositories/collectionsRepo.shared.js +8 -2
- package/dist/repositories/pointsRepo.d.ts +5 -11
- package/dist/repositories/pointsRepo.js +6 -3
- package/dist/repositories/pointsRepo.one-table/Delete.d.ts +2 -0
- package/dist/repositories/pointsRepo.one-table/Delete.js +166 -0
- package/dist/repositories/pointsRepo.one-table/PathSegmentsFilter.d.ts +14 -0
- package/dist/repositories/pointsRepo.one-table/PathSegmentsFilter.js +33 -0
- package/dist/repositories/pointsRepo.one-table/Search.d.ts +4 -0
- package/dist/repositories/pointsRepo.one-table/Search.js +208 -0
- package/dist/repositories/pointsRepo.one-table/Upsert.d.ts +2 -0
- package/dist/repositories/pointsRepo.one-table/Upsert.js +85 -0
- package/dist/repositories/pointsRepo.one-table.d.ts +3 -13
- package/dist/repositories/pointsRepo.one-table.js +3 -403
- package/dist/routes/points.js +17 -4
- package/dist/server.d.ts +1 -0
- package/dist/server.js +70 -2
- package/dist/services/CollectionService.d.ts +9 -0
- package/dist/services/CollectionService.js +9 -0
- package/dist/services/PointsService.d.ts +3 -10
- package/dist/services/PointsService.js +73 -3
- package/dist/types.d.ts +59 -5
- package/dist/types.js +27 -3
- package/dist/utils/normalization.d.ts +1 -0
- package/dist/utils/normalization.js +2 -1
- package/dist/utils/vectorBinary.js +94 -10
- package/dist/ydb/bootstrapMetaTable.d.ts +7 -0
- package/dist/ydb/bootstrapMetaTable.js +75 -0
- package/dist/ydb/client.d.ts +10 -3
- package/dist/ydb/client.js +26 -2
- package/dist/ydb/helpers.d.ts +0 -2
- package/dist/ydb/helpers.js +0 -7
- package/dist/ydb/schema.js +100 -66
- package/package.json +3 -6
package/dist/ydb/schema.js
CHANGED
|
@@ -1,65 +1,91 @@
|
|
|
1
1
|
import { withSession, TableDescription, Column, Types, Ydb } from "./client.js";
|
|
2
2
|
import { logger } from "../logging/logger.js";
|
|
3
|
-
import { GLOBAL_POINTS_AUTOMIGRATE_ENABLED } from "../config/env.js";
|
|
4
3
|
export const GLOBAL_POINTS_TABLE = "qdrant_all_points";
|
|
5
4
|
// Shared YDB-related constants for repositories.
|
|
6
5
|
export { UPSERT_BATCH_SIZE } from "../config/env.js";
|
|
6
|
+
let metaTableReady = false;
|
|
7
|
+
let metaTableReadyInFlight = null;
|
|
7
8
|
let globalPointsTableReady = false;
|
|
9
|
+
let globalPointsTableReadyInFlight = null;
|
|
8
10
|
function throwMigrationRequired(message) {
|
|
9
11
|
logger.error(message);
|
|
10
12
|
throw new Error(message);
|
|
11
13
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// current ydb-sdk internals (tested with ydb-sdk v5.11.1) to run ALTER TABLE
|
|
28
|
-
// as a scheme query. If the SDK changes its internal API, this may need to be
|
|
29
|
-
// revisited or replaced with an officially supported migration mechanism.
|
|
30
|
-
const rawSession = s;
|
|
31
|
-
await rawSession.api.executeSchemeQuery({
|
|
32
|
-
sessionId: rawSession.sessionId,
|
|
33
|
-
yqlText: alterDdl,
|
|
34
|
-
});
|
|
35
|
-
logger.info("added last_accessed_at column to metadata table qdr__collections");
|
|
36
|
-
}
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
// create via schema API
|
|
41
|
-
const desc = new TableDescription()
|
|
42
|
-
.withColumns(new Column("collection", Types.UTF8), new Column("table_name", Types.UTF8), new Column("vector_dimension", Types.UINT32), new Column("distance", Types.UTF8), new Column("vector_type", Types.UTF8), new Column("created_at", Types.TIMESTAMP), new Column("last_accessed_at", Types.TIMESTAMP))
|
|
43
|
-
.withPrimaryKey("collection");
|
|
44
|
-
await s.createTable("qdr__collections", desc);
|
|
45
|
-
logger.info("created metadata table qdr__collections");
|
|
46
|
-
}
|
|
47
|
-
});
|
|
14
|
+
function isTableNotFoundError(err) {
|
|
15
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
16
|
+
const ctorName = err instanceof Error
|
|
17
|
+
? err.constructor?.name
|
|
18
|
+
: undefined;
|
|
19
|
+
const statusCodeMatch = /code\s+(\d{6})/i.exec(msg);
|
|
20
|
+
const statusCode = statusCodeMatch && statusCodeMatch[1]
|
|
21
|
+
? Number(statusCodeMatch[1])
|
|
22
|
+
: undefined;
|
|
23
|
+
// ydb-sdk exposes dedicated error classes with server status codes.
|
|
24
|
+
// In practice, table-not-found can surface as:
|
|
25
|
+
// - NotFound (code 400140)
|
|
26
|
+
// - SchemeError (code 400070) with empty issues (observed in CI logs for describeTable)
|
|
27
|
+
if (ctorName === "NotFound" || statusCode === 400140) {
|
|
28
|
+
return true;
|
|
48
29
|
}
|
|
49
|
-
|
|
50
|
-
|
|
30
|
+
if ((ctorName === "SchemeError" || statusCode === 400070) &&
|
|
31
|
+
/:\s*\[\s*\]\s*$/i.test(msg)) {
|
|
32
|
+
return true;
|
|
51
33
|
}
|
|
34
|
+
return (/table.*not found/i.test(msg) ||
|
|
35
|
+
/path.*not found/i.test(msg) ||
|
|
36
|
+
/does not exist/i.test(msg));
|
|
52
37
|
}
|
|
53
|
-
|
|
54
|
-
|
|
38
|
+
function isAlreadyExistsError(err) {
|
|
39
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
40
|
+
return /already exists/i.test(msg) || /path.*exists/i.test(msg);
|
|
41
|
+
}
|
|
42
|
+
async function ensureMetaTableOnce() {
|
|
43
|
+
await withSession(async (s) => {
|
|
44
|
+
let tableDescription = null;
|
|
45
|
+
try {
|
|
46
|
+
tableDescription = await s.describeTable("qdr__collections");
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (isTableNotFoundError(err)) {
|
|
50
|
+
throwMigrationRequired("Metadata table qdr__collections does not exist; please create it before starting the service");
|
|
51
|
+
}
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
54
|
+
// Table exists: validate required columns.
|
|
55
|
+
const columns = tableDescription.columns ?? [];
|
|
56
|
+
const hasLastAccessedAt = columns.some((col) => col.name === "last_accessed_at");
|
|
57
|
+
if (!hasLastAccessedAt) {
|
|
58
|
+
throwMigrationRequired("Metadata table qdr__collections is missing required column last_accessed_at; please recreate the table or apply a manual schema migration before starting the service");
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
metaTableReady = true;
|
|
62
|
+
}
|
|
63
|
+
export async function ensureMetaTable() {
|
|
64
|
+
if (metaTableReady) {
|
|
55
65
|
return;
|
|
56
66
|
}
|
|
67
|
+
if (metaTableReadyInFlight) {
|
|
68
|
+
await metaTableReadyInFlight;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
metaTableReadyInFlight = ensureMetaTableOnce();
|
|
72
|
+
try {
|
|
73
|
+
await metaTableReadyInFlight;
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
metaTableReadyInFlight = null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function ensureGlobalPointsTableOnce() {
|
|
57
80
|
await withSession(async (s) => {
|
|
58
81
|
let tableDescription = null;
|
|
59
82
|
try {
|
|
60
83
|
tableDescription = await s.describeTable(GLOBAL_POINTS_TABLE);
|
|
61
84
|
}
|
|
62
|
-
catch {
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (!isTableNotFoundError(err)) {
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
63
89
|
// Table doesn't exist, create it with all columns using the new schema and
|
|
64
90
|
// auto-partitioning enabled.
|
|
65
91
|
const desc = new TableDescription()
|
|
@@ -70,35 +96,43 @@ export async function ensureGlobalPointsTable() {
|
|
|
70
96
|
partitioningBySize: Ydb.FeatureFlag.Status.ENABLED,
|
|
71
97
|
partitionSizeMb: 100,
|
|
72
98
|
});
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
99
|
+
try {
|
|
100
|
+
await s.createTable(GLOBAL_POINTS_TABLE, desc);
|
|
101
|
+
logger.info(`created global points table ${GLOBAL_POINTS_TABLE}`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
catch (createErr) {
|
|
105
|
+
// Race-safe: another concurrent caller might have created the table.
|
|
106
|
+
if (!isAlreadyExistsError(createErr)) {
|
|
107
|
+
throw createErr;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// If the table already exists (race), fall through to a fresh describe +
|
|
111
|
+
// schema validation.
|
|
112
|
+
tableDescription = await s.describeTable(GLOBAL_POINTS_TABLE);
|
|
77
113
|
}
|
|
78
114
|
// Table exists, require the new embedding_quantized column.
|
|
79
115
|
const columns = tableDescription.columns ?? [];
|
|
80
116
|
const hasEmbeddingQuantized = columns.some((col) => col.name === "embedding_quantized");
|
|
81
117
|
if (!hasEmbeddingQuantized) {
|
|
82
|
-
|
|
83
|
-
throwMigrationRequired(`Global points table ${GLOBAL_POINTS_TABLE} is missing required column embedding_quantized; apply the migration (e.g., ALTER TABLE ${GLOBAL_POINTS_TABLE} RENAME COLUMN embedding_bit TO embedding_quantized) or set YDB_QDRANT_GLOBAL_POINTS_AUTOMIGRATE=true after backup to allow automatic migration.`);
|
|
84
|
-
}
|
|
85
|
-
const alterDdl = `
|
|
86
|
-
ALTER TABLE ${GLOBAL_POINTS_TABLE}
|
|
87
|
-
ADD COLUMN embedding_quantized String;
|
|
88
|
-
`;
|
|
89
|
-
// NOTE: Same rationale as in ensureMetaTable: executeSchemeQuery is not part of
|
|
90
|
-
// the public TableSession TypeScript surface, so we reach into the underlying
|
|
91
|
-
// ydb-sdk implementation (verified with ydb-sdk v5.11.1) to apply schema changes.
|
|
92
|
-
// If future SDK versions alter this shape, this cast and migration path must be
|
|
93
|
-
// updated accordingly.
|
|
94
|
-
const rawSession = s;
|
|
95
|
-
await rawSession.api.executeSchemeQuery({
|
|
96
|
-
sessionId: rawSession.sessionId,
|
|
97
|
-
yqlText: alterDdl,
|
|
98
|
-
});
|
|
99
|
-
logger.info(`added embedding_quantized column to existing table ${GLOBAL_POINTS_TABLE}`);
|
|
118
|
+
throwMigrationRequired(`Global points table ${GLOBAL_POINTS_TABLE} is missing required column embedding_quantized; please recreate the table or apply a manual schema migration before starting the service`);
|
|
100
119
|
}
|
|
101
|
-
// Mark table ready after schema checks/migrations succeed.
|
|
102
|
-
globalPointsTableReady = true;
|
|
103
120
|
});
|
|
104
121
|
}
|
|
122
|
+
export async function ensureGlobalPointsTable() {
|
|
123
|
+
if (globalPointsTableReady) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (globalPointsTableReadyInFlight) {
|
|
127
|
+
await globalPointsTableReadyInFlight;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
globalPointsTableReadyInFlight = ensureGlobalPointsTableOnce();
|
|
131
|
+
try {
|
|
132
|
+
await globalPointsTableReadyInFlight;
|
|
133
|
+
globalPointsTableReady = true;
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
globalPointsTableReadyInFlight = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ydb-qdrant",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"main": "dist/package/api.js",
|
|
5
5
|
"types": "dist/package/api.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -65,11 +65,8 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@bufbuild/protobuf": "^2.10.0",
|
|
67
67
|
"@grpc/grpc-js": "^1.14.0",
|
|
68
|
+
"@qdrant/js-client-rest": "^1.16.2",
|
|
68
69
|
"@yandex-cloud/nodejs-sdk": "^2.9.0",
|
|
69
|
-
"@ydbjs/api": "^6.0.4",
|
|
70
|
-
"@ydbjs/core": "^6.0.4",
|
|
71
|
-
"@ydbjs/query": "^6.0.4",
|
|
72
|
-
"@ydbjs/value": "^6.0.4",
|
|
73
70
|
"dotenv": "^17.2.3",
|
|
74
71
|
"express": "^5.1.0",
|
|
75
72
|
"nice-grpc": "^2.1.13",
|
|
@@ -91,4 +88,4 @@
|
|
|
91
88
|
"typescript-eslint": "^8.47.0",
|
|
92
89
|
"vitest": "^4.0.12"
|
|
93
90
|
}
|
|
94
|
-
}
|
|
91
|
+
}
|