uql-orm 0.2.9 → 0.3.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/CHANGELOG.md +31 -2
- package/README.md +5 -3
- package/dist/dialect/abstractSqlDialect.d.ts +43 -2
- package/dist/dialect/abstractSqlDialect.d.ts.map +1 -1
- package/dist/dialect/abstractSqlDialect.js +191 -5
- package/dist/dialect/abstractSqlDialect.js.map +1 -1
- package/dist/dialect/dialectConfig.d.ts +17 -1
- package/dist/dialect/dialectConfig.d.ts.map +1 -1
- package/dist/dialect/dialectConfig.js +39 -0
- package/dist/dialect/dialectConfig.js.map +1 -1
- package/dist/entity/decorator/index-decorator.d.ts +5 -4
- package/dist/entity/decorator/index-decorator.d.ts.map +1 -1
- package/dist/entity/decorator/index-decorator.js +1 -9
- package/dist/entity/decorator/index-decorator.js.map +1 -1
- package/dist/maria/mariaDialect.d.ts +5 -1
- package/dist/maria/mariaDialect.d.ts.map +1 -1
- package/dist/maria/mariaDialect.js +9 -0
- package/dist/maria/mariaDialect.js.map +1 -1
- package/dist/migrate/schemaGenerator.d.ts +10 -2
- package/dist/migrate/schemaGenerator.d.ts.map +1 -1
- package/dist/migrate/schemaGenerator.js +87 -23
- package/dist/migrate/schemaGenerator.js.map +1 -1
- package/dist/mysql/mysqlDialect.d.ts.map +1 -1
- package/dist/mysql/mysqlDialect.js +6 -5
- package/dist/mysql/mysqlDialect.js.map +1 -1
- package/dist/postgres/postgresDialect.d.ts +5 -1
- package/dist/postgres/postgresDialect.d.ts.map +1 -1
- package/dist/postgres/postgresDialect.js +22 -5
- package/dist/postgres/postgresDialect.js.map +1 -1
- package/dist/schema/canonicalType.d.ts +13 -1
- package/dist/schema/canonicalType.d.ts.map +1 -1
- package/dist/schema/canonicalType.js +47 -6
- package/dist/schema/canonicalType.js.map +1 -1
- package/dist/schema/schemaASTBuilder.d.ts.map +1 -1
- package/dist/schema/schemaASTBuilder.js +4 -0
- package/dist/schema/schemaASTBuilder.js.map +1 -1
- package/dist/schema/types.d.ts +5 -4
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js.map +1 -1
- package/dist/sqlite/sqliteDialect.d.ts +5 -1
- package/dist/sqlite/sqliteDialect.d.ts.map +1 -1
- package/dist/sqlite/sqliteDialect.js +16 -5
- package/dist/sqlite/sqliteDialect.js.map +1 -1
- package/dist/type/entity.d.ts +34 -8
- package/dist/type/entity.d.ts.map +1 -1
- package/dist/type/entity.js.map +1 -1
- package/dist/type/migration.d.ts +5 -3
- package/dist/type/migration.d.ts.map +1 -1
- package/dist/type/query.d.ts +58 -3
- package/dist/type/query.d.ts.map +1 -1
- package/dist/type/query.js.map +1 -1
- package/dist/util/dialect.util.d.ts +3 -1
- package/dist/util/dialect.util.d.ts.map +1 -1
- package/dist/util/dialect.util.js +4 -0
- package/dist/util/dialect.util.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
# [0.3.0](https://github.com/rogerpadilla/uql/compare/uql-orm@0.2.10...uql-orm@0.3.0) (2026-03-12)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* vector index support with dialect-driven schema generation ([5bb1a6e](https://github.com/rogerpadilla/uql/commit/5bb1a6e99ae9108dad492630c55cf3ba15862a3c))
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
|
|
@@ -17,6 +20,32 @@ All notable changes to this project will be documented in this file. Please add
|
|
|
17
20
|
|
|
18
21
|
date format is [yyyy-mm-dd]
|
|
19
22
|
|
|
23
|
+
## [0.3.0] - 2026-03-12
|
|
24
|
+
### New Features
|
|
25
|
+
- **Semantic Search**: First-class vector similarity search across PostgreSQL (pgvector), MariaDB, and SQLite. Query via `$sort` on vector fields:
|
|
26
|
+
```ts
|
|
27
|
+
const results = await querier.findMany(Article, {
|
|
28
|
+
$sort: { embedding: { $vector: queryVec, $distance: 'cosine' } },
|
|
29
|
+
$limit: 10,
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
Supports 5 distance metrics (`cosine`, `l2`, `inner`, `l1`, `hamming`), distance projection via `$project`, and the `WithDistance<E>` utility type. Each dialect generates native SQL: Postgres operators (`<=>`, `<->`), MariaDB (`VEC_DISTANCE_COSINE()`), SQLite (`vec_distance_cosine()`).
|
|
33
|
+
- **Vector Field Types**: `@Field({ type: 'vector', dimensions: 1536 })` for standard 32-bit embeddings, plus Postgres-specific `'halfvec'` (16-bit, 50% storage savings) and `'sparsevec'` (for SPLADE-style sparse embeddings). Automatic SQL mapping across dialects.
|
|
34
|
+
- **Vector Indexes**: `@Index()` supports HNSW and IVFFlat index types with `distance`, `m`, `efConstruction`, and `lists` options. Generates pgvector operator classes for Postgres, inline `VECTOR INDEX` for MariaDB, and standard indexes for SQLite.
|
|
35
|
+
- **Auto Extension Creation**: Schema generator automatically emits `CREATE EXTENSION IF NOT EXISTS vector` for Postgres tables containing vector columns.
|
|
36
|
+
|
|
37
|
+
### Architecture
|
|
38
|
+
- **Schema Generator Dialect Config Refactor**: Eliminated all `this.dialect ===` branches from `schemaGenerator.ts` by adding new declarative `dialectConfig` properties (`columnComment`, `vectorIndexStyle`, `dropIndexSyntax`, `renameTableSyntax`, `booleanLiteral`, `alterColumnStrategy`, `vectorOpsClass`, `vectorExtension`). All dialect-specific behavior is now config-driven.
|
|
39
|
+
- **Unified CREATE INDEX**: `generateCreateIndexFromNode` now delegates to `generateCreateIndex`, eliminating duplicated SQL assembly and ensuring consistent vector index handling across both code paths.
|
|
40
|
+
|
|
41
|
+
## [0.2.7] - 2026-03-11
|
|
42
|
+
### New Features
|
|
43
|
+
- **More `$size` Comparison Operators**: `$size` now accepts comparison operator objects in addition to exact numbers — e.g. `{ $size: { $gte: 2 } }`, `{ $size: { $gt: 0, $lte: 5 } }`, `{ $size: { $between: [1, 10] } }`. Supported operators: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$between`.
|
|
44
|
+
- **Relation Count Filtering**: `$size` on to-many relations (OneToMany, ManyToMany) now generates efficient `COUNT(*)` subqueries. E.g. `{ tags: { $size: { $gte: 2 } } }` produces `WHERE (SELECT COUNT(*) FROM ...) >= $1`.
|
|
45
|
+
|
|
46
|
+
### Test Coverage
|
|
47
|
+
- Added 22 new test cases across all SQL dialects: exact match, every comparison operator, multi-op ranges, `$between`, error paths (unsupported operator, missing references). All coverage thresholds met.
|
|
48
|
+
|
|
20
49
|
## [0.2.6] - 2026-03-11
|
|
21
50
|
### Documentation
|
|
22
51
|
- **README – Migrations & Synchronization**: Rewrote the section with an entity-first intro explaining that UQL auto-generates migrations from entities. Reordered CLI commands to lead with `generate:entities`, renumbered usage examples, and added a concrete quick-start snippet.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<a href="https://uql-orm.dev"><img src="assets/logo.svg" alt="uql" width="80" /></a>
|
|
4
4
|
|
|
5
|
-
[](https://github.com/rogerpadilla/uql) [](https://coveralls.io/github/rogerpadilla/uql?branch=main) [](https://github.com/rogerpadilla/uql/blob/main/LICENSE) [](https://www.npmjs.com/package/uql-orm)
|
|
5
|
+
[](https://github.com/rogerpadilla/uql) [](https://coveralls.io/github/rogerpadilla/uql?branch=main) [](https://github.com/rogerpadilla/uql/blob/main/LICENSE) [](https://www.npmjs.com/package/uql-orm) [](https://discord.gg/DHJYp6MDS7)
|
|
6
6
|
|
|
7
7
|
**[UQL](https://uql-orm.dev)** is the [smartest ORM](https://medium.com/@rogerpadillac/in-search-of-the-perfect-orm-e01fcc9bce3d) for TypeScript. It is engineered to be **fast**, **safe**, and **universally compatible**.
|
|
8
8
|
|
|
@@ -31,7 +31,8 @@ const users = await querier.findMany(User, {
|
|
|
31
31
|
| **[Declarative Transactions](https://uql-orm.dev/querying/transactions)** | Standard `@Transactional()` and `@InjectQuerier()` decorators for NestJS/DI. |
|
|
32
32
|
| **[Lifecycle Hooks](https://uql-orm.dev/entities/lifecycle-hooks)**| `@BeforeInsert`, `@AfterLoad` and 5 more decorators for validation, timestamps, and computed fields. |
|
|
33
33
|
| **[Aggregate Queries](https://uql-orm.dev/querying/aggregate)** | `GROUP BY`, `HAVING`, `COUNT`, `SUM`, `AVG`, `MIN`, `MAX`, and `DISTINCT` across all dialects. |
|
|
34
|
-
| **[
|
|
34
|
+
| **[Semantic Search](https://uql-orm.dev/querying/semantic-search)** | Vector similarity via `$sort` with `$vector`/`$distance`. Supports `vector`, `halfvec`, `sparsevec` types, HNSW/IVFFlat indexes, and 5 distance metrics across Postgres, MariaDB, and SQLite. |
|
|
35
|
+
| **[Modern & Versatile](https://uql-orm.dev/entities/virtual-fields)** | **Pure ESM**, high-res timing, [Soft-delete](https://uql-orm.dev/entities/soft-delete), and **JSONB/JSON** support. |
|
|
35
36
|
| **[Database Migrations](https://www.uql-orm.dev/migrations)** | Built-in [Entity-First synchronization](https://uql-orm.dev/migrations#3-entity-first-synchronization-development) and a robust CLI for version-controlled schema evolution. |
|
|
36
37
|
| **[Logging & Monitoring](https://www.uql-orm.dev/logging)** | Professional-grade monitoring with slow-query detection and colored output. |
|
|
37
38
|
|
|
@@ -101,7 +102,7 @@ UQL separates the **intent** of your data from its **storage**. Both properties
|
|
|
101
102
|
|
|
102
103
|
| Property | Purpose | Values |
|
|
103
104
|
| :--- | :--- | :--- |
|
|
104
|
-
| **`type`** | **Logical Type** (Abstraction). Used for runtime behavior and automatic SQL mapping. | `String`, `Number`, `Boolean`, `Date`, `BigInt`, or semantic strings: `'uuid'`, `'json'`, `'vector'`. |
|
|
105
|
+
| **`type`** | **Logical Type** (Abstraction). Used for runtime behavior and automatic SQL mapping. | `String`, `Number`, `Boolean`, `Date`, `BigInt`, or semantic strings: `'uuid'`, `'json'`, `'vector'`, `'halfvec'`, `'sparsevec'`. |
|
|
105
106
|
| **`columnType`** | **Physical Type** (Implementation). **Highest Priority**. Bypasses UQL's inference for exact SQL control. | Raw SQL types: `'varchar(100)'`, `'decimal(10,2)'`, `'smallint'`, etc. |
|
|
106
107
|
|
|
107
108
|
```ts
|
|
@@ -590,6 +591,7 @@ Learn more about UQL at [uql-orm.dev](https://uql-orm.dev) for details on:
|
|
|
590
591
|
|
|
591
592
|
- [Complex Logical Operators](https://uql-orm.dev/querying/logical-operators)
|
|
592
593
|
- [Aggregate Queries (GROUP BY, HAVING, DISTINCT)](https://uql-orm.dev/querying/aggregate)
|
|
594
|
+
- [Semantic Search (Vector Similarity)](https://uql-orm.dev/querying/semantic-search)
|
|
593
595
|
- [Relationship Mapping (1-1, 1-M, M-M)](https://uql-orm.dev/querying/relations)
|
|
594
596
|
- [Lifecycle Hooks](https://uql-orm.dev/entities/lifecycle-hooks)
|
|
595
597
|
- [Soft Deletes & Auditing](https://uql-orm.dev/entities/soft-delete)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type VectorCast } from '../schema/canonicalType.js';
|
|
2
|
+
import { type EntityMeta, type FieldKey, type FieldOptions, type IsolationLevel, type JsonMergeOp, type Query, type QueryAggregate, type QueryComparisonOptions, type QueryConflictPaths, type QueryContext, type QueryDialect, type QueryHavingMap, type QueryOptions, type QueryPager, QueryRaw, type QueryRawFnOptions, type QuerySearch, type QuerySelect, type QuerySelectOptions, type QuerySizeComparisonOps, type QuerySortMap, type QueryVectorSearch, type QueryWhere, type QueryWhereArray, type QueryWhereFieldOperatorMap, type QueryWhereMap, type QueryWhereOptions, type RelationOptions, type SqlDialect, type SqlQueryDialect, type Type, type UpdatePayload, type VectorDistance } from '../type/index.js';
|
|
2
3
|
import { type CallbackKey } from '../util/index.js';
|
|
3
4
|
import { AbstractDialect } from './abstractDialect.js';
|
|
4
5
|
export declare abstract class AbstractSqlDialect extends AbstractDialect implements QueryDialect, SqlQueryDialect {
|
|
@@ -14,7 +15,7 @@ export declare abstract class AbstractSqlDialect extends AbstractDialect impleme
|
|
|
14
15
|
returningId<E>(entity: Type<E>): string;
|
|
15
16
|
search<E>(ctx: QueryContext, entity: Type<E>, q?: Query<E>, opts?: QueryOptions): void;
|
|
16
17
|
selectFields<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E> | QueryRaw[] | undefined, opts?: QuerySelectOptions): void;
|
|
17
|
-
select<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E> | QueryRaw[] | undefined, opts?: QueryOptions, distinct?: boolean): void;
|
|
18
|
+
select<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E> | QueryRaw[] | undefined, opts?: QueryOptions, distinct?: boolean, sort?: QuerySortMap<E>): void;
|
|
18
19
|
protected selectRelationFields<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E> | undefined, opts?: {
|
|
19
20
|
prefix?: string;
|
|
20
21
|
}): void;
|
|
@@ -43,6 +44,31 @@ export declare abstract class AbstractSqlDialect extends AbstractDialect impleme
|
|
|
43
44
|
protected buildJsonFieldCondition(ctx: QueryContext, config: JsonFieldConfig, jsonPath: string, op: string, value: unknown): string;
|
|
44
45
|
getComparisonKey<E>(ctx: QueryContext, entity: Type<E>, key: FieldKey<E>, { prefix }?: QueryOptions): void;
|
|
45
46
|
sort<E>(ctx: QueryContext, entity: Type<E>, sort: QuerySortMap<E> | undefined, { prefix }: QueryOptions): void;
|
|
47
|
+
/**
|
|
48
|
+
* Resolve common parameters for a vector similarity ORDER BY expression.
|
|
49
|
+
* Shared by all dialect overrides of `appendVectorSort`.
|
|
50
|
+
*/
|
|
51
|
+
protected resolveVectorSortParams<E>(meta: EntityMeta<E>, key: string, search: QueryVectorSearch): {
|
|
52
|
+
colName: string;
|
|
53
|
+
distance: VectorDistance;
|
|
54
|
+
field: FieldOptions | undefined;
|
|
55
|
+
vectorCast: VectorCast;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Append a vector similarity ORDER BY expression.
|
|
59
|
+
* Base implementation throws — subclasses (Postgres, SQLite, MariaDB) override for dialect-specific operators.
|
|
60
|
+
*/
|
|
61
|
+
protected appendVectorSort<E>(_ctx: QueryContext, _meta: EntityMeta<E>, _key: string, _search: QueryVectorSearch): void;
|
|
62
|
+
/**
|
|
63
|
+
* Shared helper for dialects that use function-call syntax: `fn(col, ?)`.
|
|
64
|
+
* Used by SQLite (`vec_distance_cosine`) and MariaDB (`VEC_DISTANCE_COSINE`).
|
|
65
|
+
*/
|
|
66
|
+
protected appendFunctionVectorSort<E>(ctx: QueryContext, meta: EntityMeta<E>, key: string, search: QueryVectorSearch, fnMap: Partial<Record<VectorDistance, string>>, dialectName: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* Append a vector distance projection (`<expr> AS <alias>`) in the SELECT clause.
|
|
69
|
+
* Delegates to `appendVectorSort()` (which emits the distance expression) then appends the alias.
|
|
70
|
+
*/
|
|
71
|
+
protected appendVectorProjection<E>(ctx: QueryContext, meta: EntityMeta<E>, key: string, search: QueryVectorSearch): void;
|
|
46
72
|
pager(ctx: QueryContext, opts: QueryPager): void;
|
|
47
73
|
count<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, opts?: QueryOptions): void;
|
|
48
74
|
aggregate<E>(ctx: QueryContext, entity: Type<E>, q: QueryAggregate<E>, opts?: QueryOptions): void;
|
|
@@ -126,6 +152,21 @@ export declare abstract class AbstractSqlDialect extends AbstractDialect impleme
|
|
|
126
152
|
* Supports all cardinalities: mm (via junction), 1m, m1, and 11.
|
|
127
153
|
*/
|
|
128
154
|
protected compareRelation<E>(ctx: QueryContext, entity: Type<E>, key: string, val: QueryWhereMap<unknown>, rel: RelationOptions, opts: QueryComparisonOptions): void;
|
|
155
|
+
/**
|
|
156
|
+
* Filter by relation size using a `COUNT(*)` subquery.
|
|
157
|
+
* Supports all cardinalities: mm (via junction), 1m.
|
|
158
|
+
*/
|
|
159
|
+
protected compareRelationSize<E>(ctx: QueryContext, entity: Type<E>, key: string, sizeVal: number | QuerySizeComparisonOps, rel: RelationOptions, opts: QueryComparisonOptions): void;
|
|
160
|
+
/**
|
|
161
|
+
* Build a complete `$size` comparison expression.
|
|
162
|
+
* Handles both single and multiple comparison operators by repeating the size expression.
|
|
163
|
+
* @param sizeExprFn - function that appends the size expression to ctx (e.g. `jsonb_array_length("col")`)
|
|
164
|
+
*/
|
|
165
|
+
protected buildSizeComparison(ctx: QueryContext, sizeExprFn: () => void, sizeVal: number | QuerySizeComparisonOps): void;
|
|
166
|
+
/**
|
|
167
|
+
* Append a single size comparison operator and value to the context.
|
|
168
|
+
*/
|
|
169
|
+
private appendSizeOp;
|
|
129
170
|
abstract escape(value: unknown): string;
|
|
130
171
|
}
|
|
131
172
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abstractSqlDialect.d.ts","sourceRoot":"","sources":["../../src/dialect/abstractSqlDialect.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,YAAY,EAEjB,KAAK,cAAc,EACnB,KAAK,WAAW,EAEhB,KAAK,KAAK,EACV,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,QAAQ,EACR,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,kBAAkB,
|
|
1
|
+
{"version":3,"file":"abstractSqlDialect.d.ts","sourceRoot":"","sources":["../../src/dialect/abstractSqlDialect.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getMeta } from '../entity/index.js';
|
|
2
|
+
import { resolveVectorCast } from '../schema/canonicalType.js';
|
|
2
3
|
import { QueryRaw, RAW_ALIAS, RAW_VALUE, } from '../type/index.js';
|
|
3
|
-
import { buildQueryWhereAsMap, buildSortMap, escapeSqlId, fillOnFields, filterFieldKeys, filterRelationKeys, flatObject, getFieldCallbackValue, getFieldKeys, getKeys, hasKeys, isJsonType, isSelectingRelations, parseGroupMap, raw, } from '../util/index.js';
|
|
4
|
+
import { buildQueryWhereAsMap, buildSortMap, escapeSqlId, fillOnFields, filterFieldKeys, filterRelationKeys, flatObject, getFieldCallbackValue, getFieldKeys, getKeys, hasKeys, isJsonType, isSelectingRelations, isVectorSearch, parseGroupMap, raw, } from '../util/index.js';
|
|
4
5
|
import { AbstractDialect } from './abstractDialect.js';
|
|
5
6
|
import { SqlQueryContext } from './queryContext.js';
|
|
6
7
|
export class AbstractSqlDialect extends AbstractDialect {
|
|
@@ -128,7 +129,7 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
128
129
|
}
|
|
129
130
|
});
|
|
130
131
|
}
|
|
131
|
-
select(ctx, entity, select, opts = {}, distinct) {
|
|
132
|
+
select(ctx, entity, select, opts = {}, distinct, sort) {
|
|
132
133
|
const meta = getMeta(entity);
|
|
133
134
|
const tableName = this.resolveTableName(entity, meta);
|
|
134
135
|
const mapSelect = Array.isArray(select) ? undefined : select;
|
|
@@ -137,6 +138,16 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
137
138
|
this.selectFields(ctx, entity, select, { prefix });
|
|
138
139
|
// Add related fields BEFORE FROM clause
|
|
139
140
|
this.selectRelationFields(ctx, entity, mapSelect, { prefix });
|
|
141
|
+
// Inject vector distance projections when $project is set
|
|
142
|
+
if (sort) {
|
|
143
|
+
const sortMap = buildSortMap(sort);
|
|
144
|
+
for (const [key, val] of Object.entries(sortMap)) {
|
|
145
|
+
if (isVectorSearch(val) && val.$project) {
|
|
146
|
+
ctx.append(', ');
|
|
147
|
+
this.appendVectorProjection(ctx, meta, key, val);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
140
151
|
ctx.append(` FROM ${this.escapeId(tableName)}`);
|
|
141
152
|
// Add JOINs AFTER FROM clause
|
|
142
153
|
this.selectRelationJoins(ctx, entity, mapSelect, { prefix });
|
|
@@ -294,6 +305,12 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
294
305
|
// Detect relation filtering
|
|
295
306
|
const rel = meta.relations[keyStr];
|
|
296
307
|
if (rel) {
|
|
308
|
+
// Check if this is a $size query on a relation (count filtering)
|
|
309
|
+
const valObj = val;
|
|
310
|
+
if (valObj && typeof valObj === 'object' && '$size' in valObj && Object.keys(valObj).length === 1) {
|
|
311
|
+
this.compareRelationSize(ctx, entity, keyStr, valObj['$size'], rel, opts);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
297
314
|
this.compareRelation(ctx, entity, keyStr, val, rel, opts);
|
|
298
315
|
return;
|
|
299
316
|
}
|
|
@@ -526,12 +543,38 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
526
543
|
return;
|
|
527
544
|
}
|
|
528
545
|
const meta = getMeta(entity);
|
|
529
|
-
|
|
546
|
+
// Separate vector search entries from direction entries before flattening,
|
|
547
|
+
// because flatObject recursively destructures objects — it would break QueryVectorSearch.
|
|
548
|
+
const vectorEntries = [];
|
|
549
|
+
const directionEntries = {};
|
|
550
|
+
for (const [key, val] of Object.entries(sortMap)) {
|
|
551
|
+
if (isVectorSearch(val)) {
|
|
552
|
+
vectorEntries.push([key, val]);
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
directionEntries[key] = val;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
const flattenedSort = flatObject(directionEntries, prefix);
|
|
559
|
+
// Merge: vector entries first (primary ordering), then flattened direction entries.
|
|
560
|
+
const allEntries = [...vectorEntries, ...Object.entries(flattenedSort)];
|
|
561
|
+
if (!allEntries.length)
|
|
562
|
+
return;
|
|
530
563
|
ctx.append(' ORDER BY ');
|
|
531
|
-
|
|
564
|
+
allEntries.forEach(([key, sort], index) => {
|
|
532
565
|
if (index > 0) {
|
|
533
566
|
ctx.append(', ');
|
|
534
567
|
}
|
|
568
|
+
if (isVectorSearch(sort)) {
|
|
569
|
+
if (sort.$project) {
|
|
570
|
+
// Distance already projected in SELECT — reference the alias to avoid recomputation
|
|
571
|
+
ctx.append(this.escapeId(sort.$project));
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
this.appendVectorSort(ctx, meta, key, sort);
|
|
575
|
+
}
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
535
578
|
const direction = AbstractSqlDialect.SORT_DIRECTION_MAP[sort];
|
|
536
579
|
// Detect JSONB dot-notation: 'column.path'
|
|
537
580
|
const jsonDot = this.resolveJsonDotPath(meta, key);
|
|
@@ -544,6 +587,46 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
544
587
|
ctx.append(this.escapeId(name) + direction);
|
|
545
588
|
});
|
|
546
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Resolve common parameters for a vector similarity ORDER BY expression.
|
|
592
|
+
* Shared by all dialect overrides of `appendVectorSort`.
|
|
593
|
+
*/
|
|
594
|
+
resolveVectorSortParams(meta, key, search) {
|
|
595
|
+
const field = meta.fields[key];
|
|
596
|
+
const colName = this.resolveColumnName(key, field);
|
|
597
|
+
const distance = search.$distance ?? field?.distance ?? 'cosine';
|
|
598
|
+
const vectorCast = resolveVectorCast(field);
|
|
599
|
+
return { colName, distance, field, vectorCast };
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Append a vector similarity ORDER BY expression.
|
|
603
|
+
* Base implementation throws — subclasses (Postgres, SQLite, MariaDB) override for dialect-specific operators.
|
|
604
|
+
*/
|
|
605
|
+
appendVectorSort(_ctx, _meta, _key, _search) {
|
|
606
|
+
throw new TypeError('Vector similarity sort is not supported by this dialect. Use raw() for vector queries.');
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Shared helper for dialects that use function-call syntax: `fn(col, ?)`.
|
|
610
|
+
* Used by SQLite (`vec_distance_cosine`) and MariaDB (`VEC_DISTANCE_COSINE`).
|
|
611
|
+
*/
|
|
612
|
+
appendFunctionVectorSort(ctx, meta, key, search, fnMap, dialectName) {
|
|
613
|
+
const { colName, distance } = this.resolveVectorSortParams(meta, key, search);
|
|
614
|
+
const fn = fnMap[distance];
|
|
615
|
+
if (!fn) {
|
|
616
|
+
throw new TypeError(`${dialectName} does not support vector distance metric: ${distance}`);
|
|
617
|
+
}
|
|
618
|
+
ctx.append(`${fn}(${this.escapeId(colName)}, `);
|
|
619
|
+
ctx.addValue(`[${search.$vector.join(',')}]`);
|
|
620
|
+
ctx.append(')');
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Append a vector distance projection (`<expr> AS <alias>`) in the SELECT clause.
|
|
624
|
+
* Delegates to `appendVectorSort()` (which emits the distance expression) then appends the alias.
|
|
625
|
+
*/
|
|
626
|
+
appendVectorProjection(ctx, meta, key, search) {
|
|
627
|
+
this.appendVectorSort(ctx, meta, key, search);
|
|
628
|
+
ctx.append(` AS ${this.escapeId(search.$project)}`);
|
|
629
|
+
}
|
|
547
630
|
pager(ctx, opts) {
|
|
548
631
|
if (opts.$limit) {
|
|
549
632
|
ctx.append(` LIMIT ${Number(opts.$limit)}`);
|
|
@@ -676,7 +759,7 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
676
759
|
});
|
|
677
760
|
}
|
|
678
761
|
find(ctx, entity, q = {}, opts) {
|
|
679
|
-
this.select(ctx, entity, q.$select, opts, q.$distinct);
|
|
762
|
+
this.select(ctx, entity, q.$select, opts, q.$distinct, q.$sort);
|
|
680
763
|
this.search(ctx, entity, q, opts);
|
|
681
764
|
}
|
|
682
765
|
insert(ctx, entity, payload, opts) {
|
|
@@ -1031,6 +1114,109 @@ export class AbstractSqlDialect extends AbstractDialect {
|
|
|
1031
1114
|
}
|
|
1032
1115
|
ctx.append(')');
|
|
1033
1116
|
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Filter by relation size using a `COUNT(*)` subquery.
|
|
1119
|
+
* Supports all cardinalities: mm (via junction), 1m.
|
|
1120
|
+
*/
|
|
1121
|
+
compareRelationSize(ctx, entity, key, sizeVal, rel, opts) {
|
|
1122
|
+
const meta = getMeta(entity);
|
|
1123
|
+
const parentTable = this.resolveTableName(entity, meta);
|
|
1124
|
+
const parentId = meta.id;
|
|
1125
|
+
const escapedParentId = (opts.prefix ? this.escapeId(opts.prefix, true, true) : this.escapeId(parentTable, false, true)) +
|
|
1126
|
+
this.escapeId(parentId);
|
|
1127
|
+
if (!rel.references?.length) {
|
|
1128
|
+
throw new TypeError(`Relation '${key}' on '${parentTable}' has no references defined`);
|
|
1129
|
+
}
|
|
1130
|
+
const appendSubquery = () => {
|
|
1131
|
+
ctx.append('(SELECT COUNT(*) FROM ');
|
|
1132
|
+
if (rel.cardinality === 'mm' && rel.through) {
|
|
1133
|
+
const throughEntity = rel.through();
|
|
1134
|
+
const throughMeta = getMeta(throughEntity);
|
|
1135
|
+
const throughTable = this.resolveTableName(throughEntity, throughMeta);
|
|
1136
|
+
const localFk = rel.references[0].local;
|
|
1137
|
+
ctx.append(this.escapeId(throughTable));
|
|
1138
|
+
ctx.append(` WHERE ${this.escapeId(throughTable, false, true)}${this.escapeId(localFk)} = ${escapedParentId}`);
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
const relatedEntity = rel.entity();
|
|
1142
|
+
const relatedMeta = getMeta(relatedEntity);
|
|
1143
|
+
const relatedTable = this.resolveTableName(relatedEntity, relatedMeta);
|
|
1144
|
+
const joinLeft = `${this.escapeId(relatedTable, false, true)}${this.escapeId(rel.references[0].foreign)}`;
|
|
1145
|
+
ctx.append(this.escapeId(relatedTable));
|
|
1146
|
+
ctx.append(` WHERE ${joinLeft} = ${escapedParentId}`);
|
|
1147
|
+
}
|
|
1148
|
+
ctx.append(')');
|
|
1149
|
+
};
|
|
1150
|
+
this.buildSizeComparison(ctx, appendSubquery, sizeVal);
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Build a complete `$size` comparison expression.
|
|
1154
|
+
* Handles both single and multiple comparison operators by repeating the size expression.
|
|
1155
|
+
* @param sizeExprFn - function that appends the size expression to ctx (e.g. `jsonb_array_length("col")`)
|
|
1156
|
+
*/
|
|
1157
|
+
buildSizeComparison(ctx, sizeExprFn, sizeVal) {
|
|
1158
|
+
if (typeof sizeVal === 'number') {
|
|
1159
|
+
sizeExprFn();
|
|
1160
|
+
ctx.append(' = ');
|
|
1161
|
+
ctx.addValue(sizeVal);
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const entries = Object.entries(sizeVal).filter(([, v]) => v !== undefined);
|
|
1165
|
+
if (entries.length > 1) {
|
|
1166
|
+
ctx.append('(');
|
|
1167
|
+
}
|
|
1168
|
+
entries.forEach(([op, val], index) => {
|
|
1169
|
+
if (index > 0) {
|
|
1170
|
+
ctx.append(' AND ');
|
|
1171
|
+
}
|
|
1172
|
+
sizeExprFn();
|
|
1173
|
+
this.appendSizeOp(ctx, op, val);
|
|
1174
|
+
});
|
|
1175
|
+
if (entries.length > 1) {
|
|
1176
|
+
ctx.append(')');
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Append a single size comparison operator and value to the context.
|
|
1181
|
+
*/
|
|
1182
|
+
appendSizeOp(ctx, op, val) {
|
|
1183
|
+
switch (op) {
|
|
1184
|
+
case '$eq':
|
|
1185
|
+
ctx.append(' = ');
|
|
1186
|
+
ctx.addValue(val);
|
|
1187
|
+
break;
|
|
1188
|
+
case '$ne':
|
|
1189
|
+
ctx.append(' <> ');
|
|
1190
|
+
ctx.addValue(val);
|
|
1191
|
+
break;
|
|
1192
|
+
case '$gt':
|
|
1193
|
+
ctx.append(' > ');
|
|
1194
|
+
ctx.addValue(val);
|
|
1195
|
+
break;
|
|
1196
|
+
case '$gte':
|
|
1197
|
+
ctx.append(' >= ');
|
|
1198
|
+
ctx.addValue(val);
|
|
1199
|
+
break;
|
|
1200
|
+
case '$lt':
|
|
1201
|
+
ctx.append(' < ');
|
|
1202
|
+
ctx.addValue(val);
|
|
1203
|
+
break;
|
|
1204
|
+
case '$lte':
|
|
1205
|
+
ctx.append(' <= ');
|
|
1206
|
+
ctx.addValue(val);
|
|
1207
|
+
break;
|
|
1208
|
+
case '$between': {
|
|
1209
|
+
const [min, max] = val;
|
|
1210
|
+
ctx.append(' BETWEEN ');
|
|
1211
|
+
ctx.addValue(min);
|
|
1212
|
+
ctx.append(' AND ');
|
|
1213
|
+
ctx.addValue(max);
|
|
1214
|
+
break;
|
|
1215
|
+
}
|
|
1216
|
+
default:
|
|
1217
|
+
throw TypeError(`unsupported $size comparison operator: ${op}`);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1034
1220
|
}
|
|
1035
1221
|
/**
|
|
1036
1222
|
* Type guard: narrows a relation select value to a query object (with optional `$required`).
|