@infino-ai/infino 0.9.1 → 0.9.3
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/infino/index.d.ts +65 -0
- package/infino/index.js +68 -29
- package/infino/native.d.ts +26 -0
- package/package.json +7 -7
package/infino/index.d.ts
CHANGED
|
@@ -19,6 +19,60 @@ export type SchemaDescriptor = Record<string, string | {
|
|
|
19
19
|
}>;
|
|
20
20
|
/** Accepted shapes for `Table.append`. */
|
|
21
21
|
export type AppendData = RowRecord[] | arrow.Table | arrow.RecordBatch | Buffer | Uint8Array;
|
|
22
|
+
/** A column's index in the schema document: full-text or vector. */
|
|
23
|
+
export type ColumnIndex = {
|
|
24
|
+
kind: "fts";
|
|
25
|
+
analyzer: string;
|
|
26
|
+
stopwords?: string;
|
|
27
|
+
stemmer?: string;
|
|
28
|
+
positions: boolean;
|
|
29
|
+
stored: boolean;
|
|
30
|
+
k1: number;
|
|
31
|
+
b: number;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "vector";
|
|
34
|
+
metric: Metric;
|
|
35
|
+
rot_seed: string;
|
|
36
|
+
rerank_codec: string;
|
|
37
|
+
};
|
|
38
|
+
/** One field of the schema document. `type` is the document's type vocabulary (`"i64"`, `"large_utf8"`, …); a vector column carries `dim`. */
|
|
39
|
+
export interface SchemaField {
|
|
40
|
+
id: number;
|
|
41
|
+
name: string;
|
|
42
|
+
type: string;
|
|
43
|
+
dim?: number;
|
|
44
|
+
nullable: boolean;
|
|
45
|
+
index?: ColumnIndex;
|
|
46
|
+
converting_from?: {
|
|
47
|
+
type: string;
|
|
48
|
+
dim?: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** The schema document `Connection.schema` returns. */
|
|
52
|
+
export interface TableSchema {
|
|
53
|
+
schema_id: number;
|
|
54
|
+
last_field_id: number;
|
|
55
|
+
max_fields: number;
|
|
56
|
+
max_depth: number;
|
|
57
|
+
fields: SchemaField[];
|
|
58
|
+
tombstoned: number[];
|
|
59
|
+
}
|
|
60
|
+
/** One field of a schema patch: a new column (`type` required), or a change to a live one addressed by `id` or `name`. */
|
|
61
|
+
export interface SchemaFieldPatch {
|
|
62
|
+
id?: number;
|
|
63
|
+
name: string;
|
|
64
|
+
type?: string;
|
|
65
|
+
dim?: number;
|
|
66
|
+
nullable?: boolean;
|
|
67
|
+
index?: ColumnIndex;
|
|
68
|
+
dropped?: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** What `Connection.schema(name, patch)` accepts: the document's own shape, every key optional. */
|
|
71
|
+
export interface SchemaPatch {
|
|
72
|
+
fields?: SchemaFieldPatch[];
|
|
73
|
+
max_fields?: number;
|
|
74
|
+
max_depth?: number;
|
|
75
|
+
}
|
|
22
76
|
/**
|
|
23
77
|
* Storage and cache config the `connect` URI can't carry. All optional.
|
|
24
78
|
*
|
|
@@ -340,6 +394,17 @@ export declare class Connection {
|
|
|
340
394
|
/** Create a table from an apache-arrow `Schema` or `{ column: type }`. */
|
|
341
395
|
createTable(name: string, schema: arrow.Schema | SchemaDescriptor | Buffer, indexes: IndexSpec): Table;
|
|
342
396
|
openTable(name: string): Table;
|
|
397
|
+
/**
|
|
398
|
+
* The schema document of `name`: its fields with ids, types, nullability
|
|
399
|
+
* and indexes, the field cap and the `schema_id`. With `patch` (the same
|
|
400
|
+
* shape), merge it into the schema — or create the table from it when
|
|
401
|
+
* there is none — and return the document afterwards. A field is matched
|
|
402
|
+
* by `id` when it carries one and by `name` otherwise; an unmatched field
|
|
403
|
+
* is added, a different type changes the column, `dropped: true` retires
|
|
404
|
+
* it, and a field not mentioned is untouched. `expectedSchemaId` is a
|
|
405
|
+
* compare-and-set against the current `schema_id`.
|
|
406
|
+
*/
|
|
407
|
+
schema(name: string, patch?: SchemaPatch, expectedSchemaId?: number): TableSchema;
|
|
343
408
|
/** Drop a table. `purge` defaults to `true`, which also deletes the table's storage; pass `false` to only unregister it and keep the bytes. */
|
|
344
409
|
dropTable(name: string, purge?: boolean): void;
|
|
345
410
|
listTables(): string[];
|
package/infino/index.js
CHANGED
|
@@ -134,33 +134,58 @@ function buildColumn(field, rows) {
|
|
|
134
134
|
}
|
|
135
135
|
return arrow.vectorFromArray(values, field.type);
|
|
136
136
|
}
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
|
|
137
|
+
// A `bigint` has no JSON form, so it travels as a one-key object the
|
|
138
|
+
// replacer alone can produce and is then spliced back to the integer
|
|
139
|
+
// literal it is — exact, and an integer rather than a float or a string.
|
|
140
|
+
// A string in the data cannot be mistaken for one: `JSON.stringify`
|
|
141
|
+
// escapes the quotes of a user's string, and the pattern below matches
|
|
142
|
+
// only unescaped ones.
|
|
143
|
+
const BIGINT_KEY = "$infino$bigint";
|
|
144
|
+
function rowsToJson(rows) {
|
|
145
|
+
const text = JSON.stringify(rows, (_key, value) => typeof value === "bigint" ? { [BIGINT_KEY]: value.toString() } : value);
|
|
146
|
+
return text.replace(/\{"\$infino\$bigint":"(-?\d+)"\}/g, "$1");
|
|
147
|
+
}
|
|
148
|
+
// Values JSON has no faithful spelling for. A `Buffer` stringifies to
|
|
149
|
+
// `{"type":"Buffer","data":[...]}` and a `Date` to a string, so neither
|
|
150
|
+
// throws on the document path and neither arrives as what it was. Rows
|
|
151
|
+
// carrying them are refused with the route that does carry them, rather
|
|
152
|
+
// than written as the object or the string JSON turned them into.
|
|
153
|
+
function unspellableValue(rows) {
|
|
154
|
+
for (const row of rows) {
|
|
155
|
+
for (const value of Object.values(row)) {
|
|
156
|
+
if (typeof Buffer !== "undefined" && Buffer.isBuffer(value))
|
|
157
|
+
return "a Buffer";
|
|
158
|
+
if (value instanceof Uint8Array)
|
|
159
|
+
return "a Uint8Array";
|
|
160
|
+
if (value instanceof Date)
|
|
161
|
+
return "a Date";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
function writeInput(data) {
|
|
144
167
|
if (Buffer.isBuffer(data))
|
|
145
|
-
return data;
|
|
168
|
+
return { ipc: data };
|
|
146
169
|
if (data instanceof Uint8Array)
|
|
147
|
-
return Buffer.from(data);
|
|
148
|
-
let rows;
|
|
149
|
-
const d = data;
|
|
170
|
+
return { ipc: Buffer.from(data) };
|
|
150
171
|
if (Array.isArray(data)) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
throw new TypeError("append: expected an array of objects, an apache-arrow Table / RecordBatch, or an Arrow IPC Buffer");
|
|
172
|
+
const unspellable = unspellableValue(data);
|
|
173
|
+
if (unspellable) {
|
|
174
|
+
throw new TypeError(`append: ${unspellable} has no document form and would be written as what JSON ` +
|
|
175
|
+
"turned it into; pass an apache-arrow Table or RecordBatch to keep its type");
|
|
176
|
+
}
|
|
177
|
+
return { rows: rowsToJson(data) };
|
|
158
178
|
}
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
179
|
+
const d = data;
|
|
180
|
+
if (d && (Array.isArray(d.batches) || (d.schema && typeof d.numRows === "number"))) {
|
|
181
|
+
const rows = Array.from(d).map((r) => r.toJSON());
|
|
182
|
+
const fields = d.schema.fields.map((f) => new arrow.Field(f.name, nativeTypeFromForeign(f.type), f.nullable));
|
|
183
|
+
const cols = {};
|
|
184
|
+
for (const field of fields)
|
|
185
|
+
cols[field.name] = buildColumn(field, rows);
|
|
186
|
+
return { ipc: Buffer.from(arrow.tableToIPC(new arrow.Table(cols), STREAM)) };
|
|
187
|
+
}
|
|
188
|
+
throw new TypeError("append: expected an array of objects, an apache-arrow Table / RecordBatch, or an Arrow IPC Buffer");
|
|
164
189
|
}
|
|
165
190
|
// A Decimal128 value renders as a 4×u32 little-endian array in records.
|
|
166
191
|
// Convert (scale-0 -> integer) to a `bigint`, matching token/exact match.
|
|
@@ -247,8 +272,8 @@ class Table {
|
|
|
247
272
|
* append == one commit.
|
|
248
273
|
*/
|
|
249
274
|
append(data) {
|
|
250
|
-
const
|
|
251
|
-
guard(this.remote, () => this.inner.append(ipc));
|
|
275
|
+
const input = writeInput(data);
|
|
276
|
+
guard(this.remote, () => ("rows" in input ? this.inner.appendRows(input.rows) : this.inner.append(input.ipc)));
|
|
252
277
|
}
|
|
253
278
|
/**
|
|
254
279
|
* {@link append}, naming the source the rows came from. The superfiles
|
|
@@ -259,8 +284,8 @@ class Table {
|
|
|
259
284
|
* a hosted table.
|
|
260
285
|
*/
|
|
261
286
|
appendNamed(data, sourceName) {
|
|
262
|
-
const
|
|
263
|
-
guard(this.remote, () => this.inner.appendNamed(ipc, sourceName));
|
|
287
|
+
const input = writeInput(data);
|
|
288
|
+
guard(this.remote, () => "rows" in input ? this.inner.appendRowsNamed(input.rows, sourceName) : this.inner.appendNamed(input.ipc, sourceName));
|
|
264
289
|
}
|
|
265
290
|
bm25Search(column, query, k, opts = {}) {
|
|
266
291
|
const buf = guard(this.remote, () => this.inner.bm25Search(column, query, k, opts.mode, opts.stats, opts.projection, opts.k1, opts.b));
|
|
@@ -293,8 +318,8 @@ class Table {
|
|
|
293
318
|
* `data` (same shapes as `append`), 1:1 — the matched count must equal the
|
|
294
319
|
* replacement-row count. Requires durable storage (not `memory://`). */
|
|
295
320
|
update(predicate, data) {
|
|
296
|
-
const
|
|
297
|
-
return guard(this.remote, () => this.inner.update(predicate, ipc));
|
|
321
|
+
const input = writeInput(data);
|
|
322
|
+
return guard(this.remote, () => "rows" in input ? this.inner.updateRows(predicate, input.rows) : this.inner.update(predicate, input.ipc));
|
|
298
323
|
}
|
|
299
324
|
/** Delete rows matching a SQL predicate (e.g. `"status = 'spam'"`).
|
|
300
325
|
* Requires durable storage (not `memory://`). */
|
|
@@ -373,6 +398,20 @@ class Connection {
|
|
|
373
398
|
openTable(name) {
|
|
374
399
|
return new Table(guard(this.remote, () => this.inner.openTable(name)), this.remote);
|
|
375
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* The schema document of `name`: its fields with ids, types, nullability
|
|
403
|
+
* and indexes, the field cap and the `schema_id`. With `patch` (the same
|
|
404
|
+
* shape), merge it into the schema — or create the table from it when
|
|
405
|
+
* there is none — and return the document afterwards. A field is matched
|
|
406
|
+
* by `id` when it carries one and by `name` otherwise; an unmatched field
|
|
407
|
+
* is added, a different type changes the column, `dropped: true` retires
|
|
408
|
+
* it, and a field not mentioned is untouched. `expectedSchemaId` is a
|
|
409
|
+
* compare-and-set against the current `schema_id`.
|
|
410
|
+
*/
|
|
411
|
+
schema(name, patch, expectedSchemaId) {
|
|
412
|
+
const text = guard(this.remote, () => this.inner.schema(name, patch === undefined ? undefined : JSON.stringify(patch), expectedSchemaId));
|
|
413
|
+
return JSON.parse(text);
|
|
414
|
+
}
|
|
376
415
|
/** Drop a table. `purge` defaults to `true`, which also deletes the table's storage; pass `false` to only unregister it and keep the bytes. */
|
|
377
416
|
dropTable(name, purge) {
|
|
378
417
|
guard(this.remote, () => this.inner.dropTable(name, purge));
|
package/infino/native.d.ts
CHANGED
|
@@ -300,6 +300,14 @@ export declare class Connection {
|
|
|
300
300
|
createTable(name: string, schema: Buffer, indexes: IndexSpec): Table
|
|
301
301
|
/** Open an existing table by name. */
|
|
302
302
|
openTable(name: string): Table
|
|
303
|
+
/**
|
|
304
|
+
* The table's schema document as JSON text, or — with `patch_json`, a
|
|
305
|
+
* patch in the same shape — the document after merging the patch into
|
|
306
|
+
* the schema (creating the table when there is none). The JS wrapper
|
|
307
|
+
* parses the result and stringifies the patch; `expected_schema_id` is
|
|
308
|
+
* a compare-and-set against the current `schema_id`.
|
|
309
|
+
*/
|
|
310
|
+
schema(name: string, patchJson?: string | undefined | null, expectedSchemaId?: number | undefined | null): string
|
|
303
311
|
/**
|
|
304
312
|
* Drop a table. `purge` defaults to `true`, which also deletes the
|
|
305
313
|
* table's storage subtree after the catalog commit, reclaiming the bytes;
|
|
@@ -326,6 +334,24 @@ export declare class Table {
|
|
|
326
334
|
* concatenated into one commit; an empty stream is a no-op.
|
|
327
335
|
*/
|
|
328
336
|
append(data: Buffer): void
|
|
337
|
+
/**
|
|
338
|
+
* Append rows given as a JSON array of objects (the text the JS wrapper
|
|
339
|
+
* produces with `JSON.stringify`): nested objects flatten to dot paths,
|
|
340
|
+
* arrays become list columns, a key the table has not seen adds a
|
|
341
|
+
* column typed from its values, and a value whose type disagrees with
|
|
342
|
+
* the column's is refused.
|
|
343
|
+
*/
|
|
344
|
+
appendRows(rowsJson: string): void
|
|
345
|
+
/**
|
|
346
|
+
* `appendRows`, naming the source the rows came from as `appendNamed`
|
|
347
|
+
* does.
|
|
348
|
+
*/
|
|
349
|
+
appendRowsNamed(rowsJson: string, sourceName: string): void
|
|
350
|
+
/**
|
|
351
|
+
* `update` with the replacement rows as a JSON array of objects, mapped
|
|
352
|
+
* as `appendRows` maps them.
|
|
353
|
+
*/
|
|
354
|
+
updateRows(predicate: string, rowsJson: string): MutationStats
|
|
329
355
|
/**
|
|
330
356
|
* `append`, naming the source the rows came from: the superfiles this
|
|
331
357
|
* commit writes are keyed `data/<stem>-<uuid>.sf.parquet`, with the stem
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@infino-ai/infino",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.3",
|
|
4
4
|
"description": "Fast search on object storage — SQL, full-text, and vector search.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -82,12 +82,12 @@
|
|
|
82
82
|
"apache-arrow": "^17"
|
|
83
83
|
},
|
|
84
84
|
"optionalDependencies": {
|
|
85
|
-
"infx-darwin-x64": "0.9.
|
|
86
|
-
"infx-darwin-arm64": "0.9.
|
|
87
|
-
"infx-linux-x64-gnu": "0.9.
|
|
88
|
-
"infx-linux-arm64-gnu": "0.9.
|
|
89
|
-
"infx-linux-x64-musl": "0.9.
|
|
90
|
-
"infx-linux-arm64-musl": "0.9.
|
|
85
|
+
"infx-darwin-x64": "0.9.3",
|
|
86
|
+
"infx-darwin-arm64": "0.9.3",
|
|
87
|
+
"infx-linux-x64-gnu": "0.9.3",
|
|
88
|
+
"infx-linux-arm64-gnu": "0.9.3",
|
|
89
|
+
"infx-linux-x64-musl": "0.9.3",
|
|
90
|
+
"infx-linux-arm64-musl": "0.9.3"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
93
|
"@napi-rs/cli": "^2.18.0",
|