uql-orm 0.57.0 → 0.59.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 +6 -8
- package/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +3 -3
- package/dist/dialect/abstractSqlDialect.d.ts +2 -2
- package/dist/dialect/abstractSqlDialect.js +4 -4
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +1 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +1 -1
- package/dist/dialect/queryJoins.js +1 -0
- package/dist/entity/decorator/bag.d.ts +2 -2
- package/dist/entity/decorator/entity.d.ts +8 -9
- package/dist/entity/decorator/entity.js +6 -7
- package/dist/entity/decorator/members.d.ts +7 -6
- package/dist/entity/decorator/members.js +2 -1
- package/dist/entity/metadata/definition.d.ts +16 -11
- package/dist/entity/metadata/definition.js +73 -50
- package/dist/http/handler.d.ts +2 -2
- package/dist/http/handler.js +0 -1
- package/dist/migrate/codegen/entityCodeGenerator.js +6 -4
- package/dist/migrate/codegen/entityTypes.d.ts +1 -1
- package/dist/migrate/codegen/entityTypes.js +4 -3
- package/dist/migrate/codegen/indexDecoratorSource.d.ts +5 -4
- package/dist/migrate/codegen/indexDecoratorSource.js +17 -13
- package/dist/migrate/codegen/sourceLiteral.d.ts +2 -0
- package/dist/migrate/codegen/sourceLiteral.js +4 -0
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +3 -3
- package/dist/migrate/migrator.d.ts +2 -2
- package/dist/migrate/schemaGenerator.d.ts +5 -5
- package/dist/mongo/mongoDialect.d.ts +1 -1
- package/dist/mongo/mongoDialect.js +3 -3
- package/dist/mongo/mongodbQuerier.js +0 -1
- package/dist/querier/abstractSqlQuerier.js +6 -6
- package/dist/schema/schemaASTBuilder.d.ts +3 -3
- package/dist/schema/schemaASTBuilder.js +2 -2
- package/dist/type/config.d.ts +1 -1
- package/dist/type/entity.d.ts +115 -68
- package/dist/type/migration.d.ts +7 -7
- package/dist/type/querierPool.d.ts +2 -2
- package/dist/type/query.d.ts +19 -27
- package/dist/type/queryAggregate.d.ts +38 -29
- package/dist/type/queryWhere.d.ts +12 -9
- package/dist/util/dialect.util.d.ts +3 -3
- package/dist/util/dialect.util.js +24 -15
- package/dist/util/relationQuery.util.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FieldKey, JsonFieldPaths, JsonFieldPathValue, RelationKey, RelationTarget } from './entity.js';
|
|
2
|
+
import type { QuerySelect } from './query.js';
|
|
2
3
|
import type { QueryRaw } from './queryRaw.js';
|
|
3
4
|
import type { ExpandScalar, IsMany, QueryComparableScalar, Scalar } from './utility.js';
|
|
4
5
|
import type { QueryVectorQuery } from './vector.js';
|
|
@@ -11,9 +12,9 @@ export type QueryTextSearchOptions<E> = {
|
|
|
11
12
|
*/
|
|
12
13
|
$value: string;
|
|
13
14
|
/**
|
|
14
|
-
*
|
|
15
|
+
* the fields to search, `{ title: true, body: true }`, in the order a MySQL `FULLTEXT` index lists them.
|
|
15
16
|
*/
|
|
16
|
-
$fields?:
|
|
17
|
+
$fields?: QuerySelect<E>;
|
|
17
18
|
/**
|
|
18
19
|
* Postgres text-search configuration (e.g. `'english'`), applied to both the document and the
|
|
19
20
|
* query. Defaults to the server's `default_text_search_config`. Ignored by other dialects.
|
|
@@ -27,17 +28,19 @@ export type QueryTextSearchOptions<E> = {
|
|
|
27
28
|
* filtered via nested typed objects; dotted relation paths are not supported (the dialects throw
|
|
28
29
|
* for non-JSON dotted keys).
|
|
29
30
|
*
|
|
30
|
-
*
|
|
31
|
-
* {@link
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* Fields and relations share one mapped type over `K extends keyof E`, which keeps each key linked to
|
|
32
|
+
* its property (see {@link QuerySelect}). JSON paths are not keys of `E`, so they are a second member,
|
|
33
|
+
* and only where the entity has one: an empty member would switch off the weak-type check that
|
|
34
|
+
* rejects `$where: 1`.
|
|
34
35
|
*
|
|
35
36
|
* An object and nothing else: in a union with ids or lists, TypeScript reports a wrong value against
|
|
36
37
|
* the whole `$where` instead of the key holding it. Ids are `{ id: 1 }`, or the by-id methods.
|
|
37
38
|
*/
|
|
38
|
-
export type QueryWhere<E> = QueryWhereRootOperator<E> & {
|
|
39
|
-
[
|
|
40
|
-
}
|
|
39
|
+
export type QueryWhere<E, K extends keyof E = FieldKey<E> | RelationKey<E>> = QueryWhereRootOperator<E> & {
|
|
40
|
+
[P in K]?: P extends FieldKey<E> ? QueryWhereFieldValue<E[P]> : QueryWhere<RelationTarget<E[P]>> | QueryRelationSizeFilter;
|
|
41
|
+
} & ([JsonFieldPaths<E>] extends [never] ? unknown : {
|
|
42
|
+
[P in JsonFieldPaths<E>]?: QueryWhereFieldValue<JsonFieldPathValue<E, P>>;
|
|
43
|
+
});
|
|
41
44
|
/**
|
|
42
45
|
* Filter a to-many relation by its row count.
|
|
43
46
|
* @example { users: { $size: 2 } }
|
|
@@ -145,10 +145,10 @@ export declare function parseRelationSize(val: unknown): number | QuerySizeCompa
|
|
|
145
145
|
*/
|
|
146
146
|
export declare function parseSortByCount(val: unknown): unknown;
|
|
147
147
|
/**
|
|
148
|
-
* Parse the `$group` (grouped columns) and `$
|
|
148
|
+
* Parse the `$group` (grouped columns) and `$select` (computed aggregates) maps into structured
|
|
149
149
|
* entries consumable by any dialect. Grouped columns come first, then computed columns.
|
|
150
150
|
*/
|
|
151
|
-
export declare function parseGroupMap<E>(group?: QueryGroupMap<E>,
|
|
151
|
+
export declare function parseGroupMap<E>(group?: QueryGroupMap<E>, select?: QueryAggMap<E>): ParsedGroupEntry[];
|
|
152
152
|
/**
|
|
153
153
|
* Whether `value` is a map of comparison operators rather than a value to compare against. Only a
|
|
154
154
|
* plain object qualifies: `Date`, `QueryRaw`, `Uint8Array` and arrays are all `typeof 'object'`, and
|
|
@@ -165,7 +165,7 @@ export declare function isOperatorMap(value: unknown): value is Record<string, u
|
|
|
165
165
|
export declare function assertNonNegativeInteger(value: number, clause: string): number;
|
|
166
166
|
/**
|
|
167
167
|
* Rejects a `$having`/`$sort` key naming something an aggregate does not emit. Its rows are its
|
|
168
|
-
* `$group` columns and its `$
|
|
168
|
+
* `$group` columns and its `$select` aliases; anything else is a value that is not there. Shared so
|
|
169
169
|
* SQL and MongoDB refuse the same query with the same words.
|
|
170
170
|
*/
|
|
171
171
|
export declare function throwUnknownAggregateColumn(key: string, clause: string): never;
|
|
@@ -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, isWhereMap, someKey } from './object.util.js';
|
|
6
|
+
import { entityName, getFieldKeys, getKeys, hasKeys, isScalarId, isRecord, isWhereMap, someKey, } from './object.util.js';
|
|
7
7
|
export function filterFieldKeys(meta, payload, callbackKey) {
|
|
8
8
|
return getKeys(payload).filter((key) => {
|
|
9
9
|
const fieldOpts = meta.fields[key];
|
|
@@ -355,10 +355,10 @@ export function parseSortByCount(val) {
|
|
|
355
355
|
return val.$count;
|
|
356
356
|
}
|
|
357
357
|
/**
|
|
358
|
-
* Parse the `$group` (grouped columns) and `$
|
|
358
|
+
* Parse the `$group` (grouped columns) and `$select` (computed aggregates) maps into structured
|
|
359
359
|
* entries consumable by any dialect. Grouped columns come first, then computed columns.
|
|
360
360
|
*/
|
|
361
|
-
export function parseGroupMap(group,
|
|
361
|
+
export function parseGroupMap(group, select) {
|
|
362
362
|
const entries = [];
|
|
363
363
|
const groupMap = group ?? {};
|
|
364
364
|
for (const alias of getKeys(groupMap)) {
|
|
@@ -366,22 +366,30 @@ export function parseGroupMap(group, agg) {
|
|
|
366
366
|
entries.push({ kind: 'key', alias });
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
|
-
if (!
|
|
369
|
+
if (!select) {
|
|
370
370
|
return entries;
|
|
371
371
|
}
|
|
372
|
-
for (const alias of getKeys(
|
|
373
|
-
const fnEntry =
|
|
372
|
+
for (const alias of getKeys(select)) {
|
|
373
|
+
const fnEntry = select[alias];
|
|
374
374
|
const key = getKeys(fnEntry)[0];
|
|
375
375
|
// Flat DISTINCT ops (`$countDistinct`, ...) normalize to their base op + a `distinct` flag.
|
|
376
376
|
const { op, distinct } = resolveAggregateOp(key);
|
|
377
|
-
|
|
378
|
-
if (fieldRef === undefined) {
|
|
379
|
-
throw TypeError(`empty aggregate function for: ${alias}`);
|
|
380
|
-
}
|
|
381
|
-
entries.push({ kind: 'fn', alias, op, fieldRef, distinct });
|
|
377
|
+
entries.push({ kind: 'fn', alias, op, fieldRef: aggregateFieldRef(alias, fnEntry[key]), distinct });
|
|
382
378
|
}
|
|
383
379
|
return entries;
|
|
384
380
|
}
|
|
381
|
+
/** The column an aggregate reads: `'*'`, or the one field its `{ field: true }` names. */
|
|
382
|
+
function aggregateFieldRef(alias, arg) {
|
|
383
|
+
const [field, ...rest] = arg === '*' ? [arg] : namedKeys(arg);
|
|
384
|
+
if (field === undefined || rest.length) {
|
|
385
|
+
throw new TypeError(`aggregate '${alias}' takes one field as { field: true }, or '*': got ${JSON.stringify(arg)}`);
|
|
386
|
+
}
|
|
387
|
+
return field;
|
|
388
|
+
}
|
|
389
|
+
/** The keys a `{ key: true }` map switches on; anything that is not such a map switches on none. */
|
|
390
|
+
function namedKeys(map) {
|
|
391
|
+
return isRecord(map) ? getKeys(map).filter((key) => map[key]) : [];
|
|
392
|
+
}
|
|
385
393
|
/**
|
|
386
394
|
* Whether `value` is a map of comparison operators rather than a value to compare against. Only a
|
|
387
395
|
* plain object qualifies: `Date`, `QueryRaw`, `Uint8Array` and arrays are all `typeof 'object'`, and
|
|
@@ -410,11 +418,11 @@ export function assertNonNegativeInteger(value, clause) {
|
|
|
410
418
|
}
|
|
411
419
|
/**
|
|
412
420
|
* Rejects a `$having`/`$sort` key naming something an aggregate does not emit. Its rows are its
|
|
413
|
-
* `$group` columns and its `$
|
|
421
|
+
* `$group` columns and its `$select` aliases; anything else is a value that is not there. Shared so
|
|
414
422
|
* SQL and MongoDB refuse the same query with the same words.
|
|
415
423
|
*/
|
|
416
424
|
export function throwUnknownAggregateColumn(key, clause) {
|
|
417
|
-
throw new TypeError(`cannot ${clause} by '${key}': it is neither a $group column nor
|
|
425
|
+
throw new TypeError(`cannot ${clause} by '${key}': it is neither a $group column nor a $select alias`);
|
|
418
426
|
}
|
|
419
427
|
/** {@link throwUnknownAggregateColumn} over every key of a clause, for backends that check up front. */
|
|
420
428
|
export function assertAggregateColumns(clauseMap, emitted, clause) {
|
|
@@ -430,8 +438,9 @@ export function assertAggregateColumns(clauseMap, emitted, clause) {
|
|
|
430
438
|
* where neither says, rather than guessed: every engine answers a guess with an error of its own.
|
|
431
439
|
*/
|
|
432
440
|
export function textSearchFields(meta, search) {
|
|
433
|
-
|
|
434
|
-
|
|
441
|
+
const named = namedKeys(search.$fields);
|
|
442
|
+
if (named.length) {
|
|
443
|
+
return named;
|
|
435
444
|
}
|
|
436
445
|
const fulltext = (meta.indexes ?? []).filter((index) => index.type === 'fulltext');
|
|
437
446
|
if (fulltext.length === 1) {
|
|
@@ -57,7 +57,7 @@ export declare function populatesRelations<E>(meta: EntityMeta<E>, populate?: Qu
|
|
|
57
57
|
export declare function countedRelations<E>(meta: EntityMeta<E>, counts: QueryCount<E> | undefined): {
|
|
58
58
|
readonly relKey: RelationKey<E>;
|
|
59
59
|
readonly relation: RelationMeta;
|
|
60
|
-
readonly where: QueryWhere<
|
|
60
|
+
readonly where: QueryWhere<object>;
|
|
61
61
|
}[];
|
|
62
62
|
export type ParsedRelationQuery<E extends object = object> = {
|
|
63
63
|
query: RelationQuery<E>;
|
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.59.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=24"
|