@functional-systems/lambdadb 0.3.4-dev.0 → 0.4.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 +50 -7
- package/dist/commonjs/compat/qdrant/client.d.ts +210 -0
- package/dist/commonjs/compat/qdrant/client.d.ts.map +1 -0
- package/dist/commonjs/compat/qdrant/client.js +573 -0
- package/dist/commonjs/compat/qdrant/client.js.map +1 -0
- package/dist/commonjs/compat/qdrant/conversions.d.ts +40 -0
- package/dist/commonjs/compat/qdrant/conversions.d.ts.map +1 -0
- package/dist/commonjs/compat/qdrant/conversions.js +386 -0
- package/dist/commonjs/compat/qdrant/conversions.js.map +1 -0
- package/dist/commonjs/compat/qdrant/errors.d.ts +10 -0
- package/dist/commonjs/compat/qdrant/errors.d.ts.map +1 -0
- package/dist/commonjs/compat/qdrant/errors.js +25 -0
- package/dist/commonjs/compat/qdrant/errors.js.map +1 -0
- package/dist/commonjs/compat/qdrant/filters.d.ts +10 -0
- package/dist/commonjs/compat/qdrant/filters.d.ts.map +1 -0
- package/dist/commonjs/compat/qdrant/filters.js +172 -0
- package/dist/commonjs/compat/qdrant/filters.js.map +1 -0
- package/dist/commonjs/compat/qdrant/models.d.ts +198 -0
- package/dist/commonjs/compat/qdrant/models.d.ts.map +1 -0
- package/dist/commonjs/compat/qdrant/models.js +205 -0
- package/dist/commonjs/compat/qdrant/models.js.map +1 -0
- package/dist/commonjs/compat/qdrant.d.ts +4 -0
- package/dist/commonjs/compat/qdrant.d.ts.map +1 -0
- package/dist/commonjs/compat/qdrant.js +45 -0
- package/dist/commonjs/compat/qdrant.js.map +1 -0
- package/dist/commonjs/models/indexconfigsunion.d.ts +2 -2
- package/dist/commonjs/models/indexconfigsunion.d.ts.map +1 -1
- package/dist/commonjs/models/indexconfigsunion.js +4 -4
- package/dist/commonjs/models/indexconfigsunion.js.map +1 -1
- package/dist/esm/compat/qdrant/client.d.ts +210 -0
- package/dist/esm/compat/qdrant/client.d.ts.map +1 -0
- package/dist/esm/compat/qdrant/client.js +536 -0
- package/dist/esm/compat/qdrant/client.js.map +1 -0
- package/dist/esm/compat/qdrant/conversions.d.ts +40 -0
- package/dist/esm/compat/qdrant/conversions.d.ts.map +1 -0
- package/dist/esm/compat/qdrant/conversions.js +336 -0
- package/dist/esm/compat/qdrant/conversions.js.map +1 -0
- package/dist/esm/compat/qdrant/errors.d.ts +10 -0
- package/dist/esm/compat/qdrant/errors.d.ts.map +1 -0
- package/dist/esm/compat/qdrant/errors.js +19 -0
- package/dist/esm/compat/qdrant/errors.js.map +1 -0
- package/dist/esm/compat/qdrant/filters.d.ts +10 -0
- package/dist/esm/compat/qdrant/filters.d.ts.map +1 -0
- package/dist/esm/compat/qdrant/filters.js +169 -0
- package/dist/esm/compat/qdrant/filters.js.map +1 -0
- package/dist/esm/compat/qdrant/models.d.ts +198 -0
- package/dist/esm/compat/qdrant/models.d.ts.map +1 -0
- package/dist/esm/compat/qdrant/models.js +185 -0
- package/dist/esm/compat/qdrant/models.js.map +1 -0
- package/dist/esm/compat/qdrant.d.ts +4 -0
- package/dist/esm/compat/qdrant.d.ts.map +1 -0
- package/dist/esm/compat/qdrant.js +4 -0
- package/dist/esm/compat/qdrant.js.map +1 -0
- package/dist/esm/models/indexconfigsunion.d.ts +2 -2
- package/dist/esm/models/indexconfigsunion.d.ts.map +1 -1
- package/dist/esm/models/indexconfigsunion.js +4 -4
- package/dist/esm/models/indexconfigsunion.js.map +1 -1
- package/jsr.json +3 -1
- package/package.json +32 -1
- package/src/compat/qdrant/client.ts +901 -0
- package/src/compat/qdrant/conversions.ts +422 -0
- package/src/compat/qdrant/errors.ts +20 -0
- package/src/compat/qdrant/filters.ts +213 -0
- package/src/compat/qdrant/models.ts +317 -0
- package/src/compat/qdrant.ts +3 -0
- package/src/models/indexconfigsunion.ts +6 -6
- package/test/integration/qdrant-compat-external.test.mjs +226 -0
- package/test/integration/qdrant-compat-live.test.mjs +122 -0
- package/test/public-api.test.mjs +145 -4
- package/test/qdrant-compat.test.mjs +402 -0
package/test/public-api.test.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
BadRequestError,
|
|
6
6
|
HTTPClient,
|
|
7
7
|
LambdaDBClient,
|
|
8
|
+
SDKValidationError,
|
|
8
9
|
} from "../dist/esm/index.js";
|
|
9
10
|
|
|
10
11
|
const BASE_URL = "https://api.test";
|
|
@@ -147,15 +148,155 @@ test("public client supports managed embedding vector index configs", async () =
|
|
|
147
148
|
indexConfigs: managedIndexConfigs,
|
|
148
149
|
});
|
|
149
150
|
|
|
151
|
+
assert.equal(calls.length, 1);
|
|
152
|
+
assert.deepEqual(
|
|
153
|
+
result.collection.indexConfigs.bodyEmbedding.embedding,
|
|
154
|
+
{
|
|
155
|
+
...managedIndexConfigs.bodyEmbedding.embedding,
|
|
156
|
+
dimensions: 1536,
|
|
157
|
+
similarity: "cosine",
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("managed embedding vector config supports optional embedding dimensions and similarity", async () => {
|
|
163
|
+
const { calls, client } = createClient((call) => {
|
|
164
|
+
assert.equal(call.method, "POST");
|
|
165
|
+
assert.deepEqual(JSON.parse(call.body), {
|
|
166
|
+
collectionName: "semantic-items",
|
|
167
|
+
indexConfigs: {
|
|
168
|
+
bodyEmbedding: {
|
|
169
|
+
type: "vector",
|
|
170
|
+
managedEmbedding: true,
|
|
171
|
+
embedding: {
|
|
172
|
+
provider: "openai",
|
|
173
|
+
model: "text-embedding-3-small",
|
|
174
|
+
sourceField: "body",
|
|
175
|
+
dimensions: 1536,
|
|
176
|
+
similarity: "cosine",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return jsonResponse(
|
|
183
|
+
{
|
|
184
|
+
collection: collectionFixture("semantic-items", {
|
|
185
|
+
indexConfigs: {
|
|
186
|
+
bodyEmbedding: {
|
|
187
|
+
type: "vector",
|
|
188
|
+
managedEmbedding: true,
|
|
189
|
+
embedding: {
|
|
190
|
+
provider: "openai",
|
|
191
|
+
model: "text-embedding-3-small",
|
|
192
|
+
sourceField: "body",
|
|
193
|
+
dimensions: 1536,
|
|
194
|
+
similarity: "cosine",
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
}),
|
|
199
|
+
},
|
|
200
|
+
{ status: 202 },
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const result = await client.createCollection({
|
|
205
|
+
collectionName: "semantic-items",
|
|
206
|
+
indexConfigs: {
|
|
207
|
+
bodyEmbedding: {
|
|
208
|
+
type: "vector",
|
|
209
|
+
managedEmbedding: true,
|
|
210
|
+
embedding: {
|
|
211
|
+
provider: "openai",
|
|
212
|
+
model: "text-embedding-3-small",
|
|
213
|
+
sourceField: "body",
|
|
214
|
+
dimensions: 1536,
|
|
215
|
+
similarity: "cosine",
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
|
|
150
221
|
assert.equal(calls.length, 1);
|
|
151
222
|
assert.equal(
|
|
152
223
|
result.collection.indexConfigs.bodyEmbedding.embedding.dimensions,
|
|
153
224
|
1536,
|
|
154
225
|
);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
)
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("managed embedding vector config rejects invalid Java contract shapes", async () => {
|
|
229
|
+
const { calls, client } = createClient(() => {
|
|
230
|
+
throw new Error("request should not be sent for invalid index configs");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const invalidIndexConfigs = [
|
|
234
|
+
{
|
|
235
|
+
type: "vector",
|
|
236
|
+
dimensions: 1536,
|
|
237
|
+
embedding: {
|
|
238
|
+
provider: "openai",
|
|
239
|
+
model: "text-embedding-3-small",
|
|
240
|
+
sourceField: "body",
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
type: "vector",
|
|
245
|
+
managedEmbedding: false,
|
|
246
|
+
dimensions: 1536,
|
|
247
|
+
embedding: {
|
|
248
|
+
provider: "openai",
|
|
249
|
+
model: "text-embedding-3-small",
|
|
250
|
+
sourceField: "body",
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
type: "vector",
|
|
255
|
+
managedEmbedding: true,
|
|
256
|
+
dimensions: 1536,
|
|
257
|
+
embedding: {
|
|
258
|
+
provider: "openai",
|
|
259
|
+
model: "text-embedding-3-small",
|
|
260
|
+
sourceField: "body",
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
type: "vector",
|
|
265
|
+
managedEmbedding: true,
|
|
266
|
+
similarity: "cosine",
|
|
267
|
+
embedding: {
|
|
268
|
+
provider: "openai",
|
|
269
|
+
model: "text-embedding-3-small",
|
|
270
|
+
sourceField: "body",
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
type: "vector",
|
|
275
|
+
managedEmbedding: true,
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
type: "vector",
|
|
279
|
+
managedEmbedding: true,
|
|
280
|
+
embedding: {
|
|
281
|
+
provider: "openai",
|
|
282
|
+
model: "text-embedding-3-small",
|
|
283
|
+
sourceField: "body",
|
|
284
|
+
unsupported: true,
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
for (const indexConfig of invalidIndexConfigs) {
|
|
290
|
+
const result = await client.createCollectionSafe({
|
|
291
|
+
collectionName: "invalid-vector",
|
|
292
|
+
indexConfigs: { bodyEmbedding: indexConfig },
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
assert.equal(result.ok, false);
|
|
296
|
+
assert.ok(result.error instanceof SDKValidationError);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
assert.equal(calls.length, 0);
|
|
159
300
|
});
|
|
160
301
|
|
|
161
302
|
test("collection query supports managed embedding queryText KNN payloads", async () => {
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
QdrantClient,
|
|
6
|
+
QdrantCompatClient,
|
|
7
|
+
UnsupportedQdrantFeatureError,
|
|
8
|
+
models,
|
|
9
|
+
} from "../dist/esm/compat/qdrant.js";
|
|
10
|
+
import { PointStruct } from "../dist/esm/compat/qdrant/models.js";
|
|
11
|
+
|
|
12
|
+
class FakeDocs {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.upserts = [];
|
|
15
|
+
this.deletes = [];
|
|
16
|
+
this.fetches = [];
|
|
17
|
+
this.lists = [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async upsert(body) {
|
|
21
|
+
this.upserts.push(body);
|
|
22
|
+
return { message: "ok" };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async fetch(body) {
|
|
26
|
+
this.fetches.push(body);
|
|
27
|
+
return {
|
|
28
|
+
docs: [
|
|
29
|
+
{
|
|
30
|
+
collection: "docs",
|
|
31
|
+
doc: {
|
|
32
|
+
id: "1",
|
|
33
|
+
_qdrant_id: 1,
|
|
34
|
+
_qdrant_vector: [0.1, 0.2],
|
|
35
|
+
tenant: "acme",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async delete(body) {
|
|
43
|
+
this.deletes.push(body);
|
|
44
|
+
return { message: "ok" };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async list(body = {}) {
|
|
48
|
+
this.lists.push(body);
|
|
49
|
+
return {
|
|
50
|
+
docs: [
|
|
51
|
+
{
|
|
52
|
+
collection: "docs",
|
|
53
|
+
doc: { id: "1", _qdrant_id: 1, tenant: "acme" },
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
nextPageToken: undefined,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class FakeCollection {
|
|
62
|
+
constructor(owner, name) {
|
|
63
|
+
this.owner = owner;
|
|
64
|
+
this.name = name;
|
|
65
|
+
this.docs = new FakeDocs();
|
|
66
|
+
this.queries = [];
|
|
67
|
+
this.updates = [];
|
|
68
|
+
this.deletes = [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async get() {
|
|
72
|
+
this.owner.gets.push({ collectionName: this.name });
|
|
73
|
+
return {
|
|
74
|
+
collection: {
|
|
75
|
+
collectionName: this.name,
|
|
76
|
+
indexConfigs: this.owner.indexConfigs,
|
|
77
|
+
numDocs: this.owner.numDocs,
|
|
78
|
+
collectionStatus: "ACTIVE",
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async update(body) {
|
|
84
|
+
this.updates.push(body);
|
|
85
|
+
this.owner.indexConfigs = body.indexConfigs;
|
|
86
|
+
return { collection: { collectionName: this.name, indexConfigs: body.indexConfigs } };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async delete() {
|
|
90
|
+
this.deletes.push({});
|
|
91
|
+
return { message: "ok" };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async query(body) {
|
|
95
|
+
this.queries.push(body);
|
|
96
|
+
return {
|
|
97
|
+
docs: [
|
|
98
|
+
{
|
|
99
|
+
collection: this.name,
|
|
100
|
+
score: 0.9,
|
|
101
|
+
doc: {
|
|
102
|
+
id: "1",
|
|
103
|
+
_qdrant_id: 1,
|
|
104
|
+
_qdrant_vector: [0.1, 0.2],
|
|
105
|
+
tenant: "acme",
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
class FakeLambdaDB {
|
|
114
|
+
constructor() {
|
|
115
|
+
this.created = [];
|
|
116
|
+
this.gets = [];
|
|
117
|
+
this.indexConfigs = {};
|
|
118
|
+
this.numDocs = 0;
|
|
119
|
+
this.collectionsByName = new Map();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async createCollection(body) {
|
|
123
|
+
this.created.push(body);
|
|
124
|
+
this.indexConfigs = body.indexConfigs ?? {};
|
|
125
|
+
return { collection: { collectionName: body.collectionName } };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
collection(name) {
|
|
129
|
+
if (!this.collectionsByName.has(name)) {
|
|
130
|
+
this.collectionsByName.set(name, new FakeCollection(this, name));
|
|
131
|
+
}
|
|
132
|
+
return this.collectionsByName.get(name);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
test("qdrant compatibility exports public client and models", () => {
|
|
137
|
+
assert.equal(QdrantClient, QdrantCompatClient);
|
|
138
|
+
assert.equal(models.PointStruct, PointStruct);
|
|
139
|
+
|
|
140
|
+
const point = new models.PointStruct({
|
|
141
|
+
id: 1,
|
|
142
|
+
vector: [0.1, 0.2],
|
|
143
|
+
payload: { tenant: "acme" },
|
|
144
|
+
});
|
|
145
|
+
assert.equal(point.id, 1);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("createCollection maps vector params and payload schema", async () => {
|
|
149
|
+
const fake = new FakeLambdaDB();
|
|
150
|
+
const client = new QdrantCompatClient(fake);
|
|
151
|
+
|
|
152
|
+
assert.equal(
|
|
153
|
+
await client.createCollection("docs", {
|
|
154
|
+
vectorsConfig: new models.VectorParams({
|
|
155
|
+
size: 3,
|
|
156
|
+
distance: models.Distance.DOT,
|
|
157
|
+
}),
|
|
158
|
+
payloadSchema: {
|
|
159
|
+
tenant: models.PayloadSchemaType.KEYWORD,
|
|
160
|
+
views: models.PayloadSchemaType.INTEGER,
|
|
161
|
+
score: models.PayloadSchemaType.FLOAT,
|
|
162
|
+
active: models.PayloadSchemaType.BOOL,
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
true,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
assert.deepEqual(fake.created, [
|
|
169
|
+
{
|
|
170
|
+
collectionName: "docs",
|
|
171
|
+
indexConfigs: {
|
|
172
|
+
_qdrant_vector: {
|
|
173
|
+
type: "vector",
|
|
174
|
+
dimensions: 3,
|
|
175
|
+
similarity: "dot_product",
|
|
176
|
+
},
|
|
177
|
+
tenant: { type: "keyword" },
|
|
178
|
+
views: { type: "long" },
|
|
179
|
+
score: { type: "double" },
|
|
180
|
+
active: { type: "boolean" },
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
]);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("createPayloadIndex merges existing configs and rejects non-empty backfill", async () => {
|
|
187
|
+
const fake = new FakeLambdaDB();
|
|
188
|
+
fake.indexConfigs = {
|
|
189
|
+
_qdrant_vector: {
|
|
190
|
+
type: "vector",
|
|
191
|
+
dimensions: 3,
|
|
192
|
+
similarity: "cosine",
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
const client = new QdrantCompatClient(fake);
|
|
196
|
+
|
|
197
|
+
assert.equal(
|
|
198
|
+
await client.createPayloadIndex(
|
|
199
|
+
"docs",
|
|
200
|
+
"tenant",
|
|
201
|
+
models.PayloadSchemaType.KEYWORD,
|
|
202
|
+
),
|
|
203
|
+
true,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
assert.deepEqual(fake.collection("docs").updates, [
|
|
207
|
+
{
|
|
208
|
+
indexConfigs: {
|
|
209
|
+
_qdrant_vector: {
|
|
210
|
+
type: "vector",
|
|
211
|
+
dimensions: 3,
|
|
212
|
+
similarity: "cosine",
|
|
213
|
+
},
|
|
214
|
+
tenant: { type: "keyword" },
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
fake.numDocs = 3;
|
|
220
|
+
await assert.rejects(
|
|
221
|
+
client.createPayloadIndex("docs", "views", models.PayloadSchemaType.INTEGER),
|
|
222
|
+
UnsupportedQdrantFeatureError,
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("upsert maps qdrant points to LambdaDB documents", async () => {
|
|
227
|
+
const fake = new FakeLambdaDB();
|
|
228
|
+
const client = new QdrantCompatClient(fake);
|
|
229
|
+
|
|
230
|
+
const result = await client.upsert("docs", {
|
|
231
|
+
points: [
|
|
232
|
+
new models.PointStruct({
|
|
233
|
+
id: 1,
|
|
234
|
+
vector: [0.1, 0.2],
|
|
235
|
+
payload: { tenant: "acme" },
|
|
236
|
+
}),
|
|
237
|
+
],
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
assert.equal(result.status, models.UpdateStatus.COMPLETED);
|
|
241
|
+
assert.deepEqual(fake.collection("docs").docs.upserts, [
|
|
242
|
+
{
|
|
243
|
+
docs: [
|
|
244
|
+
{
|
|
245
|
+
id: "1",
|
|
246
|
+
_qdrant_id: 1,
|
|
247
|
+
_qdrant_vector: [0.1, 0.2],
|
|
248
|
+
tenant: "acme",
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
await assert.rejects(
|
|
255
|
+
client.upsert("docs", {
|
|
256
|
+
points: [{ id: 1, vector: [0.1], payload: { _qdrant_vector: [0.2] } }],
|
|
257
|
+
}),
|
|
258
|
+
/reserved prefix/,
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
await assert.rejects(
|
|
262
|
+
client.upsert("docs", {
|
|
263
|
+
points: [
|
|
264
|
+
{
|
|
265
|
+
id: 1,
|
|
266
|
+
vector: {
|
|
267
|
+
sparse: new models.SparseVector({ indices: [1], values: [0.9] }),
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
}),
|
|
272
|
+
UnsupportedQdrantFeatureError,
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test("queryPoints maps dense vectors, filters, and responses", async () => {
|
|
277
|
+
const fake = new FakeLambdaDB();
|
|
278
|
+
const client = new QdrantCompatClient(fake);
|
|
279
|
+
|
|
280
|
+
const response = await client.queryPoints("docs", {
|
|
281
|
+
query: [0.1, 0.2],
|
|
282
|
+
queryFilter: new models.Filter({
|
|
283
|
+
must: [
|
|
284
|
+
new models.FieldCondition({
|
|
285
|
+
key: "tenant",
|
|
286
|
+
match: new models.MatchValue({ value: "acme" }),
|
|
287
|
+
}),
|
|
288
|
+
],
|
|
289
|
+
mustNot: [
|
|
290
|
+
new models.FieldCondition({
|
|
291
|
+
key: "status",
|
|
292
|
+
match: new models.MatchValue({ value: "deleted" }),
|
|
293
|
+
}),
|
|
294
|
+
],
|
|
295
|
+
}),
|
|
296
|
+
limit: 5,
|
|
297
|
+
withVectors: true,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
assert.equal(response.points[0].id, 1);
|
|
301
|
+
assert.equal(response.points[0].score, 0.9);
|
|
302
|
+
assert.deepEqual(response.points[0].payload, { tenant: "acme" });
|
|
303
|
+
assert.deepEqual(response.points[0].vector, [0.1, 0.2]);
|
|
304
|
+
assert.deepEqual(fake.collection("docs").queries, [
|
|
305
|
+
{
|
|
306
|
+
query: {
|
|
307
|
+
knn: {
|
|
308
|
+
field: "_qdrant_vector",
|
|
309
|
+
k: 5,
|
|
310
|
+
queryVector: [0.1, 0.2],
|
|
311
|
+
filter: {
|
|
312
|
+
bool: [
|
|
313
|
+
{ queryString: { query: "tenant:acme" }, occur: "filter" },
|
|
314
|
+
{ queryString: { query: "status:deleted" }, occur: "must_not" },
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
size: 5,
|
|
320
|
+
consistentRead: true,
|
|
321
|
+
includeVectors: true,
|
|
322
|
+
},
|
|
323
|
+
]);
|
|
324
|
+
|
|
325
|
+
await assert.rejects(
|
|
326
|
+
client.queryPoints("docs", { query: [0.1], offset: 1 }),
|
|
327
|
+
UnsupportedQdrantFeatureError,
|
|
328
|
+
);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("retrieve, delete, scroll, count, and getCollection return qdrant-style objects", async () => {
|
|
332
|
+
const fake = new FakeLambdaDB();
|
|
333
|
+
fake.indexConfigs = {
|
|
334
|
+
_qdrant_vector: {
|
|
335
|
+
type: "vector",
|
|
336
|
+
dimensions: 2,
|
|
337
|
+
similarity: "cosine",
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
fake.numDocs = 12;
|
|
341
|
+
const client = new QdrantCompatClient(fake);
|
|
342
|
+
|
|
343
|
+
const collection = await client.getCollection("docs");
|
|
344
|
+
assert.equal(collection.config.params.vectors.size, 2);
|
|
345
|
+
assert.equal(collection.config.params.vectors.distance, models.Distance.COSINE);
|
|
346
|
+
|
|
347
|
+
const records = await client.retrieve("docs", { ids: [1], withVectors: true });
|
|
348
|
+
assert.equal(records[0].id, 1);
|
|
349
|
+
assert.deepEqual(records[0].payload, { tenant: "acme" });
|
|
350
|
+
assert.deepEqual(records[0].vector, [0.1, 0.2]);
|
|
351
|
+
assert.deepEqual(fake.collection("docs").docs.fetches, [
|
|
352
|
+
{ ids: ["1"], consistentRead: true, includeVectors: true },
|
|
353
|
+
]);
|
|
354
|
+
|
|
355
|
+
const deleteResult = await client.delete("docs", { pointsSelector: [1] });
|
|
356
|
+
assert.equal(deleteResult.status, models.UpdateStatus.COMPLETED);
|
|
357
|
+
assert.deepEqual(fake.collection("docs").docs.deletes, [{ ids: ["1"] }]);
|
|
358
|
+
|
|
359
|
+
await client.delete("docs", {
|
|
360
|
+
filter: new models.Filter({
|
|
361
|
+
must: [
|
|
362
|
+
new models.FieldCondition({
|
|
363
|
+
key: "tenant",
|
|
364
|
+
match: new models.MatchValue({ value: "acme" }),
|
|
365
|
+
}),
|
|
366
|
+
],
|
|
367
|
+
}),
|
|
368
|
+
});
|
|
369
|
+
await client.delete("docs", {
|
|
370
|
+
pointsSelector: {
|
|
371
|
+
filter: {
|
|
372
|
+
must: [
|
|
373
|
+
{
|
|
374
|
+
key: "status",
|
|
375
|
+
match: { value: "archived" },
|
|
376
|
+
},
|
|
377
|
+
],
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
assert.deepEqual(fake.collection("docs").docs.deletes, [
|
|
382
|
+
{ ids: ["1"] },
|
|
383
|
+
{
|
|
384
|
+
filter: {
|
|
385
|
+
queryString: { query: "tenant:acme" },
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
filter: {
|
|
390
|
+
queryString: { query: "status:archived" },
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
]);
|
|
394
|
+
|
|
395
|
+
const [scrollRecords, nextOffset] = await client.scroll("docs", { limit: 3 });
|
|
396
|
+
assert.equal(scrollRecords[0].id, 1);
|
|
397
|
+
assert.equal(nextOffset, undefined);
|
|
398
|
+
assert.deepEqual(fake.collection("docs").docs.lists, [{ size: 3 }]);
|
|
399
|
+
|
|
400
|
+
const count = await client.count("docs");
|
|
401
|
+
assert.equal(count.count, 12);
|
|
402
|
+
});
|