lucivy 0.2.1 → 2.0.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 +111 -30
- package/index.d.ts +202 -19
- package/lucivy.node +0 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
# lucivy
|
|
1
|
+
# lucivy v2
|
|
2
2
|
|
|
3
3
|
Fast BM25 full-text search for Node.js — with substring matching, fuzzy search, regex, and highlights. Powered by Rust via napi-rs.
|
|
4
4
|
|
|
5
|
+
[**Try the live playground**](https://l-defraiteur.github.io/lucivy/) — runs entirely in your browser via WASM.
|
|
6
|
+
|
|
7
|
+
### What's new in v2
|
|
8
|
+
|
|
9
|
+
- **SFX-only engine** — all queries route through the Suffix FST, no legacy code paths
|
|
10
|
+
- **Distributed search** — `exportStats` / `mergeStats` / `searchWithGlobalStats`
|
|
11
|
+
- **Incremental sync** — LUCIDS sharded delta export/apply
|
|
12
|
+
- **Correct BM25 cross-shard** — identical scores whether 1 shard or 4
|
|
13
|
+
- **5 bindings** — Python, Node.js, C++, WASM, Rust
|
|
14
|
+
|
|
5
15
|
## Install
|
|
6
16
|
|
|
7
17
|
```bash
|
|
@@ -13,16 +23,16 @@ npm install lucivy
|
|
|
13
23
|
```javascript
|
|
14
24
|
const { Index } = require('lucivy');
|
|
15
25
|
|
|
16
|
-
const index = Index.create('
|
|
17
|
-
{ name: 'title', type: 'text' },
|
|
18
|
-
{ name: 'body', type: 'text' },
|
|
26
|
+
const index = Index.create('/tmp/my_index', [
|
|
27
|
+
{ name: 'title', type: 'text', stored: true },
|
|
28
|
+
{ name: 'body', type: 'text', stored: true },
|
|
19
29
|
]);
|
|
20
30
|
|
|
21
31
|
index.add(1, { title: 'Rust Programming', body: 'Systems programming with memory safety' });
|
|
22
32
|
index.add(2, { title: 'Python Guide', body: 'Data science and web development' });
|
|
23
33
|
index.commit();
|
|
24
34
|
|
|
25
|
-
|
|
35
|
+
const results = index.search('programming', { highlights: true });
|
|
26
36
|
for (const r of results) {
|
|
27
37
|
console.log(r.docId, r.score, r.highlights);
|
|
28
38
|
}
|
|
@@ -33,24 +43,31 @@ for (const r of results) {
|
|
|
33
43
|
### Create / open
|
|
34
44
|
|
|
35
45
|
```javascript
|
|
36
|
-
const index = Index.create('
|
|
37
|
-
{ name: 'title', type: 'text' },
|
|
38
|
-
{ name: 'body', type: 'text' },
|
|
39
|
-
{ name: '
|
|
40
|
-
{ name: 'year', type: 'i64', indexed: true, fast: true },
|
|
46
|
+
const index = Index.create('/tmp/my_index', [
|
|
47
|
+
{ name: 'title', type: 'text', stored: true },
|
|
48
|
+
{ name: 'body', type: 'text', stored: true },
|
|
49
|
+
{ name: 'score', type: 'f64', fast: true },
|
|
41
50
|
]);
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
// Sharded (4 shards)
|
|
53
|
+
const sharded = Index.create('/tmp/sharded', [...], 4);
|
|
54
|
+
|
|
55
|
+
// Open existing
|
|
56
|
+
const index2 = Index.open('/tmp/my_index');
|
|
44
57
|
```
|
|
45
58
|
|
|
59
|
+
Field types: `"text"` (full-text, tokenized), `"u64"`, `"i64"`, `"f64"`, `"bool"`, `"date"`.
|
|
60
|
+
|
|
46
61
|
### Add / update / delete
|
|
47
62
|
|
|
48
63
|
```javascript
|
|
49
|
-
index.add(1, { title: 'Hello', body: 'World' });
|
|
64
|
+
index.add(1, { title: 'Hello', body: 'World', score: 3.14 });
|
|
65
|
+
|
|
50
66
|
index.addMany([
|
|
51
67
|
{ docId: 2, title: 'Foo', body: 'Bar' },
|
|
52
68
|
{ docId: 3, title: 'Baz', body: 'Qux' },
|
|
53
69
|
]);
|
|
70
|
+
|
|
54
71
|
index.update(1, { title: 'Updated', body: 'Content' });
|
|
55
72
|
index.delete(2);
|
|
56
73
|
index.commit();
|
|
@@ -62,7 +79,7 @@ index.commit();
|
|
|
62
79
|
// String query — each word searched across all text fields (contains_split)
|
|
63
80
|
let results = index.search('rust async programming');
|
|
64
81
|
|
|
65
|
-
// Options
|
|
82
|
+
// Options: limit, highlights, allowedIds, fields
|
|
66
83
|
results = index.search('rust', { limit: 20, highlights: true, allowedIds: [1, 3] });
|
|
67
84
|
|
|
68
85
|
// Retrieve stored field values with results
|
|
@@ -74,26 +91,33 @@ for (const r of results) {
|
|
|
74
91
|
|
|
75
92
|
#### contains — substring, fuzzy, regex (cross-token)
|
|
76
93
|
|
|
77
|
-
|
|
94
|
+
All substring queries are cross-token: they match across token boundaries.
|
|
78
95
|
|
|
79
96
|
```javascript
|
|
80
|
-
// Substring — matches "programming", "programmer", etc.
|
|
97
|
+
// Substring — matches "programming", "programmer", "getProgramHandle", etc.
|
|
81
98
|
index.search({ type: 'contains', field: 'body', value: 'program' });
|
|
82
99
|
|
|
83
|
-
//
|
|
84
|
-
index.search({ type: 'contains', field: 'body', value: '
|
|
100
|
+
// Fuzzy substring (Levenshtein distance)
|
|
101
|
+
index.search({ type: 'contains', field: 'body', value: 'mutx', distance: 1 });
|
|
102
|
+
|
|
103
|
+
// Regex substring — cross-token regex matching
|
|
104
|
+
index.search({ type: 'contains', field: 'body', value: 'lock.*mutex', regex: true });
|
|
105
|
+
|
|
106
|
+
// Prefix / startsWith — match must start at token boundary (SI=0)
|
|
107
|
+
index.search({ type: 'startsWith', field: 'body', value: 'prog' });
|
|
85
108
|
|
|
86
|
-
//
|
|
87
|
-
index.search({ type: '
|
|
109
|
+
// Exact whole-token match
|
|
110
|
+
index.search({ type: 'term', field: 'body', value: 'lock' });
|
|
88
111
|
|
|
89
|
-
//
|
|
90
|
-
index.search({ type: '
|
|
112
|
+
// Phrase — adjacent tokens in order
|
|
113
|
+
index.search({ type: 'phrase', field: 'body', value: 'mutex lock' });
|
|
91
114
|
```
|
|
92
115
|
|
|
93
|
-
#### contains_split —
|
|
116
|
+
#### contains_split — multi-word search
|
|
117
|
+
|
|
118
|
+
Split on whitespace, each word becomes a `contains` query, combined with boolean OR.
|
|
94
119
|
|
|
95
120
|
```javascript
|
|
96
|
-
// "rust safety" → contains("rust") OR contains("safety") on body
|
|
97
121
|
index.search({ type: 'contains_split', field: 'body', value: 'rust safety' });
|
|
98
122
|
|
|
99
123
|
// With fuzzy distance — each word gets fuzzy tolerance
|
|
@@ -117,16 +141,31 @@ index.search({
|
|
|
117
141
|
});
|
|
118
142
|
```
|
|
119
143
|
|
|
120
|
-
####
|
|
144
|
+
#### Filtering
|
|
145
|
+
|
|
146
|
+
Filter on non-text fields (combined with AND):
|
|
121
147
|
|
|
122
148
|
```javascript
|
|
123
|
-
index.search({ type: 'keyword', field: 'tag', value: 'rust' });
|
|
124
149
|
index.search({
|
|
125
|
-
type: 'contains', field: 'body', value: '
|
|
126
|
-
filters: [
|
|
150
|
+
type: 'contains', field: 'body', value: 'lock',
|
|
151
|
+
filters: [
|
|
152
|
+
{ field: 'category', op: 'eq', value: 'kernel' },
|
|
153
|
+
{ field: 'score', op: 'gte', value: 0.5 },
|
|
154
|
+
{ field: 'status', op: 'in', value: ['active', 'review'] },
|
|
155
|
+
]
|
|
127
156
|
});
|
|
128
157
|
```
|
|
129
158
|
|
|
159
|
+
Filter ops: `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `in`, `not_in`, `between`, `starts_with`, `contains`.
|
|
160
|
+
|
|
161
|
+
Pre-filter by document ID (fast, bitmap-based):
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
index.search({ type: 'contains', field: 'body', value: 'lock' }, { allowedIds: [1, 2, 3] });
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
> **Note:** napi-rs converts snake_case to camelCase — use `allowedIds`, `docId`, `numDocs`, etc. in JavaScript.
|
|
168
|
+
|
|
130
169
|
### Snapshots (export / import)
|
|
131
170
|
|
|
132
171
|
```javascript
|
|
@@ -143,12 +182,54 @@ const restored = Index.importSnapshotFrom('./backup.luce', './restored_index');
|
|
|
143
182
|
const restored2 = Index.importSnapshot(buf, './restored_index');
|
|
144
183
|
```
|
|
145
184
|
|
|
185
|
+
### Delta sync (incremental)
|
|
186
|
+
|
|
187
|
+
Sync only the segments that changed since the client's last version.
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
// Get current shard versions
|
|
191
|
+
const versions = index.shardVersions;
|
|
192
|
+
|
|
193
|
+
// Export delta (only changed segments)
|
|
194
|
+
const delta = index.exportShardedDelta(clientVersions);
|
|
195
|
+
|
|
196
|
+
// Apply delta on the client side
|
|
197
|
+
clientIndex.applyShardedDelta(delta);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Distributed search
|
|
201
|
+
|
|
202
|
+
Run BM25 search across multiple machines with correct IDF.
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
const { mergeStats } = require('lucivy');
|
|
206
|
+
|
|
207
|
+
const queryJson = '{"type":"contains","field":"body","value":"mutex"}';
|
|
208
|
+
|
|
209
|
+
// 1. Each node exports its local BM25 stats
|
|
210
|
+
const statsA = nodeA.exportStats(queryJson); // JSON string
|
|
211
|
+
const statsB = nodeB.exportStats(queryJson); // JSON string
|
|
212
|
+
|
|
213
|
+
// 2. Coordinator merges stats from all nodes
|
|
214
|
+
const merged = mergeStats([statsA, statsB]);
|
|
215
|
+
|
|
216
|
+
// 3. Each node searches with global stats (correct IDF across all nodes)
|
|
217
|
+
const resultsA = nodeA.searchWithGlobalStats(queryJson, merged, 10);
|
|
218
|
+
const resultsB = nodeB.searchWithGlobalStats(queryJson, merged, 10);
|
|
219
|
+
|
|
220
|
+
// 4. Coordinator merges top-K results by score
|
|
221
|
+
const all = [...resultsA, ...resultsB].sort((a, b) => b.score - a.score).slice(0, 10);
|
|
222
|
+
```
|
|
223
|
+
|
|
146
224
|
### Properties
|
|
147
225
|
|
|
148
226
|
```javascript
|
|
149
|
-
index.numDocs
|
|
150
|
-
index.
|
|
151
|
-
index.
|
|
227
|
+
index.numDocs // number of documents (getter)
|
|
228
|
+
index.numShards // number of shards (getter)
|
|
229
|
+
index.path // index directory path (getter)
|
|
230
|
+
index.schema // array of {name, type} objects (getter)
|
|
231
|
+
index.shardVersions // per-shard version info for delta sync (getter)
|
|
232
|
+
index.close() // flush + release writer lock
|
|
152
233
|
```
|
|
153
234
|
|
|
154
235
|
## License
|
package/index.d.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
/* auto-generated by NAPI-RS */
|
|
5
5
|
|
|
6
|
+
export interface ShardVersion {
|
|
7
|
+
shardId: number
|
|
8
|
+
version: string
|
|
9
|
+
segmentIds: Array<string>
|
|
10
|
+
}
|
|
6
11
|
export interface SearchResult {
|
|
7
12
|
docId: number
|
|
8
13
|
score: number
|
|
@@ -23,48 +28,226 @@ export interface SearchOptions {
|
|
|
23
28
|
fields?: boolean
|
|
24
29
|
}
|
|
25
30
|
export declare class Index {
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Create a new index at the given path.
|
|
33
|
+
*
|
|
34
|
+
* @param path - Directory path for the index files.
|
|
35
|
+
* @param fields - Field definitions: `[{name: "body", type: "text", stored: true}]`.
|
|
36
|
+
* Types: `"text"` (full-text), `"u64"`, `"i64"`, `"f64"`, `"bool"`, `"date"`.
|
|
37
|
+
* @param shards - Number of shards (default 1). More shards = faster search on large datasets.
|
|
38
|
+
*/
|
|
39
|
+
static create(path: string, fields: Array<FieldDef>, shards?: number | undefined | null): Index
|
|
40
|
+
/**
|
|
41
|
+
* Open an existing index at the given path.
|
|
42
|
+
*
|
|
43
|
+
* Reads the persisted schema and segment metadata from disk.
|
|
44
|
+
* The index must have been previously created with `Index.create()`.
|
|
45
|
+
*
|
|
46
|
+
* @param path - Directory path of the existing index (same path used in `create()`).
|
|
47
|
+
* @returns An `Index` ready for search, add, delete, etc.
|
|
48
|
+
*/
|
|
29
49
|
static open(path: string): Index
|
|
30
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Add a document.
|
|
52
|
+
*
|
|
53
|
+
* @param docId - Unique document ID (_node_id).
|
|
54
|
+
* @param fields - Object with field names as keys: `{title: "Hello", body: "World", score: 3.14}`
|
|
55
|
+
*/
|
|
31
56
|
add(docId: number, fields: Record<string, any>): void
|
|
32
57
|
/**
|
|
33
58
|
* Add multiple documents at once.
|
|
34
|
-
*
|
|
59
|
+
*
|
|
60
|
+
* Each element must have a `docId` (or `doc_id`) key plus field values.
|
|
61
|
+
*
|
|
62
|
+
* @param docs - Array of objects: `[{docId: 1, title: "Hello"}, {docId: 2, title: "World"}]`.
|
|
35
63
|
*/
|
|
36
64
|
addMany(docs: Array<Record<string, any>>): void
|
|
37
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Delete a document by its `_node_id`.
|
|
67
|
+
*
|
|
68
|
+
* The deletion is staged in memory. Call `commit()` or run a search
|
|
69
|
+
* (which auto-commits via lazy commit) to make it visible.
|
|
70
|
+
*
|
|
71
|
+
* @param docId - The `_node_id` of the document to delete.
|
|
72
|
+
*/
|
|
38
73
|
delete(docId: number): void
|
|
39
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Update a document (delete old + re-add with new fields).
|
|
76
|
+
*
|
|
77
|
+
* @param docId - The `_node_id` of the document to update.
|
|
78
|
+
* @param fields - New field values: `{title: "Updated", body: "New content"}`.
|
|
79
|
+
*/
|
|
40
80
|
update(docId: number, fields: Record<string, any>): void
|
|
41
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Commit pending changes to disk, making them visible to subsequent searches.
|
|
83
|
+
*
|
|
84
|
+
* Lucivy uses lazy commit: if you search without calling `commit()`,
|
|
85
|
+
* uncommitted changes are auto-flushed before the search executes.
|
|
86
|
+
* Call `commit()` explicitly when you need to control the commit point
|
|
87
|
+
* (e.g., after a batch of adds/deletes).
|
|
88
|
+
*/
|
|
42
89
|
commit(): void
|
|
43
|
-
/**
|
|
44
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Flush any pending writes and release the writer lock.
|
|
92
|
+
*
|
|
93
|
+
* After `close()`, the index data remains on disk and can be re-opened
|
|
94
|
+
* with `Index.open()`. No further mutations are allowed on this instance.
|
|
95
|
+
*/
|
|
96
|
+
close(): void
|
|
45
97
|
/**
|
|
46
98
|
* Search the index.
|
|
47
|
-
*
|
|
99
|
+
*
|
|
100
|
+
* @param query - String or query object.
|
|
101
|
+
*
|
|
102
|
+
* **String**: `"hello world"` — auto contains_split across all text fields.
|
|
103
|
+
*
|
|
104
|
+
* **Query object** (all substring queries are cross-token):
|
|
105
|
+
* - `{type: "contains", field: "body", value: "lock"}` — substring match
|
|
106
|
+
* - `{type: "contains", field: "body", value: "lock", distance: 1}` — fuzzy substring
|
|
107
|
+
* - `{type: "contains", field: "body", value: "lock.*init", regex: true}` — regex substring
|
|
108
|
+
* - `{type: "startsWith", field: "body", value: "lock"}` — token prefix
|
|
109
|
+
* - `{type: "contains_split", field: "body", value: "struct device"}` — words OR'd
|
|
110
|
+
* - `{type: "term", field: "body", value: "lock"}` — exact whole-token
|
|
111
|
+
* - `{type: "fuzzy", field: "body", value: "schdule", distance: 1}` — alias for contains+distance
|
|
112
|
+
* - `{type: "phrase", field: "body", value: "mutex lock"}` — adjacent tokens
|
|
113
|
+
* - `{type: "regex", field: "body", pattern: "sched[a-z]+"}` — regex on tokens
|
|
114
|
+
* - `{type: "boolean", must: [...], should: [...], must_not: [...]}` — boolean combo
|
|
115
|
+
* - `{type: "disjunction_max", queries: [...], tie_breaker: 0.1}` — best score
|
|
116
|
+
* - `{type: "more_like_this", field: "body", value: "sample text", min_doc_frequency: 1}` — similarity
|
|
117
|
+
*
|
|
118
|
+
* **Filtering:**
|
|
119
|
+
* - `allowed_ids` in options: pre-filter by _node_id (fast, bitmap-based)
|
|
120
|
+
* - `filters` key in query: filter on non-text fields (AND'd with search):
|
|
121
|
+
* ```json
|
|
122
|
+
* {type: "contains", field: "body", value: "lock",
|
|
123
|
+
* filters: [
|
|
124
|
+
* {field: "category", op: "eq", value: "kernel"},
|
|
125
|
+
* {field: "score", op: "gte", value: 0.5},
|
|
126
|
+
* {field: "status", op: "in", value: ["active", "review"]}
|
|
127
|
+
* ]}
|
|
128
|
+
* ```
|
|
129
|
+
* Ops: `eq`, `ne`, `lt`, `lte`, `gt`, `gte`, `in`, `not_in`, `between`, `starts_with`, `contains`.
|
|
130
|
+
* Composite: `must`, `should`, `must_not` with nested `clauses`.
|
|
131
|
+
*
|
|
132
|
+
* @param options - `{limit?: number, highlights?: boolean, fields?: boolean, allowed_ids?: number[]}`
|
|
48
133
|
*/
|
|
49
134
|
search(query: any, options?: SearchOptions | undefined | null): Array<SearchResult>
|
|
50
|
-
/**
|
|
135
|
+
/**
|
|
136
|
+
* Number of documents in the index (getter, access as `index.numDocs`).
|
|
137
|
+
*
|
|
138
|
+
* @returns Total document count across all shards.
|
|
139
|
+
*/
|
|
51
140
|
get numDocs(): number
|
|
52
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* Number of shards (getter, access as `index.numShards`).
|
|
143
|
+
*
|
|
144
|
+
* @returns Shard count (1 for single-shard indexes).
|
|
145
|
+
*/
|
|
146
|
+
get numShards(): number
|
|
147
|
+
/**
|
|
148
|
+
* Index directory path (getter, access as `index.path`).
|
|
149
|
+
*
|
|
150
|
+
* @returns The directory path where the index files are stored.
|
|
151
|
+
*/
|
|
53
152
|
get path(): string
|
|
54
153
|
/**
|
|
55
|
-
* Export this index as a LUCE snapshot
|
|
56
|
-
*
|
|
154
|
+
* Export this index as a LUCE snapshot.
|
|
155
|
+
*
|
|
156
|
+
* Returns the full index content (all shards, schema, segments) as a
|
|
157
|
+
* binary blob. Restore later with `Index.importSnapshot()`.
|
|
158
|
+
*
|
|
159
|
+
* @returns `Buffer` containing the LUCE snapshot bytes.
|
|
57
160
|
*/
|
|
58
161
|
exportSnapshot(): Buffer
|
|
59
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* Export this index as a LUCE snapshot directly to a file.
|
|
164
|
+
*
|
|
165
|
+
* @param path - Destination file path (typically ending in `.luce`).
|
|
166
|
+
*/
|
|
60
167
|
exportSnapshotTo(path: string): void
|
|
61
168
|
/**
|
|
62
169
|
* Import an index from a LUCE snapshot (Buffer).
|
|
63
|
-
*
|
|
170
|
+
*
|
|
171
|
+
* Restores a full index from a binary blob previously created by
|
|
172
|
+
* `exportSnapshot()`. The index files are written to `destPath`.
|
|
173
|
+
*
|
|
174
|
+
* @param data - Raw LUCE snapshot bytes (Buffer).
|
|
175
|
+
* @param destPath - Directory to write the restored index into.
|
|
176
|
+
* Defaults to `"/tmp/lucivy_import"`.
|
|
177
|
+
* @returns A new `Index` instance ready for search.
|
|
64
178
|
*/
|
|
65
179
|
static importSnapshot(data: Buffer, destPath?: string | undefined | null): Index
|
|
66
|
-
/**
|
|
180
|
+
/**
|
|
181
|
+
* Import an index from a LUCE snapshot file (.luce).
|
|
182
|
+
*
|
|
183
|
+
* Convenience wrapper that reads the file then calls `importSnapshot()`.
|
|
184
|
+
*
|
|
185
|
+
* @param path - Path to the `.luce` snapshot file.
|
|
186
|
+
* @param destPath - Directory to write the restored index into.
|
|
187
|
+
* Defaults to `"/tmp/lucivy_import"`.
|
|
188
|
+
* @returns A new `Index` instance ready for search.
|
|
189
|
+
*/
|
|
67
190
|
static importSnapshotFrom(path: string, destPath?: string | undefined | null): Index
|
|
68
|
-
/**
|
|
191
|
+
/**
|
|
192
|
+
* Schema as a list of field definitions (getter, access as `index.schema`).
|
|
193
|
+
*
|
|
194
|
+
* @returns `Array<{name: string, type: string}>` for each user-defined field.
|
|
195
|
+
*/
|
|
69
196
|
get schema(): Array<FieldDef>
|
|
197
|
+
/**
|
|
198
|
+
* Per-shard version info for delta sync (getter, access as `index.shardVersions`).
|
|
199
|
+
*
|
|
200
|
+
* Returns the current version and segment IDs for each shard.
|
|
201
|
+
* Pass this to a remote server's `exportShardedDelta()` to
|
|
202
|
+
* receive only the segments that changed since your last sync.
|
|
203
|
+
*
|
|
204
|
+
* @returns `Array<{shardId: number, version: string, segmentIds: string[]}>`.
|
|
205
|
+
*/
|
|
206
|
+
get shardVersions(): Array<ShardVersion>
|
|
207
|
+
/**
|
|
208
|
+
* Export a sharded delta (LUCIDS blob) containing only segments that
|
|
209
|
+
* changed since the client's known versions.
|
|
210
|
+
*
|
|
211
|
+
* Used for incremental sync: the client sends its `shardVersions`,
|
|
212
|
+
* the server computes and returns only the diff.
|
|
213
|
+
*
|
|
214
|
+
* @param clientVersions - Array of `{shardId, version, segmentIds}`,
|
|
215
|
+
* typically obtained from the client's `shardVersions` getter.
|
|
216
|
+
* @returns `Buffer` containing the LUCIDS binary delta blob.
|
|
217
|
+
*/
|
|
218
|
+
exportShardedDelta(clientVersions: Array<ShardVersion>): Buffer
|
|
219
|
+
/**
|
|
220
|
+
* Apply a sharded delta (LUCIDS blob) to this index.
|
|
221
|
+
*
|
|
222
|
+
* Merges the delta's segments into the local index, bringing it
|
|
223
|
+
* up to date with the server. Only modified shards are touched.
|
|
224
|
+
*
|
|
225
|
+
* @param data - LUCIDS binary blob from `exportShardedDelta()`.
|
|
226
|
+
*/
|
|
227
|
+
applyShardedDelta(data: Buffer): void
|
|
228
|
+
/**
|
|
229
|
+
* Export BM25 statistics for a query (for distributed search).
|
|
230
|
+
*
|
|
231
|
+
* In a distributed setup, each node exports its local BM25 stats.
|
|
232
|
+
* A coordinator merges them into global stats and sends them back
|
|
233
|
+
* for scoring with `searchWithGlobalStats()`.
|
|
234
|
+
*
|
|
235
|
+
* @param queryJson - JSON string of QueryConfig (same format as `search()` object).
|
|
236
|
+
* @returns JSON string of `ExportableStats` (document frequencies, doc counts).
|
|
237
|
+
*/
|
|
238
|
+
exportStats(queryJson: string): string
|
|
239
|
+
/**
|
|
240
|
+
* Search using externally-provided global BM25 stats (distributed mode).
|
|
241
|
+
*
|
|
242
|
+
* Scores are computed using the merged global stats instead of local-only
|
|
243
|
+
* stats, ensuring consistent ranking across nodes.
|
|
244
|
+
*
|
|
245
|
+
* @param queryJson - JSON string of QueryConfig.
|
|
246
|
+
* @param globalStatsJson - JSON string of merged `ExportableStats`
|
|
247
|
+
* from all nodes (obtained by merging `exportStats()` outputs).
|
|
248
|
+
* @param limit - Maximum number of results (default 10).
|
|
249
|
+
* @param highlights - If true, return highlight byte offsets per field.
|
|
250
|
+
* @returns `Array<SearchResult>` scored with global BM25 statistics.
|
|
251
|
+
*/
|
|
252
|
+
searchWithGlobalStats(queryJson: string, globalStatsJson: string, limit?: number | undefined | null, highlights?: boolean | undefined | null): Array<SearchResult>
|
|
70
253
|
}
|
package/lucivy.node
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lucivy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Fast BM25 full-text search with substring matching, fuzzy search, and regex — powered by Rust",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"README.md"
|
|
34
34
|
],
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@napi-rs/cli": "^2.18.
|
|
36
|
+
"@napi-rs/cli": "^2.18.4"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "napi build --release",
|