uql-orm 0.56.0 → 0.57.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 +1 -1
- package/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +4 -4
- package/dist/cockroachdb/cockroachDialect.d.ts +5 -2
- package/dist/cockroachdb/cockroachDialect.js +2 -10
- package/dist/d1/d1SqliteDialect.d.ts +1 -0
- package/dist/d1/d1SqliteDialect.js +2 -0
- package/dist/dialect/abstractSqlDialect.d.ts +195 -32
- package/dist/dialect/abstractSqlDialect.js +406 -199
- package/dist/dialect/aliases.d.ts +10 -7
- package/dist/dialect/aliases.js +12 -7
- package/dist/dialect/hydrateColumn.d.ts +8 -2
- package/dist/dialect/hydrateColumn.js +33 -1
- package/dist/dialect/jsonSql.d.ts +13 -5
- package/dist/dialect/jsonSql.js +24 -7
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +30 -2
- package/dist/dialect/mysqlLikeSqlDialect.js +56 -4
- package/dist/dialect/pgLikeSqlDialect.d.ts +20 -20
- package/dist/dialect/pgLikeSqlDialect.js +23 -48
- package/dist/dialect/pgVectorMetrics.d.ts +13 -0
- package/dist/dialect/pgVectorMetrics.js +17 -0
- package/dist/dialect/queryContext.d.ts +3 -7
- package/dist/dialect/queryContext.js +13 -8
- package/dist/dialect/queryJoins.d.ts +8 -4
- package/dist/dialect/queryJoins.js +25 -11
- package/dist/dialect/vectorSqlDialect.d.ts +2 -2
- package/dist/dialect/vectorSqlDialect.js +2 -3
- package/dist/entity/metadata/definition.js +3 -3
- package/dist/maria/mariaDialect.d.ts +13 -6
- package/dist/maria/mariaDialect.js +29 -9
- package/dist/migrate/cli.d.ts +2 -3
- package/dist/migrate/cli.js +2 -2
- package/dist/migrate/ddl/index.d.ts +1 -5
- package/dist/migrate/ddl/index.js +14 -25
- package/dist/migrate/ddl/indexDdl.d.ts +11 -2
- package/dist/migrate/ddl/indexDdl.js +17 -1
- package/dist/migrate/ddl/mssqlIndexDdl.d.ts +10 -0
- package/dist/migrate/ddl/mssqlIndexDdl.js +10 -0
- package/dist/migrate/ddl/mysqlIndexDdl.d.ts +10 -17
- package/dist/migrate/ddl/mysqlIndexDdl.js +16 -27
- package/dist/migrate/ddl/pgIndexDdl.d.ts +18 -8
- package/dist/migrate/ddl/pgIndexDdl.js +29 -12
- package/dist/migrate/migrator.d.ts +2 -2
- package/dist/migrate/schemaGenerator.d.ts +3 -3
- package/dist/migrate/schemaGenerator.js +5 -7
- package/dist/migrate/schemaGeneratorAsync.d.ts +2 -3
- package/dist/mongo/mongoDialect.d.ts +30 -17
- package/dist/mongo/mongoDialect.js +143 -101
- package/dist/mongo/mongodbQuerier.d.ts +10 -17
- package/dist/mongo/mongodbQuerier.js +31 -105
- package/dist/mssql/mssqlDialect.d.ts +16 -0
- package/dist/mssql/mssqlDialect.js +26 -4
- package/dist/mysql/mysqlDialect.d.ts +2 -0
- package/dist/mysql/mysqlDialect.js +4 -0
- package/dist/querier/abstractQuerier.d.ts +20 -36
- package/dist/querier/abstractQuerier.js +35 -129
- package/dist/querier/abstractQuerierPool.d.ts +2 -2
- package/dist/querier/abstractSqlQuerier.d.ts +4 -17
- package/dist/querier/abstractSqlQuerier.js +34 -44
- package/dist/schema/canonicalType.js +4 -4
- package/dist/schema/indexDifferences.js +4 -4
- package/dist/schema/schemaASTBuilder.js +31 -2
- package/dist/schema/schemaASTDiffer.js +5 -5
- package/dist/sqlite/sqliteDialect.d.ts +20 -1
- package/dist/sqlite/sqliteDialect.js +38 -7
- package/dist/turso/tursoDialect.d.ts +2 -0
- package/dist/turso/tursoDialect.js +2 -0
- package/dist/type/config.d.ts +2 -2
- package/dist/type/dialect.d.ts +4 -5
- package/dist/type/entity.d.ts +2 -1
- package/dist/type/migratorDialect.d.ts +4 -0
- package/dist/type/querier.d.ts +6 -6
- package/dist/type/query.d.ts +25 -48
- package/dist/type/query.js +10 -5
- package/dist/type/queryAggregate.d.ts +10 -10
- package/dist/type/queryAggregate.js +1 -1
- package/dist/type/universalQuerier.d.ts +4 -4
- package/dist/util/dialect.util.d.ts +1 -1
- package/dist/util/field.util.d.ts +5 -0
- package/dist/util/field.util.js +19 -0
- package/dist/util/object.util.d.ts +2 -0
- package/dist/util/object.util.js +4 -0
- package/dist/util/relationQuery.util.d.ts +12 -65
- package/dist/util/relationQuery.util.js +27 -81
- package/dist/util/rowKey.util.d.ts +1 -11
- package/dist/util/rowKey.util.js +1 -13
- package/package.json +1 -1
- package/dist/querier/relationCount.d.ts +0 -16
- package/dist/querier/relationCount.js +0 -121
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.57.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=24"
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { Querier, Query, QueryCount, Type } from '../type/index.js';
|
|
2
|
-
/** What counting asks of a querier, rather than the whole interface: two reads, both batched. */
|
|
3
|
-
type CountingQuerier = Pick<Querier, 'aggregate' | 'findMany'>;
|
|
4
|
-
/**
|
|
5
|
-
* A `$count` groups its tallies by the parent's id, so the id has to outlive the projection - the
|
|
6
|
-
* same reason populating a relation keeps it. A whitelisting `$select` gains the key and an
|
|
7
|
-
* `$exclude` loses it; the raw-array `$select` form has nothing to augment, so it is refused.
|
|
8
|
-
*/
|
|
9
|
-
export declare function withIdForCounts<E extends object>(entity: Type<E>, q: Query<E>): Query<E>;
|
|
10
|
-
/**
|
|
11
|
-
* How many rows each counted relation holds, under `_count` on every parent. One grouped aggregate
|
|
12
|
-
* per relation over every parent at once - the batching a populated to-many already gets - so the
|
|
13
|
-
* cost stays flat in the number of rows the read returned rather than one statement per row.
|
|
14
|
-
*/
|
|
15
|
-
export declare function fillRelationCounts<E>(querier: CountingQuerier, entity: Type<E>, payload: E[], count?: QueryCount<E>): Promise<void>;
|
|
16
|
-
export {};
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { COUNT_ALIAS } from '../dialect/aliases.js';
|
|
2
|
-
import { getMeta, soleIdOf } from '../entity/index.js';
|
|
3
|
-
import { COUNT_RESULT_KEY } from '../type/index.js';
|
|
4
|
-
import { asSelectMap, dataKeyed, getKeys, joinedColumns, keyColumns, parentJoins, parentsIn, rowKey, targetKeyColumns, } from '../util/index.js';
|
|
5
|
-
/**
|
|
6
|
-
* A `$count` groups its tallies by the parent's id, so the id has to outlive the projection - the
|
|
7
|
-
* same reason populating a relation keeps it. A whitelisting `$select` gains the key and an
|
|
8
|
-
* `$exclude` loses it; the raw-array `$select` form has nothing to augment, so it is refused.
|
|
9
|
-
*/
|
|
10
|
-
export function withIdForCounts(entity, q) {
|
|
11
|
-
const meta = getMeta(entity);
|
|
12
|
-
if (!q.$count) {
|
|
13
|
-
return q;
|
|
14
|
-
}
|
|
15
|
-
const { ids } = meta;
|
|
16
|
-
const named = ids.join(', ');
|
|
17
|
-
if (q.$distinct) {
|
|
18
|
-
// The keys would have to join the projection for the tallies to group by, and that is the set
|
|
19
|
-
// `$distinct` collapses on: adding them silently stops the grouping collapsing anything at all.
|
|
20
|
-
throw new TypeError(`$count cannot be combined with $distinct: the tallies group by each row's '${named}', which the grouping does not keep.`);
|
|
21
|
-
}
|
|
22
|
-
if (Array.isArray(q.$select)) {
|
|
23
|
-
// Nothing to add the keys to, and without them every tally would be looked up by `undefined` and
|
|
24
|
-
// come back zero. Refused rather than answered wrong, the way an unorderable clause is.
|
|
25
|
-
throw new TypeError(`$count needs the '${named}' of each row to group its tallies by, which a raw $select cannot carry. Use a $select map, or drop the $count.`);
|
|
26
|
-
}
|
|
27
|
-
const next = { ...q };
|
|
28
|
-
const select = asSelectMap(q.$select);
|
|
29
|
-
const missing = select ? ids.filter((key) => !select[key]) : [];
|
|
30
|
-
if (missing.length) {
|
|
31
|
-
next.$select = { ...select, ...Object.fromEntries(missing.map((key) => [key, true])) };
|
|
32
|
-
}
|
|
33
|
-
const excluded = ids.filter((key) => q.$exclude?.[key]);
|
|
34
|
-
if (excluded.length) {
|
|
35
|
-
const kept = { ...q.$exclude };
|
|
36
|
-
for (const key of excluded) {
|
|
37
|
-
delete kept[key];
|
|
38
|
-
}
|
|
39
|
-
next.$exclude = kept;
|
|
40
|
-
}
|
|
41
|
-
return next;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* How many rows each counted relation holds, under `_count` on every parent. One grouped aggregate
|
|
45
|
-
* per relation over every parent at once - the batching a populated to-many already gets - so the
|
|
46
|
-
* cost stays flat in the number of rows the read returned rather than one statement per row.
|
|
47
|
-
*/
|
|
48
|
-
export async function fillRelationCounts(querier, entity, payload, count) {
|
|
49
|
-
if (!payload.length || !count) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
const meta = getMeta(entity);
|
|
53
|
-
// The tallies come back keyed by the columns *this relation* joins from, so those columns are kept
|
|
54
|
-
// beside them: reading the parent through `meta.ids` instead matches only where the two coincide,
|
|
55
|
-
// which is a to-many and nothing else.
|
|
56
|
-
const counted = new Map();
|
|
57
|
-
for (const relKey of getKeys(count)) {
|
|
58
|
-
const value = count[relKey];
|
|
59
|
-
const relOpts = meta.relations[relKey];
|
|
60
|
-
if (!value || !relOpts) {
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
const where = typeof value === 'object' ? value.$where : undefined;
|
|
64
|
-
const joins = parentJoins(relOpts, meta.ids.length);
|
|
65
|
-
counted.set(relKey, {
|
|
66
|
-
parentKeys: keyColumns(joins, 'parent'),
|
|
67
|
-
byParent: await countPerParent(querier, relOpts, joins, payload, where),
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
for (const parent of payload) {
|
|
71
|
-
// A plain object, unlike the tallies below: this one is keyed by relation names the entity
|
|
72
|
-
// declares, not by data, and it is handed to the caller - who would meet a null prototype.
|
|
73
|
-
const row = {};
|
|
74
|
-
for (const [relKey, { parentKeys, byParent }] of counted) {
|
|
75
|
-
// A parent the grouped result has no row for matched nothing, which is a zero rather than a
|
|
76
|
-
// gap: `_count` names what the caller asked to count, so every key it asked for is present.
|
|
77
|
-
row[relKey] = byParent[rowKey(parent, parentKeys)] ?? 0;
|
|
78
|
-
}
|
|
79
|
-
parent[COUNT_RESULT_KEY] = row;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
async function countPerParent(querier, relOpts, joins, parents, where) {
|
|
83
|
-
const through = relOpts.through;
|
|
84
|
-
if (through) {
|
|
85
|
-
return countThroughPerParent(querier, relOpts, through(), joins, parents, where);
|
|
86
|
-
}
|
|
87
|
-
return groupedCount(querier, relOpts.entity(), joins, {
|
|
88
|
-
...where,
|
|
89
|
-
...parentsIn(joins, parents),
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* A many-to-many counts its junction rows, one per pairing. A filter names the target's columns,
|
|
94
|
-
* which the junction does not have, so the matching targets are resolved first and the junction
|
|
95
|
-
* counted against them - the one shape here that costs a second statement, and only when filtered.
|
|
96
|
-
*/
|
|
97
|
-
async function countThroughPerParent(querier, relOpts, throughEntity, joins, parents, where) {
|
|
98
|
-
const throughWhere = parentsIn(joins, parents);
|
|
99
|
-
if (where) {
|
|
100
|
-
const target = relOpts.entity();
|
|
101
|
-
const targetId = soleIdOf(getMeta(target), 'a many-to-many target');
|
|
102
|
-
const targets = await querier.findMany(target, { $select: { [targetId]: true }, $where: where });
|
|
103
|
-
const [targetColumn] = targetKeyColumns(relOpts, joins.length);
|
|
104
|
-
throughWhere[targetColumn] = targets.map((it) => it[targetId]);
|
|
105
|
-
}
|
|
106
|
-
return groupedCount(querier, throughEntity, joins, throughWhere);
|
|
107
|
-
}
|
|
108
|
-
/** `SELECT <keys>, COUNT(*) ... GROUP BY <keys>`, as a lookup from parent key to tally. */
|
|
109
|
-
async function groupedCount(querier, entity, joins, where) {
|
|
110
|
-
const $agg = { [COUNT_ALIAS]: { $count: '*' } };
|
|
111
|
-
const $group = joinedColumns(joins);
|
|
112
|
-
const rows = await querier.aggregate(entity, { $group, $agg, $where: where });
|
|
113
|
-
const byParent = dataKeyed();
|
|
114
|
-
// Keyed by every joined column, which is how a tally finds the one parent whose whole key it
|
|
115
|
-
// matches - and how the rows an over-selecting `IN` brought back find no parent at all.
|
|
116
|
-
const joinedKeys = keyColumns(joins, 'joined');
|
|
117
|
-
for (const row of rows) {
|
|
118
|
-
byParent[rowKey(row, joinedKeys)] = Number(row[COUNT_ALIAS]);
|
|
119
|
-
}
|
|
120
|
-
return byParent;
|
|
121
|
-
}
|