uql-orm 0.67.0 → 0.68.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/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +3 -3
- package/dist/cockroachdb/cockroachDialect.d.ts +6 -4
- package/dist/cockroachdb/cockroachDialect.js +7 -3
- package/dist/d1/d1SqliteDialect.d.ts +1 -1
- package/dist/d1/d1SqliteDialect.js +1 -1
- package/dist/dialect/abstractSqlDialect.d.ts +107 -97
- package/dist/dialect/abstractSqlDialect.js +250 -293
- package/dist/dialect/hydrateColumn.js +33 -30
- package/dist/dialect/jsonSql.d.ts +32 -23
- package/dist/dialect/jsonSql.js +42 -31
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +19 -23
- package/dist/dialect/mysqlLikeSqlDialect.js +34 -51
- package/dist/dialect/pgLikeSqlDialect.d.ts +24 -17
- package/dist/dialect/pgLikeSqlDialect.js +53 -26
- package/dist/dialect/vectorCast.d.ts +2 -0
- package/dist/dialect/vectorCast.js +25 -5
- package/dist/dialect/vectorSqlDialect.d.ts +8 -8
- package/dist/dialect/vectorSqlDialect.js +11 -11
- package/dist/index.d.ts +1 -0
- package/dist/maria/mariaDialect.d.ts +16 -11
- package/dist/maria/mariaDialect.js +23 -19
- package/dist/migrate/ddl/indexDdl.d.ts +3 -1
- package/dist/migrate/ddl/indexDdl.js +5 -1
- package/dist/migrate/ddl/mysqlIndexDdl.d.ts +7 -2
- package/dist/migrate/ddl/mysqlIndexDdl.js +28 -6
- package/dist/migrate/ddl/pgIndexDdl.d.ts +0 -9
- package/dist/migrate/ddl/pgIndexDdl.js +2 -6
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +12 -1
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +13 -3
- package/dist/migrate/introspection/mssqlIntrospector.d.ts +2 -9
- package/dist/migrate/introspection/mssqlIntrospector.js +2 -9
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +2 -9
- package/dist/migrate/introspection/mysqlIntrospector.js +2 -8
- package/dist/mongo/mongoDialect.d.ts +22 -10
- package/dist/mongo/mongoDialect.js +86 -38
- package/dist/mssql/mssqlDialect.d.ts +22 -18
- package/dist/mssql/mssqlDialect.js +54 -43
- package/dist/mysql/mysqlDialect.d.ts +16 -1
- package/dist/mysql/mysqlDialect.js +18 -2
- package/dist/sqlite/sqliteDialect.d.ts +17 -16
- package/dist/sqlite/sqliteDialect.js +35 -39
- package/dist/type/dialect.d.ts +0 -4
- package/dist/type/entity.d.ts +8 -3
- package/dist/type/vector.d.ts +5 -7
- package/dist/util/dialect.util.d.ts +2 -0
- package/dist/util/dialect.util.js +6 -2
- package/dist/util/object.util.d.ts +2 -4
- package/dist/util/object.util.js +4 -9
- package/package.json +1 -1
- package/dist/dialect/jsonArrayElemMatchUtils.d.ts +0 -2
- package/dist/dialect/jsonArrayElemMatchUtils.js +0 -7
- package/dist/dialect/pgVectorMetrics.d.ts +0 -13
- package/dist/dialect/pgVectorMetrics.js +0 -17
- package/dist/maria/mariaVectorMetrics.d.ts +0 -8
- package/dist/maria/mariaVectorMetrics.js +0 -10
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { JsonSlot } from '../dialect/jsonSql.js';
|
|
1
2
|
import { MysqlLikeSqlDialect } from '../dialect/mysqlLikeSqlDialect.js';
|
|
3
|
+
import type { QueryContext } from '../type/index.js';
|
|
2
4
|
export declare class MySqlDialect extends MysqlLikeSqlDialect {
|
|
3
5
|
readonly dialectName = "mysql";
|
|
4
6
|
/**
|
|
@@ -6,6 +8,19 @@ export declare class MySqlDialect extends MysqlLikeSqlDialect {
|
|
|
6
8
|
* "subject to removal in a future version"; aliasing the inserted row (8.0.19+) is its replacement.
|
|
7
9
|
*/
|
|
8
10
|
protected readonly upsertNewRowAlias = "_uql_new";
|
|
9
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* 26.7 may plan the correlated `JSON_TABLE` as a semijoin materialized once for the whole table, which
|
|
13
|
+
* answers every row with one row's elements. Verified in `mysqlJsonElemMatch.test.ts`.
|
|
14
|
+
*/
|
|
15
|
+
protected readonly jsonElemHint = "/*+ NO_SEMIJOIN() */";
|
|
16
|
+
/**
|
|
17
|
+
* `JSON_OVERLAPS`, which a multi-valued index serves: verified in `mysqlJsonArrayIndex.test.ts`. It reads
|
|
18
|
+
* a scalar as an array of one, so the value must be an array.
|
|
19
|
+
*/
|
|
20
|
+
protected jsonAny(ctx: QueryContext, slot: JsonSlot, values: readonly unknown[]): string;
|
|
21
|
+
/**
|
|
22
|
+
* A `SET_VAR` hint, which MySQL reads only right after the statement's own `SELECT` keyword - before
|
|
23
|
+
* `DISTINCT`, and never in a subquery - so it is anchored to the start rather than found.
|
|
24
|
+
*/
|
|
10
25
|
protected applySettings(sql: string, settings: readonly string[]): string;
|
|
11
26
|
}
|
|
@@ -7,8 +7,24 @@ export class MySqlDialect extends MysqlLikeSqlDialect {
|
|
|
7
7
|
* "subject to removal in a future version"; aliasing the inserted row (8.0.19+) is its replacement.
|
|
8
8
|
*/
|
|
9
9
|
upsertNewRowAlias = UPSERT_NEW_ROW_ALIAS;
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* 26.7 may plan the correlated `JSON_TABLE` as a semijoin materialized once for the whole table, which
|
|
12
|
+
* answers every row with one row's elements. Verified in `mysqlJsonElemMatch.test.ts`.
|
|
13
|
+
*/
|
|
14
|
+
jsonElemHint = '/*+ NO_SEMIJOIN() */';
|
|
15
|
+
/**
|
|
16
|
+
* `JSON_OVERLAPS`, which a multi-valued index serves: verified in `mysqlJsonArrayIndex.test.ts`. It reads
|
|
17
|
+
* a scalar as an array of one, so the value must be an array.
|
|
18
|
+
*/
|
|
19
|
+
jsonAny(ctx, slot, values) {
|
|
20
|
+
const overlaps = `JSON_OVERLAPS(${this.jsonValue(slot)}, ${this.addValue(ctx, JSON.stringify(values))})`;
|
|
21
|
+
return `(${this.jsonIsArray(slot)} AND ${overlaps})`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A `SET_VAR` hint, which MySQL reads only right after the statement's own `SELECT` keyword - before
|
|
25
|
+
* `DISTINCT`, and never in a subquery - so it is anchored to the start rather than found.
|
|
26
|
+
*/
|
|
11
27
|
applySettings(sql, settings) {
|
|
12
|
-
return sql.replace(
|
|
28
|
+
return sql.replace(/^SELECT /, `SELECT /*+ ${settings.map((setting) => `SET_VAR(${setting})`).join(' ')} */ `);
|
|
13
29
|
}
|
|
14
30
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AbstractSqlDialect, type DerivedRelation, type HydrateKind, type RelationRows } from '../dialect/abstractSqlDialect.js';
|
|
2
|
-
import
|
|
2
|
+
import { type JsonAccessMode, type JsonSlot } from '../dialect/jsonSql.js';
|
|
3
|
+
import type { EntityMeta, FieldOptions, QueryContext, QueryPager, QueryTextSearchOptions, SqlDialectFeatures, Type, VectorDistance, VectorMetric } from '../type/index.js';
|
|
3
4
|
/** What SQLite and the engines derived from it have. */
|
|
4
5
|
export declare const SQLITE_FEATURES: SqlDialectFeatures;
|
|
5
6
|
export declare class SqliteDialect extends AbstractSqlDialect {
|
|
@@ -58,25 +59,25 @@ export declare class SqliteDialect extends AbstractSqlDialect {
|
|
|
58
59
|
* FTS5 virtual table (UQL does not create those; declare it outside your entities).
|
|
59
60
|
*/
|
|
60
61
|
protected appendTextSearch<E>(ctx: QueryContext, entity: Type<E>, meta: EntityMeta<E>, search: QueryTextSearchOptions<E>): void;
|
|
62
|
+
protected jsonLength(slot: JsonSlot): string;
|
|
63
|
+
/** `JSON_EACH` walks the array at the path itself, each element's `fullkey` naming it from the column. */
|
|
64
|
+
protected jsonElemFrom(slot: JsonSlot, alias: string): string;
|
|
65
|
+
protected jsonIsArray(slot: JsonSlot): string;
|
|
66
|
+
/** An object element's `value` is its JSON text, which its fields are paths into. */
|
|
67
|
+
protected jsonElemDoc(alias: string): string;
|
|
61
68
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* vs `"a"`), flattens booleans to 0/1, and stringifies objects.
|
|
69
|
+
* A scalar element's `value` is already typed, so a number and a string compare as such. Its JSON form
|
|
70
|
+
* is read back from the column at the element's own `fullkey`, since `value` flattens a boolean to 0/1.
|
|
65
71
|
*/
|
|
66
|
-
protected
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
protected
|
|
71
|
-
protected getJsonPathScalarExpr(escapedColumn: string, jsonPathStr: string): string;
|
|
72
|
+
protected jsonElemValue(slot: JsonSlot, alias: string, mode: JsonAccessMode): string;
|
|
73
|
+
/** `JSON_EXTRACT` already answers a number as one, and orders it by value. */
|
|
74
|
+
protected readonly jsonSortModes: readonly JsonAccessMode[];
|
|
75
|
+
/** `->` for the JSON value, and `JSON_EXTRACT` for SQLite's own: a number as a number, a string as text. */
|
|
76
|
+
protected jsonPathReading(escapedColumn: string, path: string, mode: 'json' | 'text'): string;
|
|
72
77
|
protected numericCast(expr: string): string;
|
|
73
78
|
protected jsonCast(operand: string): string;
|
|
74
|
-
/**
|
|
75
|
-
|
|
76
|
-
* through `->` at their own `fullkey` so each keeps its JSON type - `JSON_EACH`'s `value` would
|
|
77
|
-
* flatten booleans to 0/1 and stringify objects.
|
|
78
|
-
*/
|
|
79
|
-
protected jsonPullKey(ctx: QueryContext, expr: string, escapedCol: string, key: string, value: unknown): string;
|
|
79
|
+
/** `JSON` keeps each element's JSON type in the array, which answers `[]` for no rows. */
|
|
80
|
+
protected jsonArrayOf(elem: string): string;
|
|
80
81
|
protected jsonSet(ctx: QueryContext, expr: string, set: Record<string, unknown>, field?: FieldOptions): string;
|
|
81
82
|
/** `[#]` appends, creating the array where it is absent: `JSON_SET`, since Turso's `JSON_INSERT` will not touch an existing array. */
|
|
82
83
|
protected jsonPush(ctx: QueryContext, expr: string, push: Record<string, unknown>): string;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { AbstractSqlDialect, } from '../dialect/abstractSqlDialect.js';
|
|
2
|
-
import { JSON_ELEM_ALIAS, JSON_PULL_ALIAS } from '../dialect/aliases.js';
|
|
3
2
|
import { BYTES_PREFIX } from '../dialect/hydrateColumn.js';
|
|
4
|
-
import { chainedCall, groupsPerCall,
|
|
3
|
+
import { chainedCall, groupsPerCall, jsonSetCall, jsonPath, jsonArraySlotArgs, jsonSlotArgs, jsonRemoveCall, jsonSetTarget, } from '../dialect/jsonSql.js';
|
|
5
4
|
import { textSearchFields } from '../util/dialect.util.js';
|
|
6
5
|
import { columnFamily, isIntegerColumn } from '../util/field.util.js';
|
|
7
6
|
/** What SQLite and the engines derived from it have. */
|
|
@@ -25,8 +24,6 @@ export const SQLITE_FEATURES = {
|
|
|
25
24
|
rowLockOf: true,
|
|
26
25
|
orderedUpsertReturning: true,
|
|
27
26
|
orderedJsonAggregates: true,
|
|
28
|
-
partialJsonContainment: false,
|
|
29
|
-
typedJsonElements: true,
|
|
30
27
|
narrowVectorTypes: false,
|
|
31
28
|
vectorTuningNeedsTransaction: false,
|
|
32
29
|
serialDeclaresPrimaryKey: true,
|
|
@@ -126,32 +123,38 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
126
123
|
ctx.append(`${this.escapedTableName(meta)} MATCH {${columns.join(' ')}} : `);
|
|
127
124
|
ctx.addValue(search.$value);
|
|
128
125
|
}
|
|
126
|
+
jsonLength(slot) {
|
|
127
|
+
return `JSON_ARRAY_LENGTH(${jsonArraySlotArgs(slot, this.jsonIsArray(slot))})`;
|
|
128
|
+
}
|
|
129
|
+
/** `JSON_EACH` walks the array at the path itself, each element's `fullkey` naming it from the column. */
|
|
130
|
+
jsonElemFrom(slot, alias) {
|
|
131
|
+
return `JSON_EACH(${jsonArraySlotArgs(slot, this.jsonIsArray(slot))}) ${alias}`;
|
|
132
|
+
}
|
|
133
|
+
jsonIsArray(slot) {
|
|
134
|
+
return `JSON_TYPE(${jsonSlotArgs(slot)}) = 'array'`;
|
|
135
|
+
}
|
|
136
|
+
/** An object element's `value` is its JSON text, which its fields are paths into. */
|
|
137
|
+
jsonElemDoc(alias) {
|
|
138
|
+
return `${alias}.value`;
|
|
139
|
+
}
|
|
129
140
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* vs `"a"`), flattens booleans to 0/1, and stringifies objects.
|
|
141
|
+
* A scalar element's `value` is already typed, so a number and a string compare as such. Its JSON form
|
|
142
|
+
* is read back from the column at the element's own `fullkey`, since `value` flattens a boolean to 0/1.
|
|
133
143
|
*/
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const conditions = value.map((val) => jsonElemExists(from, [`${jsonField} -> ${alias}.fullkey = ${this.jsonScalarParam(ctx, val)}`]));
|
|
138
|
-
return `(${conditions.join(' AND ')})`;
|
|
139
|
-
}
|
|
140
|
-
jsonSize(ctx, jsonField, value) {
|
|
141
|
-
return this.buildFragment(ctx, (fragmentCtx) => this.buildSizeComparison(fragmentCtx, () => fragmentCtx.append(`JSON_ARRAY_LENGTH(${jsonField})`), value));
|
|
142
|
-
}
|
|
143
|
-
/** `JSON_EACH` yields both scalar and object elements, so one form covers each case. */
|
|
144
|
-
jsonElemFrom(jsonField, _fields, alias) {
|
|
145
|
-
return `JSON_EACH(${jsonField}) ${alias}`;
|
|
146
|
-
}
|
|
147
|
-
jsonElemRef(alias, field, asJson = false) {
|
|
148
|
-
if (field === undefined) {
|
|
149
|
-
return `${alias}.value`;
|
|
144
|
+
jsonElemValue(slot, alias, mode) {
|
|
145
|
+
if (mode === 'json') {
|
|
146
|
+
return `${slot.base} -> ${alias}.fullkey`;
|
|
150
147
|
}
|
|
151
|
-
|
|
148
|
+
const value = this.jsonElemDoc(alias);
|
|
149
|
+
return mode === 'numeric' ? this.numericCast(value) : value;
|
|
152
150
|
}
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
/** `JSON_EXTRACT` already answers a number as one, and orders it by value. */
|
|
152
|
+
jsonSortModes = ['text'];
|
|
153
|
+
/** `->` for the JSON value, and `JSON_EXTRACT` for SQLite's own: a number as a number, a string as text. */
|
|
154
|
+
jsonPathReading(escapedColumn, path, mode) {
|
|
155
|
+
return mode === 'json'
|
|
156
|
+
? `(${escapedColumn} -> ${jsonPath(path)})`
|
|
157
|
+
: `JSON_EXTRACT(${escapedColumn}, ${jsonPath(path)})`;
|
|
155
158
|
}
|
|
156
159
|
numericCast(expr) {
|
|
157
160
|
return `CAST(${expr} AS REAL)`;
|
|
@@ -159,25 +162,18 @@ export class SqliteDialect extends AbstractSqlDialect {
|
|
|
159
162
|
jsonCast(operand) {
|
|
160
163
|
return `JSON(${operand})`;
|
|
161
164
|
}
|
|
162
|
-
/**
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
* flatten booleans to 0/1 and stringify objects.
|
|
166
|
-
*/
|
|
167
|
-
jsonPullKey(ctx, expr, escapedCol, key, value) {
|
|
168
|
-
const path = jsonPath(key);
|
|
169
|
-
const elem = `${escapedCol} -> ${JSON_PULL_ALIAS}.fullkey`;
|
|
170
|
-
const kept = `SELECT JSON_GROUP_ARRAY(JSON(${elem})) FROM JSON_EACH(${escapedCol}, ${path}) ${JSON_PULL_ALIAS} WHERE ${elem} <> ${this.jsonScalarParam(ctx, value)}`;
|
|
171
|
-
return `JSON_REPLACE(${expr}, ${path}, (${kept}))`;
|
|
165
|
+
/** `JSON` keeps each element's JSON type in the array, which answers `[]` for no rows. */
|
|
166
|
+
jsonArrayOf(elem) {
|
|
167
|
+
return `JSON_GROUP_ARRAY(JSON(${elem}))`;
|
|
172
168
|
}
|
|
173
169
|
jsonSet(ctx, expr, set, field) {
|
|
174
|
-
return
|
|
170
|
+
return jsonSetCall((value) => this.jsonScalarParam(ctx, value), jsonSetTarget(expr, field, `'{}'`), set, this.maxFunctionArgs);
|
|
175
171
|
}
|
|
176
172
|
/** `[#]` appends, creating the array where it is absent: `JSON_SET`, since Turso's `JSON_INSERT` will not touch an existing array. */
|
|
177
173
|
jsonPush(ctx, expr, push) {
|
|
178
|
-
return
|
|
174
|
+
return jsonSetCall((value) => this.jsonScalarParam(ctx, value), expr, push, this.maxFunctionArgs, '[#]');
|
|
179
175
|
}
|
|
180
176
|
jsonUnset(_ctx, expr, unset) {
|
|
181
|
-
return jsonRemoveCall(
|
|
177
|
+
return jsonRemoveCall(expr, unset, this.maxFunctionArgs);
|
|
182
178
|
}
|
|
183
179
|
}
|
package/dist/type/dialect.d.ts
CHANGED
|
@@ -135,10 +135,6 @@ export interface SqlDialectFeatures extends DialectFeatures {
|
|
|
135
135
|
readonly orderedUpsertReturning: boolean;
|
|
136
136
|
/** Whether a JSON aggregate takes an `ORDER BY` of its own; where not, a relation's rows keep their derived table's order. */
|
|
137
137
|
readonly orderedJsonAggregates: boolean;
|
|
138
|
-
/** Whether JSON array containment matches an object element that merely includes the given keys, as `@>` does. */
|
|
139
|
-
readonly partialJsonContainment: boolean;
|
|
140
|
-
/** Whether an exploded scalar JSON element keeps its SQL type, as SQLite's `JSON_EACH` does. */
|
|
141
|
-
readonly typedJsonElements: boolean;
|
|
142
138
|
/** Whether the engine has pgvector's `halfvec` and `sparsevec`; elsewhere both map onto `vector`. */
|
|
143
139
|
readonly narrowVectorTypes: boolean;
|
|
144
140
|
/** Whether an ANN index's tuning `SET` applies only inside a transaction, as `SET LOCAL` does. */
|
package/dist/type/entity.d.ts
CHANGED
|
@@ -181,7 +181,10 @@ export type FieldOptions<V = TsTypeOf<FieldType>, E = unknown> = {
|
|
|
181
181
|
readonly type?: FieldType;
|
|
182
182
|
/** A vector column's dimensions: `@Field({ type: 'vector', dimensions: 1536 })`. */
|
|
183
183
|
readonly dimensions?: number;
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* The metric a vector search on this field uses unless it names its own `$distance`; by default its
|
|
186
|
+
* vector index's metric, else `'cosine'`.
|
|
187
|
+
*/
|
|
185
188
|
readonly distance?: VectorDistance;
|
|
186
189
|
/** The entity this column is a foreign key to. */
|
|
187
190
|
readonly references?: EntityGetter;
|
|
@@ -445,10 +448,12 @@ export type IndexJsonPath = {
|
|
|
445
448
|
readonly path: string;
|
|
446
449
|
/** How the value is read, matching what the queries over it compare against. */
|
|
447
450
|
readonly type: FieldType;
|
|
451
|
+
/** Length of a string value, which MySQL keys as `CHAR(n)` and so requires. */
|
|
452
|
+
readonly length?: number;
|
|
448
453
|
};
|
|
449
454
|
/**
|
|
450
|
-
* MySQL's multi-valued index, one key per element of the JSON array at `path`,
|
|
451
|
-
* `$elemMatch`
|
|
455
|
+
* MySQL's multi-valued index, one key per element of the JSON array at `path`, which `$all` and an
|
|
456
|
+
* `$elemMatch` on one value or several use; refused on any other engine.
|
|
452
457
|
* @example `@Index((user) => [{ column: user.tags, jsonArray: { type: String, length: 64 } }])`
|
|
453
458
|
*/
|
|
454
459
|
export type IndexJsonArray = {
|
package/dist/type/vector.d.ts
CHANGED
|
@@ -28,20 +28,18 @@ export interface QueryVectorSearch extends QueryVectorQuery {
|
|
|
28
28
|
*/
|
|
29
29
|
export type WithDistance<E, K extends string = '_distance'> = E & Record<K, number>;
|
|
30
30
|
/**
|
|
31
|
-
* How a dialect spells a metric: an operator
|
|
32
|
-
*
|
|
31
|
+
* How a dialect spells a metric: an operator, or a function taking the metric by name where `metricArg`
|
|
32
|
+
* says so, and `index`, how the dialect's vector index names it where it builds one. One map serves the
|
|
33
|
+
* query and the index alike, so its keys say which metrics exist.
|
|
33
34
|
*/
|
|
34
35
|
export type VectorMetric = {
|
|
35
36
|
readonly op: string;
|
|
36
|
-
readonly
|
|
37
|
+
readonly index: string;
|
|
37
38
|
} | {
|
|
38
39
|
readonly fn: string;
|
|
39
40
|
readonly metricArg?: string;
|
|
41
|
+
readonly index?: string;
|
|
40
42
|
};
|
|
41
|
-
/** The operator form, for the pgvector-family dialects whose index DDL also needs `opsSuffix`. */
|
|
42
|
-
export type VectorOperatorMetric = Extract<VectorMetric, {
|
|
43
|
-
op: string;
|
|
44
|
-
}>;
|
|
45
43
|
/** The error every dialect throws for a metric it lacks. */
|
|
46
44
|
export declare function unsupportedVectorMetric(dialectName: string, distance: VectorDistance, indexName?: string): TypeError;
|
|
47
45
|
/**
|
|
@@ -138,6 +138,8 @@ export declare function parseGroupMap<E>(group?: QueryGroupMap<E>, select?: Quer
|
|
|
138
138
|
* indices. Shared by the SQL and MongoDB builders, whose `$where` and `$having` all face this.
|
|
139
139
|
*/
|
|
140
140
|
export declare function isOperatorMap(value: unknown): value is Record<string, unknown>;
|
|
141
|
+
/** A JSON object, matched by what it holds: a plain object with no operator key. */
|
|
142
|
+
export declare function isJsonObject(value: unknown): value is Record<string, unknown>;
|
|
141
143
|
/**
|
|
142
144
|
* A page operand, checked before it reaches an engine. These arrive from page arithmetic and from
|
|
143
145
|
* REST query strings (`parseQueryParams` yields `NaN` for a non-numeric one), and each engine's own
|
|
@@ -3,7 +3,7 @@ import { soleIdOf } from '../entity/metadata/definition.js';
|
|
|
3
3
|
import { QueryRaw, resolveAggregateOp, SOFT_DELETE_FILTER, } from '../type/index.js';
|
|
4
4
|
import { VECTOR_INDEX_TYPES } from '../type/vector.js';
|
|
5
5
|
import { isDatabaseWritten } from './field.util.js';
|
|
6
|
-
import { entityName, getFieldKeys, getKeys, hasKeys, isScalarId, isRecord, isWhereMap, someKey, } from './object.util.js';
|
|
6
|
+
import { entityName, getFieldKeys, getKeys, hasKeys, isOperatorObject, isScalarId, isRecord, isWhereMap, someKey, } from './object.util.js';
|
|
7
7
|
/** The keys of `payload` a write persists as columns. */
|
|
8
8
|
export function filterFieldKeys(meta, payload, callbackKey) {
|
|
9
9
|
return getKeys(payload).filter((key) => {
|
|
@@ -226,7 +226,7 @@ const JSON_UPDATE_OPS = [
|
|
|
226
226
|
];
|
|
227
227
|
/** Type guard: checks whether an update payload value is a JSON operator object. */
|
|
228
228
|
export function isJsonUpdateOp(value) {
|
|
229
|
-
return value
|
|
229
|
+
return isRecord(value) && someKey(value, (key) => JSON_UPDATE_OPS.includes(key));
|
|
230
230
|
}
|
|
231
231
|
/**
|
|
232
232
|
* The `$where` naming rows by key: a bare value names the one key column (refused on a composite), a
|
|
@@ -386,6 +386,10 @@ export function isOperatorMap(value) {
|
|
|
386
386
|
!(value instanceof Uint8Array) &&
|
|
387
387
|
!(value instanceof QueryRaw));
|
|
388
388
|
}
|
|
389
|
+
/** A JSON object, matched by what it holds: a plain object with no operator key. */
|
|
390
|
+
export function isJsonObject(value) {
|
|
391
|
+
return isOperatorMap(value) && !isOperatorObject(value);
|
|
392
|
+
}
|
|
389
393
|
/**
|
|
390
394
|
* A page operand, checked before it reaches an engine. These arrive from page arithmetic and from
|
|
391
395
|
* REST query strings (`parseQueryParams` yields `NaN` for a non-numeric one), and each engine's own
|
|
@@ -9,12 +9,10 @@ export declare function hasKeys<T>(obj: T): obj is NonNullable<T>;
|
|
|
9
9
|
* without materializing a key array (unlike `Object.keys(obj).some(pred)`).
|
|
10
10
|
*/
|
|
11
11
|
export declare function someKey<T extends object>(obj: T, pred: (key: keyof T & string) => boolean): boolean;
|
|
12
|
-
/** Whether
|
|
13
|
-
export declare function
|
|
12
|
+
/** Whether `key` names an operator (`$eq`, `$push`...) rather than a field. */
|
|
13
|
+
export declare function isOperatorKey(key: string): boolean;
|
|
14
14
|
/** Whether `value` is a non-empty object with an operator key (`$eq`, `$push`...): the one test every dialect classifies with. */
|
|
15
15
|
export declare function isOperatorObject(value: unknown): value is Record<string, unknown>;
|
|
16
|
-
/** Whether every key of the non-empty object `value` is an operator (no plain field names mixed in). */
|
|
17
|
-
export declare function isOperatorOnlyObject(value: unknown): value is Record<string, unknown>;
|
|
18
16
|
/** Whether `value` is an object that is not an array, whose keys can be read. */
|
|
19
17
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
20
18
|
export declare function getKeys<T extends object>(obj: T | null | undefined): (keyof T & string)[];
|
package/dist/util/object.util.js
CHANGED
|
@@ -32,18 +32,13 @@ export function someKey(obj, pred) {
|
|
|
32
32
|
}
|
|
33
33
|
return false;
|
|
34
34
|
}
|
|
35
|
-
/** Whether
|
|
36
|
-
export function
|
|
37
|
-
return
|
|
35
|
+
/** Whether `key` names an operator (`$eq`, `$push`...) rather than a field. */
|
|
36
|
+
export function isOperatorKey(key) {
|
|
37
|
+
return key.startsWith('$');
|
|
38
38
|
}
|
|
39
|
-
const isOperatorKey = (key) => key.startsWith('$');
|
|
40
39
|
/** Whether `value` is a non-empty object with an operator key (`$eq`, `$push`...): the one test every dialect classifies with. */
|
|
41
40
|
export function isOperatorObject(value) {
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
/** Whether every key of the non-empty object `value` is an operator (no plain field names mixed in). */
|
|
45
|
-
export function isOperatorOnlyObject(value) {
|
|
46
|
-
return hasKeys(value) && !Array.isArray(value) && !someKey(value, (key) => !isOperatorKey(key));
|
|
41
|
+
return isRecord(value) && someKey(value, isOperatorKey);
|
|
47
42
|
}
|
|
48
43
|
/** Whether `value` is an object that is not an array, whose keys can be read. */
|
|
49
44
|
export function isRecord(value) {
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"homepage": "https://uql-orm.dev",
|
|
4
4
|
"description": "The JSON-native TypeScript ORM for Bun, Browsers, Edge, Deno, Node, Workers. Supports PostgreSQL, PGlite, MySQL, MariaDB, SQLite, CockroachDB, SQL Server, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.68.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=24"
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { isOperatorObject } from '../util/object.util.js';
|
|
2
|
-
/** An `$elemMatch` as per-field conditions, a plain value as `$eq`, so both spellings emit the same SQL. */
|
|
3
|
-
export function buildElemMatchConditions(match, onCondition) {
|
|
4
|
-
return Object.entries(match).flatMap(([field, value]) => isOperatorObject(value)
|
|
5
|
-
? Object.entries(value).map(([op, opVal]) => onCondition(field, op, opVal))
|
|
6
|
-
: onCondition(field, '$eq', value));
|
|
7
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { VectorDistance, VectorOperatorMetric } from '../type/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* Each metric's pgvector distance operator and the operator-class suffix its index takes, in one list
|
|
4
|
-
* so a dialect cannot have the operator but not the opclass. Its own module because the two ends live
|
|
5
|
-
* apart: the operator on the dialect, the opclass in the migrator's index DDL.
|
|
6
|
-
*/
|
|
7
|
-
export declare const PG_VECTOR_METRICS: ReadonlyMap<VectorDistance, VectorOperatorMetric>;
|
|
8
|
-
/**
|
|
9
|
-
* CockroachDB's three: `<+>` and `vector_l1_ops` answer "unimplemented: operator class ... is not
|
|
10
|
-
* supported" (verified live on v26.2), tracked at https://github.com/cockroachdb/cockroach/issues/147839.
|
|
11
|
-
* Re-check that issue before adding `l1`; it is omitted on purpose.
|
|
12
|
-
*/
|
|
13
|
-
export declare const COCKROACH_VECTOR_METRICS: ReadonlyMap<VectorDistance, VectorOperatorMetric>;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Each metric's pgvector distance operator and the operator-class suffix its index takes, in one list
|
|
3
|
-
* so a dialect cannot have the operator but not the opclass. Its own module because the two ends live
|
|
4
|
-
* apart: the operator on the dialect, the opclass in the migrator's index DDL.
|
|
5
|
-
*/
|
|
6
|
-
export const PG_VECTOR_METRICS = new Map([
|
|
7
|
-
['cosine', { op: '<=>', opsSuffix: 'cosine' }],
|
|
8
|
-
['l2', { op: '<->', opsSuffix: 'l2' }],
|
|
9
|
-
['inner', { op: '<#>', opsSuffix: 'ip' }],
|
|
10
|
-
['l1', { op: '<+>', opsSuffix: 'l1' }],
|
|
11
|
-
]);
|
|
12
|
-
/**
|
|
13
|
-
* CockroachDB's three: `<+>` and `vector_l1_ops` answer "unimplemented: operator class ... is not
|
|
14
|
-
* supported" (verified live on v26.2), tracked at https://github.com/cockroachdb/cockroach/issues/147839.
|
|
15
|
-
* Re-check that issue before adding `l1`; it is omitted on purpose.
|
|
16
|
-
*/
|
|
17
|
-
export const COCKROACH_VECTOR_METRICS = new Map([...PG_VECTOR_METRICS].filter(([metric]) => metric !== 'l1'));
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { VectorDistance } from '../type/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* MariaDB's own name for each metric it supports (`euclidean`, not `l2`), which its index clause and
|
|
4
|
-
* its distance function are both spelled from - one list, so a metric can never be searchable and
|
|
5
|
-
* unindexable or the reverse. Its own module because the two ends now live apart: the distance
|
|
6
|
-
* function on the dialect, the `DISTANCE=` clause in the migrator's index DDL.
|
|
7
|
-
*/
|
|
8
|
-
export declare const MARIA_VECTOR_METRICS: Map<VectorDistance, string>;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MariaDB's own name for each metric it supports (`euclidean`, not `l2`), which its index clause and
|
|
3
|
-
* its distance function are both spelled from - one list, so a metric can never be searchable and
|
|
4
|
-
* unindexable or the reverse. Its own module because the two ends now live apart: the distance
|
|
5
|
-
* function on the dialect, the `DISTANCE=` clause in the migrator's index DDL.
|
|
6
|
-
*/
|
|
7
|
-
export const MARIA_VECTOR_METRICS = new Map([
|
|
8
|
-
['cosine', 'cosine'],
|
|
9
|
-
['l2', 'euclidean'],
|
|
10
|
-
]);
|