usearch 2.17.1 → 2.19.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/README.md +6 -2
- package/c/lib.cpp +13 -2
- package/c/module.modulemap +4 -0
- package/c/usearch.h +13 -4
- package/cpp/README.md +1 -1
- package/cpp/bench.cpp +2 -2
- package/cpp/test.cpp +110 -16
- package/include/usearch/index.hpp +91 -35
- package/include/usearch/index_dense.hpp +34 -19
- package/include/usearch/index_plugins.hpp +29 -7
- package/javascript/README.md +8 -6
- package/javascript/dist/cjs/usearch.d.ts +9 -4
- package/javascript/dist/cjs/usearch.js +23 -8
- package/javascript/dist/esm/usearch.d.ts +9 -4
- package/javascript/dist/esm/usearch.js +21 -7
- package/javascript/lib.cpp +137 -59
- package/javascript/usearch.test.js +359 -192
- package/javascript/usearch.ts +35 -11
- package/package.json +1 -1
- package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
- package/prebuilds/linux-arm64/usearch.node +0 -0
- package/prebuilds/linux-x64/usearch.node +0 -0
- package/simsimd/CONTRIBUTING.md +23 -8
- package/simsimd/Cargo.lock +1 -1
- package/simsimd/Cargo.toml +1 -1
- package/simsimd/README.md +49 -9
- package/simsimd/build.rs +7 -3
- package/simsimd/c/lib.c +6 -1
- package/simsimd/include/simsimd/binary.h +3 -3
- package/simsimd/include/simsimd/dot.h +2 -2
- package/simsimd/include/simsimd/elementwise.h +26 -17
- package/simsimd/include/simsimd/simsimd.h +125 -49
- package/simsimd/include/simsimd/sparse.h +30 -29
- package/simsimd/include/simsimd/spatial.h +4 -3
- package/simsimd/include/simsimd/types.h +19 -8
- package/simsimd/javascript/lib.c +0 -1
- package/simsimd/package-lock.json +411 -362
- package/simsimd/package.json +9 -9
- package/simsimd/rust/lib.rs +63 -29
- package/simsimd/scripts/bench.cxx +13 -0
- package/simsimd/scripts/bench_cosine.rs +8 -0
- package/simsimd/scripts/bench_native.rs +4 -0
- package/simsimd/scripts/bench_sqeuclidean.rs +8 -0
- package/simsimd/scripts/test.c +22 -0
- package/simsimd/scripts/test.py +80 -4
- package/simsimd/swift/SimSIMD.swift +17 -0
- package/simsimd/swift/Test.swift +36 -0
|
@@ -1,4 +1,35 @@
|
|
|
1
|
-
const
|
|
1
|
+
const nodeTest = require('node:test');
|
|
2
|
+
const realTest = nodeTest.test; // the original function
|
|
3
|
+
|
|
4
|
+
function loggedTest(name, options, fn) {
|
|
5
|
+
// The API has two call signatures:
|
|
6
|
+
// test(name, fn)
|
|
7
|
+
// test(name, options, fn)
|
|
8
|
+
if (typeof options === 'function') {
|
|
9
|
+
fn = options;
|
|
10
|
+
options = undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Wrap the body so we can log before / after
|
|
14
|
+
const wrapped = async (t) => {
|
|
15
|
+
console.log('▶', name);
|
|
16
|
+
try {
|
|
17
|
+
await fn(t); // run the user’s test
|
|
18
|
+
console.log('✓', name);
|
|
19
|
+
} catch (err) {
|
|
20
|
+
console.log('✖', name);
|
|
21
|
+
throw err; // re-throw so the runner records the failure
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Delegate back to the real test() with the same options
|
|
26
|
+
return options ? realTest(name, options, wrapped) : realTest(name, wrapped);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Replace both the export and the global the runner puts on each module
|
|
30
|
+
global.test = loggedTest;
|
|
31
|
+
module.exports = loggedTest; // for completeness if this file is `require`d
|
|
32
|
+
|
|
2
33
|
const assert = require('node:assert');
|
|
3
34
|
const fs = require('node:fs');
|
|
4
35
|
const os = require('node:os');
|
|
@@ -6,250 +37,386 @@ const path = require('node:path');
|
|
|
6
37
|
const usearch = require('./dist/cjs/usearch.js');
|
|
7
38
|
|
|
8
39
|
function assertAlmostEqual(actual, expected, tolerance = 1e-6) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
40
|
+
const lowerBound = expected - tolerance;
|
|
41
|
+
const upperBound = expected + tolerance;
|
|
42
|
+
assert(
|
|
43
|
+
actual >= lowerBound && actual <= upperBound,
|
|
44
|
+
`Expected ${actual} to be almost equal to ${expected}`
|
|
45
|
+
);
|
|
15
46
|
}
|
|
16
47
|
|
|
48
|
+
const THREADS = 2;
|
|
17
49
|
|
|
18
50
|
test('Single-entry operations', async (t) => {
|
|
19
|
-
|
|
20
|
-
|
|
51
|
+
await t.test('index info', () => {
|
|
52
|
+
const index = new usearch.Index(2, 'l2sq');
|
|
21
53
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
54
|
+
assert.equal(index.connectivity(), 16, 'connectivity should be 16');
|
|
55
|
+
assert.equal(index.dimensions(), 2, 'dimensions should be 2');
|
|
56
|
+
assert.equal(index.size(), 0, 'initial size should be 0');
|
|
57
|
+
});
|
|
26
58
|
|
|
27
|
-
|
|
28
|
-
|
|
59
|
+
await t.test('add', () => {
|
|
60
|
+
const index = new usearch.Index(2, 'l2sq');
|
|
29
61
|
|
|
30
|
-
|
|
31
|
-
|
|
62
|
+
index.add(15n, new Float32Array([10, 20]));
|
|
63
|
+
index.add(16n, new Float32Array([10, 25]));
|
|
32
64
|
|
|
33
|
-
|
|
34
|
-
|
|
65
|
+
assert.equal(index.size(), 2, 'size after adding elements should be 2');
|
|
66
|
+
assert.equal(
|
|
67
|
+
index.contains(15),
|
|
68
|
+
true,
|
|
69
|
+
'entry must be present after insertion'
|
|
70
|
+
);
|
|
35
71
|
|
|
36
|
-
|
|
72
|
+
const results = index.search(new Float32Array([13, 14]), 2);
|
|
37
73
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
74
|
+
assert.deepEqual(
|
|
75
|
+
results.keys,
|
|
76
|
+
new BigUint64Array([15n, 16n]),
|
|
77
|
+
'keys should be 15 and 16'
|
|
78
|
+
);
|
|
79
|
+
assert.deepEqual(
|
|
80
|
+
results.distances,
|
|
81
|
+
new Float32Array([45, 130]),
|
|
82
|
+
'distances should be 45 and 130'
|
|
83
|
+
);
|
|
84
|
+
});
|
|
41
85
|
|
|
42
|
-
|
|
43
|
-
|
|
86
|
+
await t.test('remove', () => {
|
|
87
|
+
const index = new usearch.Index(2, 'l2sq');
|
|
44
88
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
89
|
+
index.add(15n, new Float32Array([10, 20]));
|
|
90
|
+
index.add(16n, new Float32Array([10, 25]));
|
|
91
|
+
index.add(25n, new Float32Array([20, 40]));
|
|
92
|
+
index.add(26n, new Float32Array([20, 45]));
|
|
49
93
|
|
|
50
|
-
|
|
94
|
+
assert.equal(index.remove(15n), 1);
|
|
51
95
|
|
|
52
|
-
|
|
53
|
-
|
|
96
|
+
assert.equal(index.size(), 3, 'size after removing elements should be 3');
|
|
97
|
+
assert.equal(
|
|
98
|
+
index.contains(15),
|
|
99
|
+
false,
|
|
100
|
+
'entry must be absent after insertion'
|
|
101
|
+
);
|
|
54
102
|
|
|
55
|
-
|
|
103
|
+
const results = index.search(new Float32Array([13, 14]), 2);
|
|
56
104
|
|
|
57
|
-
|
|
58
|
-
|
|
105
|
+
assert.deepEqual(
|
|
106
|
+
results.keys,
|
|
107
|
+
new BigUint64Array([16n, 25n]),
|
|
108
|
+
'keys should not include 15'
|
|
109
|
+
);
|
|
110
|
+
});
|
|
59
111
|
});
|
|
60
112
|
|
|
61
113
|
test('Batch operations', async (t) => {
|
|
62
|
-
|
|
63
|
-
|
|
114
|
+
await t.test('add', () => {
|
|
115
|
+
const indexBatch = new usearch.Index(2, 'l2sq');
|
|
64
116
|
|
|
65
|
-
|
|
66
|
-
|
|
117
|
+
const keys = [15n, 16n];
|
|
118
|
+
const vectors = [new Float32Array([10, 20]), new Float32Array([10, 25])];
|
|
67
119
|
|
|
68
|
-
|
|
69
|
-
|
|
120
|
+
indexBatch.add(keys, vectors);
|
|
121
|
+
assert.equal(indexBatch.size(), 2, 'size after adding batch should be 2');
|
|
70
122
|
|
|
71
|
-
|
|
123
|
+
const results = indexBatch.search(new Float32Array([13, 14]), 2);
|
|
72
124
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
125
|
+
assert.deepEqual(
|
|
126
|
+
results.keys,
|
|
127
|
+
new BigUint64Array([15n, 16n]),
|
|
128
|
+
'keys should be 15 and 16'
|
|
129
|
+
);
|
|
130
|
+
assert.deepEqual(
|
|
131
|
+
results.distances,
|
|
132
|
+
new Float32Array([45, 130]),
|
|
133
|
+
'distances should be 45 and 130'
|
|
134
|
+
);
|
|
135
|
+
});
|
|
76
136
|
|
|
77
|
-
|
|
78
|
-
|
|
137
|
+
await t.test('remove', () => {
|
|
138
|
+
const indexBatch = new usearch.Index(2, 'l2sq');
|
|
79
139
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
140
|
+
const keys = [15n, 16n, 25n, 26n];
|
|
141
|
+
const vectors = [
|
|
142
|
+
new Float32Array([10, 20]),
|
|
143
|
+
new Float32Array([10, 25]),
|
|
144
|
+
new Float32Array([20, 40]),
|
|
145
|
+
new Float32Array([20, 45]),
|
|
146
|
+
];
|
|
147
|
+
indexBatch.add(keys, vectors);
|
|
88
148
|
|
|
89
|
-
|
|
90
|
-
|
|
149
|
+
assert.deepEqual(indexBatch.remove([15n, 25n]), [1, 1]);
|
|
150
|
+
assert.equal(indexBatch.size(), 2, 'size after removing batch should be 2');
|
|
91
151
|
|
|
92
|
-
|
|
152
|
+
const results = indexBatch.search(new Float32Array([13, 14]), 2);
|
|
153
|
+
|
|
154
|
+
assert.deepEqual(
|
|
155
|
+
results.keys,
|
|
156
|
+
new BigUint64Array([16n, 26n]),
|
|
157
|
+
'keys should not include 15 and 25'
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
await t.test('search', () => {
|
|
162
|
+
const indexBatch = new usearch.Index(3, 'l2sq');
|
|
163
|
+
|
|
164
|
+
const keys = [15n, 16n];
|
|
165
|
+
const vectors = [new Float32Array([10, 20, 30]), new Float32Array([10, 25, 35])];
|
|
166
|
+
|
|
167
|
+
indexBatch.add(keys, vectors);
|
|
168
|
+
assert.equal(indexBatch.size(), 2, 'size after adding batch should be 2');
|
|
169
|
+
|
|
170
|
+
const results = indexBatch.search([new Float32Array([13, 14, 15]), new Float32Array([13, 14, 15])], 2);
|
|
171
|
+
|
|
172
|
+
assert.equal(results.k, 2, 'k should be 2');
|
|
173
|
+
|
|
174
|
+
assert.deepEqual(
|
|
175
|
+
results.counts,
|
|
176
|
+
new BigUint64Array([ 2n, 2n ]) ,
|
|
177
|
+
'counts should be 2 for both keys'
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
assert.deepEqual(
|
|
181
|
+
results.keys,
|
|
182
|
+
new BigUint64Array([15n, 16n, 15n, 16n]),
|
|
183
|
+
'keys should be 15 and 16'
|
|
184
|
+
);
|
|
185
|
+
assert.deepEqual(
|
|
186
|
+
results.distances,
|
|
187
|
+
new Float32Array( [ 270, 530, 270, 530 ]),
|
|
188
|
+
'distances should be 270 and 530'
|
|
189
|
+
);
|
|
190
|
+
});
|
|
93
191
|
|
|
94
|
-
assert.deepEqual(results.keys, new BigUint64Array([16n, 26n]), 'keys should not include 15 and 25');
|
|
95
|
-
});
|
|
96
192
|
});
|
|
97
193
|
|
|
98
|
-
test(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
194
|
+
test('Expected results', () => {
|
|
195
|
+
const index = new usearch.Index({
|
|
196
|
+
metric: 'l2sq',
|
|
197
|
+
connectivity: 16,
|
|
198
|
+
dimensions: 3,
|
|
199
|
+
});
|
|
200
|
+
index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
|
|
201
|
+
const results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10);
|
|
202
|
+
|
|
203
|
+
assert.equal(index.size(), 1);
|
|
204
|
+
assert.deepEqual(results.keys, new BigUint64Array([42n]));
|
|
205
|
+
assertAlmostEqual(results.distances[0], new Float32Array([0]));
|
|
206
|
+
});
|
|
106
207
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
208
|
+
test('Multithread search returns same results', () => {
|
|
209
|
+
const index = new usearch.Index({
|
|
210
|
+
metric: 'l2sq',
|
|
211
|
+
connectivity: 16,
|
|
212
|
+
dimensions: 3,
|
|
213
|
+
});
|
|
214
|
+
index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
|
|
215
|
+
assert.equal(index.size(), 1);
|
|
216
|
+
|
|
217
|
+
const results_1 = index.search(new Float32Array([0.2, 0.6, 0.4]), 10, 0);
|
|
218
|
+
const results_2 = index.search(
|
|
219
|
+
new Float32Array([0.2, 0.6, 0.4]),
|
|
220
|
+
10,
|
|
221
|
+
THREADS
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
assert.deepEqual(results_1.keys, results_2.keys);
|
|
225
|
+
assertAlmostEqual(results_1.distances, results_2.distances);
|
|
110
226
|
});
|
|
111
227
|
|
|
112
|
-
test('
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
228
|
+
test('Exact search', async (t) => {
|
|
229
|
+
const dataset = [
|
|
230
|
+
new Float32Array([0.2, 0.6, 0.4]),
|
|
231
|
+
new Float32Array([0.6, 0.6, 0.4]),
|
|
232
|
+
];
|
|
233
|
+
const queries = new Float32Array([0.2, 0.6, 0.4]);
|
|
234
|
+
const dimensions = 3;
|
|
235
|
+
const count = 2;
|
|
236
|
+
const metric = 'l2sq';
|
|
237
|
+
|
|
238
|
+
await t.test('Single core search', () => {
|
|
239
|
+
const result = usearch.exactSearch(
|
|
240
|
+
dataset,
|
|
241
|
+
queries,
|
|
242
|
+
dimensions,
|
|
243
|
+
count,
|
|
244
|
+
metric,
|
|
245
|
+
1 // threads
|
|
121
246
|
);
|
|
122
247
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
248
|
+
assert.deepEqual(result.keys, new BigUint64Array([0n, 1n]));
|
|
249
|
+
assertAlmostEqual(new Float32Array([0]), result.distances[0]);
|
|
250
|
+
assertAlmostEqual(new Float32Array([0.16]), result.distances[1]);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
await t.test('Multithreaded search', () => {
|
|
254
|
+
const result = usearch.exactSearch(
|
|
255
|
+
dataset,
|
|
256
|
+
queries,
|
|
257
|
+
dimensions,
|
|
258
|
+
count,
|
|
259
|
+
metric,
|
|
260
|
+
THREADS // threads
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
assert.deepEqual(result.keys, new BigUint64Array([0n, 1n]));
|
|
264
|
+
assertAlmostEqual(new Float32Array([0]), result.distances[0]);
|
|
265
|
+
assertAlmostEqual(new Float32Array([0.16]), result.distances[1]);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test('Expected count()', async (t) => {
|
|
270
|
+
const index = new usearch.Index({
|
|
271
|
+
metric: 'l2sq',
|
|
272
|
+
connectivity: 16,
|
|
273
|
+
dimensions: 3,
|
|
274
|
+
});
|
|
275
|
+
index.add(
|
|
276
|
+
[42n, 43n],
|
|
277
|
+
[new Float32Array([0.2, 0.6, 0.4]), new Float32Array([0.2, 0.6, 0.4])]
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
await t.test('Argument is a number', () => {
|
|
281
|
+
assert.equal(1, index.count(43n));
|
|
282
|
+
});
|
|
283
|
+
await t.test('Argument is a number (does not exist)', () => {
|
|
284
|
+
assert.equal(0, index.count(44n));
|
|
285
|
+
});
|
|
286
|
+
await t.test('Argument is an array', () => {
|
|
287
|
+
assert.deepEqual([1, 1, 0], index.count([42n, 43n, 44n]));
|
|
288
|
+
});
|
|
132
289
|
});
|
|
133
290
|
|
|
134
291
|
test('Operations with invalid values', () => {
|
|
135
|
-
|
|
292
|
+
const indexBatch = new usearch.Index(2, 'l2sq');
|
|
136
293
|
|
|
137
|
-
|
|
138
|
-
|
|
294
|
+
const keys = [NaN, 16n];
|
|
295
|
+
const vectors = [new Float32Array([10, 30]), new Float32Array([1, 5])];
|
|
139
296
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
{
|
|
143
|
-
name: 'Error',
|
|
144
|
-
message: 'All keys must be positive integers or bigints.'
|
|
145
|
-
}
|
|
146
|
-
);
|
|
297
|
+
// All keys must be positive integers or bigints
|
|
298
|
+
assert.throws(() => indexBatch.add(keys, vectors));
|
|
147
299
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
{
|
|
151
|
-
name: 'Error',
|
|
152
|
-
message: 'Vectors must be a TypedArray or an array of arrays.'
|
|
153
|
-
}
|
|
154
|
-
);
|
|
300
|
+
// Vectors must be a TypedArray or an array of arrays
|
|
301
|
+
assert.throws(() => indexBatch.search(NaN, 2));
|
|
155
302
|
});
|
|
156
303
|
|
|
157
304
|
test('Invalid operations', async (t) => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
});
|
|
164
|
-
index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
|
|
165
|
-
assert.throws(
|
|
166
|
-
() => index.add(42n, new Float32Array([0.2, 0.6, 0.4])),
|
|
167
|
-
{
|
|
168
|
-
name: 'Error',
|
|
169
|
-
message: 'Duplicate keys not allowed in high-level wrappers'
|
|
170
|
-
}
|
|
171
|
-
);
|
|
305
|
+
await t.test('Add the same keys', () => {
|
|
306
|
+
const index = new usearch.Index({
|
|
307
|
+
metric: 'l2sq',
|
|
308
|
+
connectivity: 16,
|
|
309
|
+
dimensions: 3,
|
|
172
310
|
});
|
|
173
|
-
|
|
311
|
+
index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
|
|
312
|
+
assert.throws(() => index.add(42n, new Float32Array([0.2, 0.6, 0.4])));
|
|
313
|
+
});
|
|
174
314
|
|
|
315
|
+
await t.test('Batch add containing the same key', () => {
|
|
316
|
+
const index = new usearch.Index({
|
|
317
|
+
metric: 'l2sq',
|
|
318
|
+
connectivity: 16,
|
|
319
|
+
dimensions: 3,
|
|
320
|
+
});
|
|
321
|
+
index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
|
|
322
|
+
assert.throws(() => {
|
|
323
|
+
index.add(
|
|
324
|
+
[41n, 42n, 43n],
|
|
325
|
+
[
|
|
326
|
+
[0.1, 0.6, 0.4],
|
|
327
|
+
[0.2, 0.6, 0.4],
|
|
328
|
+
[0.3, 0.6, 0.4],
|
|
329
|
+
]
|
|
330
|
+
);
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
});
|
|
175
334
|
|
|
176
335
|
test('Serialization', async (t) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
t.beforeEach(() => {
|
|
180
|
-
const index = new usearch.Index({
|
|
181
|
-
metric: "l2sq",
|
|
182
|
-
connectivity: 16,
|
|
183
|
-
dimensions: 3,
|
|
184
|
-
});
|
|
185
|
-
index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
|
|
186
|
-
index.save(indexPath);
|
|
187
|
-
});
|
|
336
|
+
const indexPath = path.join(os.tmpdir(), 'usearch.test.index');
|
|
188
337
|
|
|
189
|
-
|
|
190
|
-
|
|
338
|
+
t.beforeEach(() => {
|
|
339
|
+
const index = new usearch.Index({
|
|
340
|
+
metric: 'l2sq',
|
|
341
|
+
connectivity: 16,
|
|
342
|
+
dimensions: 3,
|
|
191
343
|
});
|
|
344
|
+
index.add(42n, new Float32Array([0.2, 0.6, 0.4]));
|
|
345
|
+
index.save(indexPath);
|
|
346
|
+
});
|
|
192
347
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
connectivity: 16,
|
|
197
|
-
dimensions: 3,
|
|
198
|
-
});
|
|
199
|
-
index.load(indexPath);
|
|
200
|
-
const results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10);
|
|
201
|
-
|
|
202
|
-
assert.equal(index.size(), 1);
|
|
203
|
-
assert.deepEqual(results.keys, new BigUint64Array([42n]));
|
|
204
|
-
assertAlmostEqual(results.distances[0], new Float32Array([0]));
|
|
205
|
-
});
|
|
348
|
+
t.afterEach(() => {
|
|
349
|
+
fs.unlinkSync(indexPath);
|
|
350
|
+
});
|
|
206
351
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
metric: "l2sq",
|
|
213
|
-
connectivity: 16,
|
|
214
|
-
dimensions: 3,
|
|
215
|
-
});
|
|
216
|
-
index.view(indexPath);
|
|
217
|
-
const results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10);
|
|
218
|
-
|
|
219
|
-
assert.equal(index.size(), 1);
|
|
220
|
-
assert.deepEqual(results.keys, new BigUint64Array([42n]));
|
|
221
|
-
assertAlmostEqual(results.distances[0], new Float32Array([0]));
|
|
352
|
+
await t.test('load', () => {
|
|
353
|
+
const index = new usearch.Index({
|
|
354
|
+
metric: 'l2sq',
|
|
355
|
+
connectivity: 16,
|
|
356
|
+
dimensions: 3,
|
|
222
357
|
});
|
|
358
|
+
index.load(indexPath);
|
|
359
|
+
const results = index.search(
|
|
360
|
+
new Float32Array([0.2, 0.6, 0.4]),
|
|
361
|
+
10,
|
|
362
|
+
THREADS
|
|
363
|
+
);
|
|
223
364
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
365
|
+
assert.equal(index.size(), 1);
|
|
366
|
+
assert.deepEqual(results.keys, new BigUint64Array([42n]));
|
|
367
|
+
assertAlmostEqual(results.distances[0], new Float32Array([0]));
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// todo: Skip as the test fails only on windows.
|
|
371
|
+
// The following error in afterEach().
|
|
372
|
+
// `error: "EBUSY: resource busy or locked, unlink`
|
|
373
|
+
await t.test(
|
|
374
|
+
'view: Read data',
|
|
375
|
+
{ skip: process.platform === 'win32' },
|
|
376
|
+
() => {
|
|
377
|
+
const index = new usearch.Index({
|
|
378
|
+
metric: 'l2sq',
|
|
379
|
+
connectivity: 16,
|
|
380
|
+
dimensions: 3,
|
|
381
|
+
});
|
|
382
|
+
index.view(indexPath);
|
|
383
|
+
const results = index.search(new Float32Array([0.2, 0.6, 0.4]), 10);
|
|
384
|
+
|
|
385
|
+
assert.equal(index.size(), 1);
|
|
386
|
+
assert.deepEqual(results.keys, new BigUint64Array([42n]));
|
|
387
|
+
assertAlmostEqual(results.distances[0], new Float32Array([0]));
|
|
388
|
+
}
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
await t.test(
|
|
392
|
+
'view: Invalid operations: add',
|
|
393
|
+
{ skip: process.platform === 'win32' },
|
|
394
|
+
() => {
|
|
395
|
+
const index = new usearch.Index({
|
|
396
|
+
metric: 'l2sq',
|
|
397
|
+
connectivity: 16,
|
|
398
|
+
dimensions: 3,
|
|
399
|
+
});
|
|
400
|
+
index.view(indexPath);
|
|
401
|
+
|
|
402
|
+
// Can't add to an immutable index
|
|
403
|
+
assert.throws(() => index.add(43n, new Float32Array([0.2, 0.6, 0.4])));
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
await t.test(
|
|
408
|
+
'view: Invalid operations: remove',
|
|
409
|
+
{ skip: process.platform === 'win32' },
|
|
410
|
+
() => {
|
|
411
|
+
const index = new usearch.Index({
|
|
412
|
+
metric: 'l2sq',
|
|
413
|
+
connectivity: 16,
|
|
414
|
+
dimensions: 3,
|
|
415
|
+
});
|
|
416
|
+
index.view(indexPath);
|
|
239
417
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
dimensions: 3,
|
|
245
|
-
});
|
|
246
|
-
index.view(indexPath);
|
|
247
|
-
assert.throws(
|
|
248
|
-
() => index.remove(42n),
|
|
249
|
-
{
|
|
250
|
-
name: 'Error',
|
|
251
|
-
message: "Can't remove from an immutable index"
|
|
252
|
-
}
|
|
253
|
-
);
|
|
254
|
-
});
|
|
418
|
+
// Can't remove from an immutable index
|
|
419
|
+
assert.throws(() => index.remove(42n));
|
|
420
|
+
}
|
|
421
|
+
);
|
|
255
422
|
});
|