@kernl-sdk/turbopuffer 0.1.3 → 0.1.5
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +12 -0
- package/dist/__tests__/integration/lifecycle.integration.test.js +1 -2
- package/dist/__tests__/integration/memory.integration.test.js +44 -1
- package/dist/__tests__/integration/query.integration.test.js +8 -7
- package/dist/handle.js +1 -1
- package/dist/search.js +1 -1
- package/package.json +7 -7
- package/src/__tests__/integration/lifecycle.integration.test.ts +1 -2
- package/src/__tests__/integration/memory.integration.test.ts +53 -1
- package/src/__tests__/integration/query.integration.test.ts +8 -9
- package/.turbo/turbo-check-types.log +0 -60
- package/dist/__tests__/filters.integration.test.d.ts +0 -8
- package/dist/__tests__/filters.integration.test.d.ts.map +0 -1
- package/dist/__tests__/filters.integration.test.js +0 -502
- package/dist/__tests__/integration.test.d.ts +0 -2
- package/dist/__tests__/integration.test.d.ts.map +0 -1
- package/dist/__tests__/integration.test.js +0 -343
- package/dist/__tests__/lifecycle.integration.test.d.ts +0 -8
- package/dist/__tests__/lifecycle.integration.test.d.ts.map +0 -1
- package/dist/__tests__/lifecycle.integration.test.js +0 -385
- package/dist/__tests__/query.integration.test.d.ts +0 -8
- package/dist/__tests__/query.integration.test.d.ts.map +0 -1
- package/dist/__tests__/query.integration.test.js +0 -423
- package/dist/convert.d.ts +0 -68
- package/dist/convert.d.ts.map +0 -1
- package/dist/convert.js +0 -333
|
@@ -1,502 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Comprehensive filter integration tests.
|
|
3
|
-
*
|
|
4
|
-
* Tests all filter operators against real Turbopuffer API with a
|
|
5
|
-
* deterministic dataset.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
8
|
-
import { TurbopufferSearchIndex } from "../search.js";
|
|
9
|
-
const TURBOPUFFER_API_KEY = process.env.TURBOPUFFER_API_KEY;
|
|
10
|
-
const TURBOPUFFER_REGION = process.env.TURBOPUFFER_REGION ?? "api";
|
|
11
|
-
/**
|
|
12
|
-
* Helper to create a DenseVector.
|
|
13
|
-
*/
|
|
14
|
-
function vec(values) {
|
|
15
|
-
return { kind: "vector", values };
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Deterministic test dataset.
|
|
19
|
-
*
|
|
20
|
-
* 10 documents with varied field values for comprehensive filter testing.
|
|
21
|
-
*/
|
|
22
|
-
const TEST_DOCS = [
|
|
23
|
-
{
|
|
24
|
-
id: "doc-01",
|
|
25
|
-
fields: {
|
|
26
|
-
num: 10,
|
|
27
|
-
flag: true,
|
|
28
|
-
status: "active",
|
|
29
|
-
tags: ["important", "urgent"],
|
|
30
|
-
name: "alice_smith",
|
|
31
|
-
optionalField: "present",
|
|
32
|
-
vector: vec([0.1, 0.0, 0.0, 0.0]),
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: "doc-02",
|
|
37
|
-
fields: {
|
|
38
|
-
num: 20,
|
|
39
|
-
flag: false,
|
|
40
|
-
status: "active",
|
|
41
|
-
tags: ["normal"],
|
|
42
|
-
name: "bob_jones",
|
|
43
|
-
optionalField: null,
|
|
44
|
-
vector: vec([0.0, 0.1, 0.0, 0.0]),
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: "doc-03",
|
|
49
|
-
fields: {
|
|
50
|
-
num: 30,
|
|
51
|
-
flag: true,
|
|
52
|
-
status: "pending",
|
|
53
|
-
tags: ["important"],
|
|
54
|
-
name: "charlie_smith",
|
|
55
|
-
optionalField: "also_present",
|
|
56
|
-
vector: vec([0.0, 0.0, 0.1, 0.0]),
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
id: "doc-04",
|
|
61
|
-
fields: {
|
|
62
|
-
num: 40,
|
|
63
|
-
flag: false,
|
|
64
|
-
status: "pending",
|
|
65
|
-
tags: ["normal", "review"],
|
|
66
|
-
name: "diana_brown",
|
|
67
|
-
optionalField: null, // intentionally null
|
|
68
|
-
vector: vec([0.0, 0.0, 0.0, 0.1]),
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id: "doc-05",
|
|
73
|
-
fields: {
|
|
74
|
-
num: 50,
|
|
75
|
-
flag: true,
|
|
76
|
-
status: "deleted",
|
|
77
|
-
tags: ["archived"],
|
|
78
|
-
name: "eve_johnson",
|
|
79
|
-
optionalField: "value",
|
|
80
|
-
vector: vec([0.1, 0.1, 0.0, 0.0]),
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
id: "doc-06",
|
|
85
|
-
fields: {
|
|
86
|
-
num: 0,
|
|
87
|
-
flag: false,
|
|
88
|
-
status: "active",
|
|
89
|
-
tags: [],
|
|
90
|
-
name: "frank_miller",
|
|
91
|
-
optionalField: null,
|
|
92
|
-
vector: vec([0.0, 0.1, 0.1, 0.0]),
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
id: "doc-07",
|
|
97
|
-
fields: {
|
|
98
|
-
num: -10,
|
|
99
|
-
flag: true,
|
|
100
|
-
status: "active",
|
|
101
|
-
tags: ["urgent", "critical"],
|
|
102
|
-
name: "grace_wilson",
|
|
103
|
-
optionalField: "exists",
|
|
104
|
-
vector: vec([0.0, 0.0, 0.1, 0.1]),
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
id: "doc-08",
|
|
109
|
-
fields: {
|
|
110
|
-
num: 100,
|
|
111
|
-
flag: false,
|
|
112
|
-
status: "deleted",
|
|
113
|
-
tags: ["archived", "old"],
|
|
114
|
-
name: "henry_davis",
|
|
115
|
-
// optionalField intentionally omitted
|
|
116
|
-
vector: vec([0.1, 0.0, 0.1, 0.0]),
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
id: "doc-09",
|
|
121
|
-
fields: {
|
|
122
|
-
num: 25,
|
|
123
|
-
flag: true,
|
|
124
|
-
status: "pending",
|
|
125
|
-
tags: ["important", "review"],
|
|
126
|
-
name: "ivy_taylor",
|
|
127
|
-
optionalField: "set",
|
|
128
|
-
vector: vec([0.0, 0.1, 0.0, 0.1]),
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
id: "doc-10",
|
|
133
|
-
fields: {
|
|
134
|
-
num: 35,
|
|
135
|
-
flag: false,
|
|
136
|
-
status: "active",
|
|
137
|
-
tags: ["normal"],
|
|
138
|
-
name: "jack_anderson",
|
|
139
|
-
optionalField: null,
|
|
140
|
-
vector: vec([0.1, 0.0, 0.0, 0.1]),
|
|
141
|
-
},
|
|
142
|
-
},
|
|
143
|
-
];
|
|
144
|
-
// Standard query vector for all filter tests
|
|
145
|
-
const QUERY_VECTOR = [0.1, 0.1, 0.1, 0.1];
|
|
146
|
-
describe("Filter integration tests", () => {
|
|
147
|
-
if (!TURBOPUFFER_API_KEY) {
|
|
148
|
-
it.skip("requires TURBOPUFFER_API_KEY to be set", () => { });
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
let tpuf;
|
|
152
|
-
let index;
|
|
153
|
-
const testIndexId = `kernl-filter-test-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
154
|
-
beforeAll(async () => {
|
|
155
|
-
tpuf = new TurbopufferSearchIndex({
|
|
156
|
-
apiKey: TURBOPUFFER_API_KEY,
|
|
157
|
-
region: TURBOPUFFER_REGION,
|
|
158
|
-
});
|
|
159
|
-
// Create index with schema
|
|
160
|
-
await tpuf.createIndex({
|
|
161
|
-
id: testIndexId,
|
|
162
|
-
schema: {
|
|
163
|
-
num: { type: "int", filterable: true },
|
|
164
|
-
flag: { type: "boolean", filterable: true },
|
|
165
|
-
status: { type: "string", filterable: true },
|
|
166
|
-
tags: { type: "string[]", filterable: true },
|
|
167
|
-
name: { type: "string", filterable: true },
|
|
168
|
-
optionalField: { type: "string", filterable: true },
|
|
169
|
-
vector: { type: "vector", dimensions: 4 },
|
|
170
|
-
},
|
|
171
|
-
});
|
|
172
|
-
index = tpuf.index(testIndexId);
|
|
173
|
-
// Insert test documents
|
|
174
|
-
await index.upsert(TEST_DOCS);
|
|
175
|
-
// Wait for indexing
|
|
176
|
-
await new Promise((r) => setTimeout(r, 1000));
|
|
177
|
-
}, 30000);
|
|
178
|
-
afterAll(async () => {
|
|
179
|
-
try {
|
|
180
|
-
await tpuf.deleteIndex(testIndexId);
|
|
181
|
-
}
|
|
182
|
-
catch {
|
|
183
|
-
// Ignore cleanup errors
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
/**
|
|
187
|
-
* Helper to query and return sorted IDs.
|
|
188
|
-
*/
|
|
189
|
-
async function queryIds(filter) {
|
|
190
|
-
const hits = await index.query({
|
|
191
|
-
query: [{ vector: QUERY_VECTOR }],
|
|
192
|
-
topK: 100,
|
|
193
|
-
filter,
|
|
194
|
-
});
|
|
195
|
-
return hits.map((h) => h.id).sort();
|
|
196
|
-
}
|
|
197
|
-
// ============================================================
|
|
198
|
-
// EQUALITY OPERATORS
|
|
199
|
-
// ============================================================
|
|
200
|
-
describe("equality operators", () => {
|
|
201
|
-
it("$eq on string field", async () => {
|
|
202
|
-
const ids = await queryIds({ status: "active" });
|
|
203
|
-
expect(ids).toEqual(["doc-01", "doc-02", "doc-06", "doc-07", "doc-10"]);
|
|
204
|
-
});
|
|
205
|
-
it("$eq on number field", async () => {
|
|
206
|
-
const ids = await queryIds({ num: 30 });
|
|
207
|
-
expect(ids).toEqual(["doc-03"]);
|
|
208
|
-
});
|
|
209
|
-
it("$eq on boolean true", async () => {
|
|
210
|
-
const ids = await queryIds({ flag: true });
|
|
211
|
-
expect(ids).toEqual(["doc-01", "doc-03", "doc-05", "doc-07", "doc-09"]);
|
|
212
|
-
});
|
|
213
|
-
it("$eq on boolean false", async () => {
|
|
214
|
-
const ids = await queryIds({ flag: false });
|
|
215
|
-
expect(ids).toEqual(["doc-02", "doc-04", "doc-06", "doc-08", "doc-10"]);
|
|
216
|
-
});
|
|
217
|
-
it("$eq on zero", async () => {
|
|
218
|
-
const ids = await queryIds({ num: 0 });
|
|
219
|
-
expect(ids).toEqual(["doc-06"]);
|
|
220
|
-
});
|
|
221
|
-
it("$eq on negative number", async () => {
|
|
222
|
-
const ids = await queryIds({ num: -10 });
|
|
223
|
-
expect(ids).toEqual(["doc-07"]);
|
|
224
|
-
});
|
|
225
|
-
it("$neq on string field", async () => {
|
|
226
|
-
const ids = await queryIds({ status: { $neq: "active" } });
|
|
227
|
-
expect(ids).toEqual(["doc-03", "doc-04", "doc-05", "doc-08", "doc-09"]);
|
|
228
|
-
});
|
|
229
|
-
it("$neq on boolean", async () => {
|
|
230
|
-
const ids = await queryIds({ flag: { $neq: true } });
|
|
231
|
-
expect(ids).toEqual(["doc-02", "doc-04", "doc-06", "doc-08", "doc-10"]);
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
|
-
// ============================================================
|
|
235
|
-
// COMPARISON OPERATORS
|
|
236
|
-
// ============================================================
|
|
237
|
-
describe("comparison operators", () => {
|
|
238
|
-
it("$gt on number", async () => {
|
|
239
|
-
const ids = await queryIds({ num: { $gt: 30 } });
|
|
240
|
-
expect(ids).toEqual(["doc-04", "doc-05", "doc-08", "doc-10"]);
|
|
241
|
-
});
|
|
242
|
-
it("$gte on number", async () => {
|
|
243
|
-
const ids = await queryIds({ num: { $gte: 30 } });
|
|
244
|
-
expect(ids).toEqual(["doc-03", "doc-04", "doc-05", "doc-08", "doc-10"]);
|
|
245
|
-
});
|
|
246
|
-
it("$lt on number", async () => {
|
|
247
|
-
const ids = await queryIds({ num: { $lt: 20 } });
|
|
248
|
-
expect(ids).toEqual(["doc-01", "doc-06", "doc-07"]);
|
|
249
|
-
});
|
|
250
|
-
it("$lte on number", async () => {
|
|
251
|
-
const ids = await queryIds({ num: { $lte: 20 } });
|
|
252
|
-
expect(ids).toEqual(["doc-01", "doc-02", "doc-06", "doc-07"]);
|
|
253
|
-
});
|
|
254
|
-
it("$gt with negative number", async () => {
|
|
255
|
-
const ids = await queryIds({ num: { $gt: -10 } });
|
|
256
|
-
expect(ids).toEqual([
|
|
257
|
-
"doc-01",
|
|
258
|
-
"doc-02",
|
|
259
|
-
"doc-03",
|
|
260
|
-
"doc-04",
|
|
261
|
-
"doc-05",
|
|
262
|
-
"doc-06",
|
|
263
|
-
"doc-08",
|
|
264
|
-
"doc-09",
|
|
265
|
-
"doc-10",
|
|
266
|
-
]);
|
|
267
|
-
});
|
|
268
|
-
it("$lt with zero", async () => {
|
|
269
|
-
const ids = await queryIds({ num: { $lt: 0 } });
|
|
270
|
-
expect(ids).toEqual(["doc-07"]);
|
|
271
|
-
});
|
|
272
|
-
it("range filter (gt + lt)", async () => {
|
|
273
|
-
const ids = await queryIds({ num: { $gt: 20, $lt: 40 } });
|
|
274
|
-
expect(ids).toEqual(["doc-03", "doc-09", "doc-10"]);
|
|
275
|
-
});
|
|
276
|
-
it("inclusive range (gte + lte)", async () => {
|
|
277
|
-
const ids = await queryIds({ num: { $gte: 20, $lte: 40 } });
|
|
278
|
-
expect(ids).toEqual(["doc-02", "doc-03", "doc-04", "doc-09", "doc-10"]);
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
// ============================================================
|
|
282
|
-
// SET MEMBERSHIP OPERATORS
|
|
283
|
-
// ============================================================
|
|
284
|
-
describe("set membership operators", () => {
|
|
285
|
-
it("$in with string values", async () => {
|
|
286
|
-
const ids = await queryIds({ status: { $in: ["active", "pending"] } });
|
|
287
|
-
expect(ids).toEqual([
|
|
288
|
-
"doc-01",
|
|
289
|
-
"doc-02",
|
|
290
|
-
"doc-03",
|
|
291
|
-
"doc-04",
|
|
292
|
-
"doc-06",
|
|
293
|
-
"doc-07",
|
|
294
|
-
"doc-09",
|
|
295
|
-
"doc-10",
|
|
296
|
-
]);
|
|
297
|
-
});
|
|
298
|
-
it("$in with single value", async () => {
|
|
299
|
-
const ids = await queryIds({ status: { $in: ["deleted"] } });
|
|
300
|
-
expect(ids).toEqual(["doc-05", "doc-08"]);
|
|
301
|
-
});
|
|
302
|
-
it("$in with number values", async () => {
|
|
303
|
-
const ids = await queryIds({ num: { $in: [10, 20, 30] } });
|
|
304
|
-
expect(ids).toEqual(["doc-01", "doc-02", "doc-03"]);
|
|
305
|
-
});
|
|
306
|
-
it("$nin with string values", async () => {
|
|
307
|
-
const ids = await queryIds({ status: { $nin: ["deleted"] } });
|
|
308
|
-
expect(ids).toEqual([
|
|
309
|
-
"doc-01",
|
|
310
|
-
"doc-02",
|
|
311
|
-
"doc-03",
|
|
312
|
-
"doc-04",
|
|
313
|
-
"doc-06",
|
|
314
|
-
"doc-07",
|
|
315
|
-
"doc-09",
|
|
316
|
-
"doc-10",
|
|
317
|
-
]);
|
|
318
|
-
});
|
|
319
|
-
it("$nin excludes multiple values", async () => {
|
|
320
|
-
const ids = await queryIds({ status: { $nin: ["active", "deleted"] } });
|
|
321
|
-
expect(ids).toEqual(["doc-03", "doc-04", "doc-09"]);
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
// ============================================================
|
|
325
|
-
// ARRAY OPERATORS
|
|
326
|
-
// ============================================================
|
|
327
|
-
describe("array operators", () => {
|
|
328
|
-
it("$contains on array field", async () => {
|
|
329
|
-
const ids = await queryIds({ tags: { $contains: "important" } });
|
|
330
|
-
expect(ids).toEqual(["doc-01", "doc-03", "doc-09"]);
|
|
331
|
-
});
|
|
332
|
-
it("$contains with different value", async () => {
|
|
333
|
-
const ids = await queryIds({ tags: { $contains: "urgent" } });
|
|
334
|
-
expect(ids).toEqual(["doc-01", "doc-07"]);
|
|
335
|
-
});
|
|
336
|
-
it("$contains with value not in any doc", async () => {
|
|
337
|
-
const ids = await queryIds({ tags: { $contains: "nonexistent" } });
|
|
338
|
-
expect(ids).toEqual([]);
|
|
339
|
-
});
|
|
340
|
-
});
|
|
341
|
-
// ============================================================
|
|
342
|
-
// STRING PATTERN OPERATORS
|
|
343
|
-
// ============================================================
|
|
344
|
-
describe("string pattern operators", () => {
|
|
345
|
-
it("$startsWith matches prefix", async () => {
|
|
346
|
-
const ids = await queryIds({ name: { $startsWith: "alice" } });
|
|
347
|
-
expect(ids).toEqual(["doc-01"]);
|
|
348
|
-
});
|
|
349
|
-
it("$startsWith with common prefix", async () => {
|
|
350
|
-
// All names ending with _smith or starting with common letters
|
|
351
|
-
const ids = await queryIds({ name: { $startsWith: "j" } });
|
|
352
|
-
expect(ids).toEqual(["doc-10"]); // jack_anderson
|
|
353
|
-
});
|
|
354
|
-
it("$endsWith matches suffix", async () => {
|
|
355
|
-
const ids = await queryIds({ name: { $endsWith: "_smith" } });
|
|
356
|
-
expect(ids).toEqual(["doc-01", "doc-03"]); // alice_smith, charlie_smith
|
|
357
|
-
});
|
|
358
|
-
it("$endsWith with different suffix", async () => {
|
|
359
|
-
const ids = await queryIds({ name: { $endsWith: "_jones" } });
|
|
360
|
-
expect(ids).toEqual(["doc-02"]); // bob_jones
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
// ============================================================
|
|
364
|
-
// EXISTENCE OPERATORS
|
|
365
|
-
// ============================================================
|
|
366
|
-
describe("existence operators", () => {
|
|
367
|
-
it("$exists: true finds docs with non-null field", async () => {
|
|
368
|
-
const ids = await queryIds({ optionalField: { $exists: true } });
|
|
369
|
-
// docs with optionalField set to a string value (not null, not missing)
|
|
370
|
-
expect(ids).toEqual(["doc-01", "doc-03", "doc-05", "doc-07", "doc-09"]);
|
|
371
|
-
});
|
|
372
|
-
it("$exists: false finds docs with null or missing field", async () => {
|
|
373
|
-
const ids = await queryIds({ optionalField: { $exists: false } });
|
|
374
|
-
// docs where optionalField is null or missing
|
|
375
|
-
expect(ids).toEqual(["doc-02", "doc-04", "doc-06", "doc-08", "doc-10"]);
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
// ============================================================
|
|
379
|
-
// LOGICAL OPERATORS
|
|
380
|
-
// ============================================================
|
|
381
|
-
describe("logical operators", () => {
|
|
382
|
-
it("implicit AND with multiple fields", async () => {
|
|
383
|
-
const ids = await queryIds({ status: "active", flag: true });
|
|
384
|
-
expect(ids).toEqual(["doc-01", "doc-07"]);
|
|
385
|
-
});
|
|
386
|
-
it("$and with two conditions", async () => {
|
|
387
|
-
const ids = await queryIds({
|
|
388
|
-
$and: [{ status: "active" }, { num: { $gte: 0 } }],
|
|
389
|
-
});
|
|
390
|
-
expect(ids).toEqual(["doc-01", "doc-02", "doc-06", "doc-10"]);
|
|
391
|
-
});
|
|
392
|
-
it("$and with three conditions", async () => {
|
|
393
|
-
const ids = await queryIds({
|
|
394
|
-
$and: [{ status: "active" }, { flag: true }, { num: { $gt: 0 } }],
|
|
395
|
-
});
|
|
396
|
-
expect(ids).toEqual(["doc-01"]);
|
|
397
|
-
});
|
|
398
|
-
it("$or with two conditions", async () => {
|
|
399
|
-
const ids = await queryIds({
|
|
400
|
-
$or: [{ status: "deleted" }, { num: { $lt: 0 } }],
|
|
401
|
-
});
|
|
402
|
-
expect(ids).toEqual(["doc-05", "doc-07", "doc-08"]);
|
|
403
|
-
});
|
|
404
|
-
it("$or with equality on same field", async () => {
|
|
405
|
-
const ids = await queryIds({
|
|
406
|
-
$or: [{ num: 10 }, { num: 20 }, { num: 30 }],
|
|
407
|
-
});
|
|
408
|
-
expect(ids).toEqual(["doc-01", "doc-02", "doc-03"]);
|
|
409
|
-
});
|
|
410
|
-
it("$not with simple condition", async () => {
|
|
411
|
-
const ids = await queryIds({
|
|
412
|
-
$not: { status: "active" },
|
|
413
|
-
});
|
|
414
|
-
expect(ids).toEqual(["doc-03", "doc-04", "doc-05", "doc-08", "doc-09"]);
|
|
415
|
-
});
|
|
416
|
-
it("$not with comparison", async () => {
|
|
417
|
-
const ids = await queryIds({
|
|
418
|
-
$not: { num: { $gte: 30 } },
|
|
419
|
-
});
|
|
420
|
-
expect(ids).toEqual(["doc-01", "doc-02", "doc-06", "doc-07", "doc-09"]);
|
|
421
|
-
});
|
|
422
|
-
it("AND of ORs", async () => {
|
|
423
|
-
const ids = await queryIds({
|
|
424
|
-
$and: [
|
|
425
|
-
{ $or: [{ status: "active" }, { status: "pending" }] },
|
|
426
|
-
{ $or: [{ flag: true }] },
|
|
427
|
-
],
|
|
428
|
-
});
|
|
429
|
-
expect(ids).toEqual(["doc-01", "doc-03", "doc-07", "doc-09"]);
|
|
430
|
-
});
|
|
431
|
-
it("OR of ANDs", async () => {
|
|
432
|
-
const ids = await queryIds({
|
|
433
|
-
$or: [
|
|
434
|
-
{ status: "active", flag: true },
|
|
435
|
-
{ status: "deleted", flag: false },
|
|
436
|
-
],
|
|
437
|
-
});
|
|
438
|
-
expect(ids).toEqual(["doc-01", "doc-07", "doc-08"]);
|
|
439
|
-
});
|
|
440
|
-
it("deeply nested filter", async () => {
|
|
441
|
-
const ids = await queryIds({
|
|
442
|
-
$and: [
|
|
443
|
-
{ num: { $gte: 0 } },
|
|
444
|
-
{
|
|
445
|
-
$or: [
|
|
446
|
-
{ status: "active", flag: true },
|
|
447
|
-
{
|
|
448
|
-
$and: [
|
|
449
|
-
{ status: "pending" },
|
|
450
|
-
{ tags: { $contains: "review" } },
|
|
451
|
-
],
|
|
452
|
-
},
|
|
453
|
-
],
|
|
454
|
-
},
|
|
455
|
-
],
|
|
456
|
-
});
|
|
457
|
-
// doc-01: active + flag=true
|
|
458
|
-
// doc-04: pending + tags contains "review"
|
|
459
|
-
// doc-09: pending + tags contains "review"
|
|
460
|
-
expect(ids).toEqual(["doc-01", "doc-04", "doc-09"]);
|
|
461
|
-
});
|
|
462
|
-
});
|
|
463
|
-
// ============================================================
|
|
464
|
-
// COMBINED FILTER + FIELD ASSERTIONS
|
|
465
|
-
// ============================================================
|
|
466
|
-
describe("filter result validation", () => {
|
|
467
|
-
it("filtered results have correct field values", async () => {
|
|
468
|
-
const hits = await index.query({
|
|
469
|
-
query: [{ vector: QUERY_VECTOR }],
|
|
470
|
-
topK: 100,
|
|
471
|
-
filter: { status: "pending" },
|
|
472
|
-
include: ["status", "flag", "num"],
|
|
473
|
-
});
|
|
474
|
-
expect(hits.length).toBe(3);
|
|
475
|
-
for (const hit of hits) {
|
|
476
|
-
expect(hit.document?.status).toBe("pending");
|
|
477
|
-
}
|
|
478
|
-
const nums = hits
|
|
479
|
-
.map((h) => h.document?.num)
|
|
480
|
-
.sort((a, b) => (a ?? 0) - (b ?? 0));
|
|
481
|
-
expect(nums).toEqual([25, 30, 40]);
|
|
482
|
-
});
|
|
483
|
-
it("complex filter returns expected documents with correct data", async () => {
|
|
484
|
-
const hits = await index.query({
|
|
485
|
-
query: [{ vector: QUERY_VECTOR }],
|
|
486
|
-
topK: 100,
|
|
487
|
-
filter: {
|
|
488
|
-
$and: [{ num: { $gte: 20, $lte: 50 } }, { flag: true }],
|
|
489
|
-
},
|
|
490
|
-
include: ["num", "flag", "name"],
|
|
491
|
-
});
|
|
492
|
-
expect(hits.length).toBe(3);
|
|
493
|
-
const names = hits.map((h) => h.document?.name).sort();
|
|
494
|
-
expect(names).toEqual(["charlie_smith", "eve_johnson", "ivy_taylor"]);
|
|
495
|
-
for (const hit of hits) {
|
|
496
|
-
expect(hit.document?.flag).toBe(true);
|
|
497
|
-
expect(hit.document?.num).toBeGreaterThanOrEqual(20);
|
|
498
|
-
expect(hit.document?.num).toBeLessThanOrEqual(50);
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/integration.test.ts"],"names":[],"mappings":""}
|