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.
Files changed (39) hide show
  1. package/dist/config/env.d.ts +0 -3
  2. package/dist/config/env.js +0 -17
  3. package/dist/package/api.d.ts +3 -0
  4. package/dist/qdrant/QdrantRestTypes.d.ts +35 -0
  5. package/dist/qdrant/QdrantRestTypes.js +1 -0
  6. package/dist/repositories/collectionsRepo.one-table.js +37 -63
  7. package/dist/repositories/collectionsRepo.shared.js +8 -2
  8. package/dist/repositories/pointsRepo.d.ts +5 -11
  9. package/dist/repositories/pointsRepo.js +6 -3
  10. package/dist/repositories/pointsRepo.one-table/Delete.d.ts +2 -0
  11. package/dist/repositories/pointsRepo.one-table/Delete.js +166 -0
  12. package/dist/repositories/pointsRepo.one-table/PathSegmentsFilter.d.ts +14 -0
  13. package/dist/repositories/pointsRepo.one-table/PathSegmentsFilter.js +33 -0
  14. package/dist/repositories/pointsRepo.one-table/Search.d.ts +4 -0
  15. package/dist/repositories/pointsRepo.one-table/Search.js +208 -0
  16. package/dist/repositories/pointsRepo.one-table/Upsert.d.ts +2 -0
  17. package/dist/repositories/pointsRepo.one-table/Upsert.js +85 -0
  18. package/dist/repositories/pointsRepo.one-table.d.ts +3 -13
  19. package/dist/repositories/pointsRepo.one-table.js +3 -403
  20. package/dist/routes/points.js +17 -4
  21. package/dist/server.d.ts +1 -0
  22. package/dist/server.js +70 -2
  23. package/dist/services/CollectionService.d.ts +9 -0
  24. package/dist/services/CollectionService.js +9 -0
  25. package/dist/services/PointsService.d.ts +3 -10
  26. package/dist/services/PointsService.js +73 -3
  27. package/dist/types.d.ts +59 -5
  28. package/dist/types.js +27 -3
  29. package/dist/utils/normalization.d.ts +1 -0
  30. package/dist/utils/normalization.js +2 -1
  31. package/dist/utils/vectorBinary.js +94 -10
  32. package/dist/ydb/bootstrapMetaTable.d.ts +7 -0
  33. package/dist/ydb/bootstrapMetaTable.js +75 -0
  34. package/dist/ydb/client.d.ts +10 -3
  35. package/dist/ydb/client.js +26 -2
  36. package/dist/ydb/helpers.d.ts +0 -2
  37. package/dist/ydb/helpers.js +0 -7
  38. package/dist/ydb/schema.js +100 -66
  39. package/package.json +3 -6
@@ -1,12 +1,68 @@
1
1
  import { UpsertPointsReq, SearchReq, DeletePointsReq } from "../types.js";
2
2
  import { ensureMetaTable } from "../ydb/schema.js";
3
3
  import { getCollectionMeta, touchCollectionLastAccess, } from "../repositories/collectionsRepo.js";
4
- import { deletePoints as repoDeletePoints, searchPoints as repoSearchPoints, upsertPoints as repoUpsertPoints, } from "../repositories/pointsRepo.js";
4
+ import { deletePoints as repoDeletePoints, deletePointsByPathSegments as repoDeletePointsByPathSegments, searchPoints as repoSearchPoints, upsertPoints as repoUpsertPoints, } from "../repositories/pointsRepo.js";
5
5
  import { logger } from "../logging/logger.js";
6
6
  import { QdrantServiceError, isVectorDimensionMismatchError, } from "./errors.js";
7
7
  import { normalizeCollectionContextShared } from "./CollectionService.shared.js";
8
8
  import { resolvePointsTableAndUidOneTable } from "./CollectionService.one-table.js";
9
9
  import { normalizeSearchBodyForSearch, normalizeSearchBodyForQuery, } from "../utils/normalization.js";
10
+ function parsePathSegmentsFilterToPaths(filter) {
11
+ const extractMust = (must) => {
12
+ if (!Array.isArray(must) || must.length === 0)
13
+ return null;
14
+ const pairs = [];
15
+ for (const cond of must) {
16
+ if (typeof cond !== "object" || cond === null)
17
+ return null;
18
+ const c = cond;
19
+ if (typeof c.key !== "string")
20
+ return null;
21
+ const m = /^pathSegments\.(\d+)$/.exec(c.key);
22
+ if (!m)
23
+ return null;
24
+ const idx = Number(m[1]);
25
+ if (!Number.isInteger(idx) || idx < 0)
26
+ return null;
27
+ if (typeof c.match !== "object" || c.match === null)
28
+ return null;
29
+ const match = c.match;
30
+ if (typeof match.value !== "string")
31
+ return null;
32
+ pairs.push({ idx, value: match.value });
33
+ }
34
+ pairs.sort((a, b) => a.idx - b.idx);
35
+ // Require contiguous indexes starting from 0 to avoid ambiguous matches.
36
+ for (let i = 0; i < pairs.length; i += 1) {
37
+ if (pairs[i].idx !== i)
38
+ return null;
39
+ }
40
+ return pairs.map((p) => p.value);
41
+ };
42
+ if (typeof filter !== "object" || filter === null)
43
+ return null;
44
+ const f = filter;
45
+ if (f.must !== undefined) {
46
+ const path = extractMust(f.must);
47
+ return path ? [path] : null;
48
+ }
49
+ if (f.should !== undefined) {
50
+ if (!Array.isArray(f.should) || f.should.length === 0)
51
+ return null;
52
+ const paths = [];
53
+ for (const g of f.should) {
54
+ if (typeof g !== "object" || g === null)
55
+ return null;
56
+ const group = g;
57
+ const path = extractMust(group.must);
58
+ if (!path)
59
+ return null;
60
+ paths.push(path);
61
+ }
62
+ return paths;
63
+ }
64
+ return null;
65
+ }
10
66
  export async function upsertPoints(ctx, body) {
11
67
  await ensureMetaTable();
12
68
  const normalized = normalizeCollectionContextShared(ctx.tenant, ctx.collection, ctx.apiKey, ctx.userAgent);
@@ -89,9 +145,10 @@ async function executeSearch(ctx, normalizedSearch, source) {
89
145
  distance: meta.distance,
90
146
  vectorType: meta.vectorType,
91
147
  }, `${source}: executing`);
148
+ const filterPaths = parsePathSegmentsFilterToPaths(normalizedSearch.filter);
92
149
  let hits;
93
150
  try {
94
- hits = await repoSearchPoints(tableName, parsed.data.vector, parsed.data.top, parsed.data.with_payload, meta.distance, meta.dimension, uid);
151
+ hits = await repoSearchPoints(tableName, parsed.data.vector, parsed.data.top, parsed.data.with_payload, meta.distance, meta.dimension, uid, filterPaths ?? undefined);
95
152
  }
96
153
  catch (err) {
97
154
  if (isVectorDimensionMismatchError(err)) {
@@ -163,7 +220,20 @@ export async function deletePoints(ctx, body) {
163
220
  });
164
221
  }
165
222
  const { tableName, uid } = await resolvePointsTableAndUidOneTable(normalized);
166
- const deleted = await repoDeletePoints(tableName, parsed.data.points, uid);
223
+ let deleted;
224
+ if ("points" in parsed.data) {
225
+ deleted = await repoDeletePoints(tableName, parsed.data.points, uid);
226
+ }
227
+ else {
228
+ const paths = parsePathSegmentsFilterToPaths(parsed.data.filter);
229
+ if (!paths) {
230
+ throw new QdrantServiceError(400, {
231
+ status: "error",
232
+ error: "unsupported delete filter: only pathSegments.N match filters with must/should are supported",
233
+ });
234
+ }
235
+ deleted = await repoDeletePointsByPathSegments(tableName, uid, paths);
236
+ }
167
237
  await touchCollectionLastAccess(normalized.metaKey);
168
238
  return { deleted };
169
239
  }
package/dist/types.d.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import { z } from "zod";
2
- export type DistanceKind = "Cosine" | "Euclid" | "Dot" | "Manhattan";
2
+ import type { QdrantDistance, QdrantPayload, QdrantWithPayloadInterface, YdbQdrantPointId, YdbQdrantUpsertPoint } from "./qdrant/QdrantRestTypes.js";
3
+ export type DistanceKind = QdrantDistance;
3
4
  export type VectorType = "float";
5
+ export type Payload = QdrantPayload;
6
+ export type WithPayload = QdrantWithPayloadInterface;
4
7
  /**
5
8
  * Collection metadata from qdr__collections table.
6
9
  *
@@ -25,16 +28,67 @@ export declare const CreateCollectionReq: z.ZodObject<{
25
28
  }, z.core.$strip>;
26
29
  export declare const UpsertPointsReq: z.ZodObject<{
27
30
  points: z.ZodArray<z.ZodObject<{
28
- id: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
31
+ id: z.ZodType<YdbQdrantPointId>;
29
32
  vector: z.ZodArray<z.ZodNumber>;
30
- payload: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
33
+ payload: z.ZodType<Payload | undefined>;
31
34
  }, z.core.$strip>>;
32
35
  }, z.core.$strip>;
36
+ export type UpsertPoint = YdbQdrantUpsertPoint;
37
+ export type UpsertPointsBody = {
38
+ points: UpsertPoint[];
39
+ };
33
40
  export declare const SearchReq: z.ZodObject<{
34
41
  vector: z.ZodArray<z.ZodNumber>;
35
42
  top: z.ZodNumber;
36
43
  with_payload: z.ZodOptional<z.ZodBoolean>;
37
44
  }, z.core.$strip>;
38
- export declare const DeletePointsReq: z.ZodObject<{
39
- points: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
45
+ export type SearchPointsBody = {
46
+ vector: number[];
47
+ top?: number;
48
+ limit?: number;
49
+ with_payload?: WithPayload;
50
+ score_threshold?: number | null;
51
+ };
52
+ export declare const DeletePointsByIdsReq: z.ZodObject<{
53
+ points: z.ZodArray<z.ZodType<YdbQdrantPointId, unknown, z.core.$ZodTypeInternals<YdbQdrantPointId, unknown>>>;
40
54
  }, z.core.$strip>;
55
+ export declare const DeletePointsByFilterReq: z.ZodObject<{
56
+ filter: z.ZodUnion<readonly [z.ZodObject<{
57
+ must: z.ZodArray<z.ZodObject<{
58
+ key: z.ZodString;
59
+ match: z.ZodObject<{
60
+ value: z.ZodString;
61
+ }, z.core.$strip>;
62
+ }, z.core.$strip>>;
63
+ }, z.core.$strip>, z.ZodObject<{
64
+ should: z.ZodArray<z.ZodObject<{
65
+ must: z.ZodArray<z.ZodObject<{
66
+ key: z.ZodString;
67
+ match: z.ZodObject<{
68
+ value: z.ZodString;
69
+ }, z.core.$strip>;
70
+ }, z.core.$strip>>;
71
+ }, z.core.$strip>>;
72
+ }, z.core.$strip>]>;
73
+ }, z.core.$strip>;
74
+ export declare const DeletePointsReq: z.ZodUnion<readonly [z.ZodObject<{
75
+ points: z.ZodArray<z.ZodType<YdbQdrantPointId, unknown, z.core.$ZodTypeInternals<YdbQdrantPointId, unknown>>>;
76
+ }, z.core.$strip>, z.ZodObject<{
77
+ filter: z.ZodUnion<readonly [z.ZodObject<{
78
+ must: z.ZodArray<z.ZodObject<{
79
+ key: z.ZodString;
80
+ match: z.ZodObject<{
81
+ value: z.ZodString;
82
+ }, z.core.$strip>;
83
+ }, z.core.$strip>>;
84
+ }, z.core.$strip>, z.ZodObject<{
85
+ should: z.ZodArray<z.ZodObject<{
86
+ must: z.ZodArray<z.ZodObject<{
87
+ key: z.ZodString;
88
+ match: z.ZodObject<{
89
+ value: z.ZodString;
90
+ }, z.core.$strip>;
91
+ }, z.core.$strip>>;
92
+ }, z.core.$strip>>;
93
+ }, z.core.$strip>]>;
94
+ }, z.core.$strip>]>;
package/dist/types.js CHANGED
@@ -16,7 +16,7 @@ export const UpsertPointsReq = z.object({
16
16
  .array(z.object({
17
17
  id: z.union([z.string(), z.number()]),
18
18
  vector: z.array(z.number()),
19
- payload: z.record(z.string(), z.any()).optional(),
19
+ payload: z.record(z.string(), z.unknown()).optional(),
20
20
  }))
21
21
  .min(1),
22
22
  });
@@ -25,6 +25,30 @@ export const SearchReq = z.object({
25
25
  top: z.number().int().positive().max(1000),
26
26
  with_payload: z.boolean().optional(),
27
27
  });
28
- export const DeletePointsReq = z.object({
29
- points: z.array(z.union([z.string(), z.number()])).min(1),
28
+ export const DeletePointsByIdsReq = z.object({
29
+ points: z
30
+ .array(z.union([z.string(), z.number()]))
31
+ .min(1),
32
+ });
33
+ const DeletePointsFilterCondition = z.object({
34
+ key: z.string(),
35
+ match: z.object({
36
+ value: z.string(),
37
+ }),
38
+ });
39
+ const DeletePointsFilterMust = z.object({
40
+ must: z.array(DeletePointsFilterCondition).min(1),
41
+ });
42
+ const DeletePointsFilter = z.union([
43
+ DeletePointsFilterMust,
44
+ z.object({
45
+ should: z.array(DeletePointsFilterMust).min(1),
46
+ }),
47
+ ]);
48
+ export const DeletePointsByFilterReq = z.object({
49
+ filter: DeletePointsFilter,
30
50
  });
51
+ export const DeletePointsReq = z.union([
52
+ DeletePointsByIdsReq,
53
+ DeletePointsByFilterReq,
54
+ ]);
@@ -3,6 +3,7 @@ export interface SearchNormalizationResult {
3
3
  top: number | undefined;
4
4
  withPayload: boolean | undefined;
5
5
  scoreThreshold: number | undefined;
6
+ filter?: unknown;
6
7
  }
7
8
  export declare function isNumberArray(value: unknown): value is number[];
8
9
  export declare function extractVectorLoose(body: unknown, depth?: number): number[] | undefined;
@@ -74,6 +74,7 @@ function normalizeSearchCommon(b, vector) {
74
74
  const topFromTop = typeof rawTop === "number" ? rawTop : undefined;
75
75
  const topFromLimit = typeof rawLimit === "number" ? rawLimit : undefined;
76
76
  const top = topFromTop ?? topFromLimit;
77
+ const filter = b["filter"];
77
78
  let withPayload;
78
79
  const rawWithPayload = b["with_payload"];
79
80
  if (typeof rawWithPayload === "boolean") {
@@ -88,5 +89,5 @@ function normalizeSearchCommon(b, vector) {
88
89
  const scoreThreshold = Number.isFinite(thresholdValue)
89
90
  ? thresholdValue
90
91
  : undefined;
91
- return { vector, top, withPayload, scoreThreshold };
92
+ return { vector, top, withPayload, scoreThreshold, filter };
92
93
  }
@@ -1,3 +1,4 @@
1
+ const IS_LITTLE_ENDIAN = new Uint8Array(new Uint16Array([0x00ff]).buffer)[0] === 0xff;
1
2
  export function vectorToFloatBinary(vector) {
2
3
  if (vector.length === 0) {
3
4
  return Buffer.from([1]);
@@ -8,28 +9,111 @@ export function vectorToFloatBinary(vector) {
8
9
  if (!Number.isFinite(value)) {
9
10
  throw new Error(`Non-finite value in vector at index ${i}: ${value}`);
10
11
  }
11
- buffer.writeFloatLE(value, i * 4);
12
+ if (IS_LITTLE_ENDIAN) {
13
+ buffer.writeFloatLE(value, i * 4);
14
+ }
15
+ else {
16
+ buffer.writeFloatBE(value, i * 4);
17
+ }
12
18
  }
13
19
  buffer.writeUInt8(1, vector.length * 4);
14
20
  return buffer;
15
21
  }
16
22
  export function vectorToBitBinary(vector) {
17
- if (vector.length === 0) {
18
- return Buffer.from([10]);
19
- }
20
- const byteCount = Math.ceil(vector.length / 8);
21
- const buffer = Buffer.alloc(byteCount + 1);
23
+ // Mirrors YDB's TKnnBitVectorSerializer (Knn::ToBinaryStringBit) layout:
24
+ // - Packed bits as integers written in native endianness
25
+ // - Then 1 byte: count of unused bits in the last data byte
26
+ // - Then 1 byte: format marker (10 = BitVector)
27
+ //
28
+ // Source: https://raw.githubusercontent.com/ydb-platform/ydb/0b506f56e399e0b4e6a6a4267799da68a3164bf7/ydb/library/yql/udfs/common/knn/knn-serializer.h
29
+ const bitLen = vector.length;
30
+ const dataByteLen = Math.ceil(bitLen / 8);
31
+ const totalLen = dataByteLen + 2; // +1 unused-bit-count +1 format marker
32
+ const buffer = Buffer.alloc(totalLen);
33
+ let offset = 0;
34
+ const writeU64 = (v) => {
35
+ if (IS_LITTLE_ENDIAN) {
36
+ buffer.writeBigUInt64LE(v, offset);
37
+ }
38
+ else {
39
+ buffer.writeBigUInt64BE(v, offset);
40
+ }
41
+ offset += 8;
42
+ };
43
+ const writeU32 = (v) => {
44
+ if (IS_LITTLE_ENDIAN) {
45
+ buffer.writeUInt32LE(v, offset);
46
+ }
47
+ else {
48
+ buffer.writeUInt32BE(v, offset);
49
+ }
50
+ offset += 4;
51
+ };
52
+ const writeU16 = (v) => {
53
+ if (IS_LITTLE_ENDIAN) {
54
+ buffer.writeUInt16LE(v, offset);
55
+ }
56
+ else {
57
+ buffer.writeUInt16BE(v, offset);
58
+ }
59
+ offset += 2;
60
+ };
61
+ const writeU8 = (v) => {
62
+ buffer.writeUInt8(v, offset);
63
+ offset += 1;
64
+ };
65
+ let accumulator = 0n;
66
+ let filledBits = 0;
22
67
  for (let i = 0; i < vector.length; i += 1) {
23
68
  const value = vector[i];
24
69
  if (!Number.isFinite(value)) {
25
70
  throw new Error(`Non-finite value in vector at index ${i}: ${value}`);
26
71
  }
27
72
  if (value > 0) {
28
- const byteIndex = Math.floor(i / 8);
29
- const bitIndex = i % 8;
30
- buffer[byteIndex] |= 1 << bitIndex;
73
+ accumulator |= 1n;
31
74
  }
75
+ filledBits += 1;
76
+ if (filledBits === 64) {
77
+ writeU64(accumulator);
78
+ accumulator = 0n;
79
+ filledBits = 0;
80
+ }
81
+ accumulator <<= 1n;
32
82
  }
33
- buffer.writeUInt8(10, byteCount);
83
+ accumulator >>= 1n;
84
+ filledBits += 7;
85
+ const tailWriteIf = (bits) => {
86
+ if (filledBits < bits) {
87
+ return;
88
+ }
89
+ if (bits === 64) {
90
+ writeU64(accumulator & 0xffffffffffffffffn);
91
+ filledBits -= 64;
92
+ return;
93
+ }
94
+ if (bits === 32) {
95
+ writeU32(Number(accumulator & 0xffffffffn));
96
+ accumulator >>= 32n;
97
+ filledBits -= 32;
98
+ return;
99
+ }
100
+ if (bits === 16) {
101
+ writeU16(Number(accumulator & 0xffffn));
102
+ accumulator >>= 16n;
103
+ filledBits -= 16;
104
+ return;
105
+ }
106
+ writeU8(Number(accumulator & 0xffn));
107
+ accumulator >>= 8n;
108
+ filledBits -= 8;
109
+ };
110
+ tailWriteIf(64);
111
+ tailWriteIf(32);
112
+ tailWriteIf(16);
113
+ tailWriteIf(8);
114
+ // After tail writes, we must have < 8 "filledBits" left.
115
+ const unusedBitsInLastByte = 7 - filledBits;
116
+ writeU8(unusedBitsInLastByte);
117
+ writeU8(10);
34
118
  return buffer;
35
119
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Creates the qdr__collections metadata table if it does not exist.
3
+ *
4
+ * Intended for CI and local dev environments where a fresh YDB is provisioned
5
+ * and schema creation is handled out-of-band from `ensureMetaTable()`.
6
+ */
7
+ export declare function bootstrapMetaTable(): Promise<void>;
@@ -0,0 +1,75 @@
1
+ import { Column, TableDescription, Types, destroyDriver, readyOrThrow, withSession, } from "./client.js";
2
+ function isTableNotFoundError(err) {
3
+ const msg = err instanceof Error ? err.message : String(err);
4
+ return (/table.*not found/i.test(msg) ||
5
+ /path.*not found/i.test(msg) ||
6
+ /does not exist/i.test(msg) ||
7
+ /NotFound\s*\(code\s*400140\)/i.test(msg) ||
8
+ (/SchemeError\s*\(code\s*400070\)/i.test(msg) &&
9
+ /:\s*\[\s*\]\s*$/i.test(msg)));
10
+ }
11
+ function isAlreadyExistsError(err) {
12
+ const msg = err instanceof Error ? err.message : String(err);
13
+ return /already exists/i.test(msg) || /path.*exists/i.test(msg);
14
+ }
15
+ /**
16
+ * Creates the qdr__collections metadata table if it does not exist.
17
+ *
18
+ * Intended for CI and local dev environments where a fresh YDB is provisioned
19
+ * and schema creation is handled out-of-band from `ensureMetaTable()`.
20
+ */
21
+ export async function bootstrapMetaTable() {
22
+ await readyOrThrow();
23
+ await withSession(async (s) => {
24
+ let desc = null;
25
+ try {
26
+ desc = await s.describeTable("qdr__collections");
27
+ }
28
+ catch (err) {
29
+ if (!isTableNotFoundError(err)) {
30
+ throw err;
31
+ }
32
+ const td = new TableDescription()
33
+ .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))
34
+ .withPrimaryKey("collection");
35
+ try {
36
+ await s.createTable("qdr__collections", td);
37
+ console.log("bootstrapMetaTable: created qdr__collections");
38
+ }
39
+ catch (createErr) {
40
+ if (!isAlreadyExistsError(createErr)) {
41
+ throw createErr;
42
+ }
43
+ }
44
+ desc = await s.describeTable("qdr__collections");
45
+ }
46
+ const cols = desc?.columns ?? [];
47
+ const hasLastAccessedAt = cols.some((c) => c.name === "last_accessed_at");
48
+ if (!hasLastAccessedAt) {
49
+ throw new Error("bootstrapMetaTable: qdr__collections exists but is missing required column last_accessed_at");
50
+ }
51
+ });
52
+ }
53
+ // CLI entrypoint
54
+ if (process.argv[1]?.endsWith("bootstrapMetaTable.js")) {
55
+ const main = async () => {
56
+ await bootstrapMetaTable();
57
+ // Important: ydb-sdk driver/session pool keeps timers alive; explicitly
58
+ // destroy it so this script can terminate in CI steps.
59
+ await destroyDriver();
60
+ };
61
+ main()
62
+ .then(() => {
63
+ process.exit(0);
64
+ })
65
+ .catch(async (err) => {
66
+ console.error(err);
67
+ try {
68
+ await destroyDriver();
69
+ }
70
+ catch {
71
+ // ignore cleanup errors
72
+ }
73
+ process.exit(1);
74
+ });
75
+ }
@@ -1,6 +1,6 @@
1
- import type { Session, IAuthService, ExecuteQuerySettings as YdbExecuteQuerySettings } from "ydb-sdk";
2
- declare const Types: typeof import("ydb-sdk").Types, TypedValues: typeof import("ydb-sdk").TypedValues, TableDescription: typeof import("ydb-sdk").TableDescription, Column: typeof import("ydb-sdk").Column, ExecuteQuerySettings: typeof YdbExecuteQuerySettings, Ydb: typeof import("ydb-sdk-proto").Ydb;
3
- export { Types, TypedValues, TableDescription, Column, ExecuteQuerySettings, Ydb, };
1
+ import type { Session, IAuthService, ExecuteQuerySettings as YdbExecuteQuerySettings, BulkUpsertSettings as YdbBulkUpsertSettings, QuerySession } from "ydb-sdk";
2
+ declare const Types: typeof import("ydb-sdk").Types, TypedValues: typeof import("ydb-sdk").TypedValues, TableDescription: typeof import("ydb-sdk").TableDescription, Column: typeof import("ydb-sdk").Column, ExecuteQuerySettings: typeof YdbExecuteQuerySettings, BulkUpsertSettings: typeof YdbBulkUpsertSettings, Ydb: typeof import("ydb-sdk-proto").Ydb;
3
+ export { Types, TypedValues, TableDescription, Column, ExecuteQuerySettings, BulkUpsertSettings, Ydb, };
4
4
  export declare function createExecuteQuerySettings(options?: {
5
5
  keepInCache?: boolean;
6
6
  idempotent?: boolean;
@@ -10,6 +10,9 @@ export declare function createExecuteQuerySettingsWithTimeout(options: {
10
10
  idempotent?: boolean;
11
11
  timeoutMs: number;
12
12
  }): YdbExecuteQuerySettings;
13
+ export declare function createBulkUpsertSettingsWithTimeout(options: {
14
+ timeoutMs: number;
15
+ }): YdbBulkUpsertSettings;
13
16
  type DriverConfig = {
14
17
  endpoint?: string;
15
18
  database?: string;
@@ -23,6 +26,10 @@ export declare function __resetRefreshStateForTests(): void;
23
26
  export declare function configureDriver(config: DriverConfig): void;
24
27
  export declare function readyOrThrow(): Promise<void>;
25
28
  export declare function withSession<T>(fn: (s: Session) => Promise<T>): Promise<T>;
29
+ export declare function withQuerySession<T>(fn: (s: QuerySession) => Promise<T>, options?: {
30
+ timeoutMs?: number;
31
+ idempotent?: boolean;
32
+ }): Promise<T>;
26
33
  export declare function withStartupProbeSession<T>(fn: (s: Session) => Promise<T>): Promise<T>;
27
34
  export declare function isYdbAvailable(timeoutMs?: number): Promise<boolean>;
28
35
  /**
@@ -2,8 +2,8 @@ import { createRequire } from "module";
2
2
  import { YDB_DATABASE, YDB_ENDPOINT, SESSION_POOL_MIN_SIZE, SESSION_POOL_MAX_SIZE, SESSION_KEEPALIVE_PERIOD_MS, STARTUP_PROBE_SESSION_TIMEOUT_MS, } from "../config/env.js";
3
3
  import { logger } from "../logging/logger.js";
4
4
  const require = createRequire(import.meta.url);
5
- const { Driver, getCredentialsFromEnv, Types, TypedValues, TableDescription, Column, ExecuteQuerySettings, OperationParams, Ydb, } = require("ydb-sdk");
6
- export { Types, TypedValues, TableDescription, Column, ExecuteQuerySettings, Ydb, };
5
+ const { Driver, getCredentialsFromEnv, Types, TypedValues, TableDescription, Column, ExecuteQuerySettings, BulkUpsertSettings, OperationParams, Ydb, } = require("ydb-sdk");
6
+ export { Types, TypedValues, TableDescription, Column, ExecuteQuerySettings, BulkUpsertSettings, Ydb, };
7
7
  export function createExecuteQuerySettings(options) {
8
8
  const { keepInCache = true, idempotent = true } = options ?? {};
9
9
  const settings = new ExecuteQuerySettings();
@@ -26,6 +26,15 @@ export function createExecuteQuerySettingsWithTimeout(options) {
26
26
  settings.withOperationParams(op);
27
27
  return settings;
28
28
  }
29
+ export function createBulkUpsertSettingsWithTimeout(options) {
30
+ const settings = new BulkUpsertSettings();
31
+ const op = new OperationParams();
32
+ const seconds = Math.max(1, Math.ceil(options.timeoutMs / 1000));
33
+ op.withOperationTimeoutSeconds(seconds);
34
+ op.withCancelAfterSeconds(seconds);
35
+ settings.withOperationParams(op);
36
+ return settings;
37
+ }
29
38
  const DRIVER_READY_TIMEOUT_MS = 15000;
30
39
  const TABLE_SESSION_TIMEOUT_MS = 20000;
31
40
  const YDB_HEALTHCHECK_READY_TIMEOUT_MS = 5000;
@@ -167,6 +176,21 @@ export async function withSession(fn) {
167
176
  throw err;
168
177
  }
169
178
  }
179
+ export async function withQuerySession(fn, options) {
180
+ const d = getOrCreateDriver();
181
+ try {
182
+ return await d.queryClient.do({
183
+ fn,
184
+ timeout: options?.timeoutMs ?? TABLE_SESSION_TIMEOUT_MS,
185
+ idempotent: options?.idempotent,
186
+ });
187
+ }
188
+ catch (err) {
189
+ // Query sessions can also get stuck/busy; reuse the same driver refresh path.
190
+ void maybeRefreshDriverOnSessionError(err);
191
+ throw err;
192
+ }
193
+ }
170
194
  export async function withStartupProbeSession(fn) {
171
195
  const d = getOrCreateDriver();
172
196
  try {
@@ -1,5 +1,3 @@
1
- export declare function buildVectorParam(vector: number[]): import("ydb-sdk-proto").Ydb.ITypedValue;
2
- export declare function buildJsonOrEmpty(payload?: Record<string, unknown>): import("ydb-sdk-proto").Ydb.ITypedValue;
3
1
  export declare function buildVectorBinaryParams(vector: number[]): {
4
2
  float: Buffer<ArrayBufferLike>;
5
3
  bit: Buffer<ArrayBufferLike>;
@@ -1,11 +1,4 @@
1
- import { Types, TypedValues } from "./client.js";
2
1
  import { vectorToFloatBinary, vectorToBitBinary, } from "../utils/vectorBinary.js";
3
- export function buildVectorParam(vector) {
4
- return TypedValues.list(Types.FLOAT, vector);
5
- }
6
- export function buildJsonOrEmpty(payload) {
7
- return TypedValues.jsonDocument(JSON.stringify(payload ?? {}));
8
- }
9
2
  export function buildVectorBinaryParams(vector) {
10
3
  return {
11
4
  float: vectorToFloatBinary(vector),