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/dist/type/query.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FieldKey,
|
|
1
|
+
import type { FieldKey, JsonFieldPaths, RelationKey, RelationTarget, WrittenId } from './entity.js';
|
|
2
2
|
import type { QueryLock } from './queryLock.js';
|
|
3
3
|
import type { QueryRaw } from './queryRaw.js';
|
|
4
4
|
import type { QueryWhere } from './queryWhere.js';
|
|
@@ -25,16 +25,6 @@ export type QueryOptions = {
|
|
|
25
25
|
*/
|
|
26
26
|
autoPrefix?: boolean;
|
|
27
27
|
};
|
|
28
|
-
export type QuerySelectOptions = {
|
|
29
|
-
/**
|
|
30
|
-
* prefix the query with this.
|
|
31
|
-
*/
|
|
32
|
-
prefix?: string;
|
|
33
|
-
/**
|
|
34
|
-
* automatically add the prefix for the alias.
|
|
35
|
-
*/
|
|
36
|
-
autoPrefixAlias?: boolean;
|
|
37
|
-
};
|
|
38
28
|
/**
|
|
39
29
|
* Query field selection - `{ name: true }` whitelists specific fields. Fields only: a relation is a
|
|
40
30
|
* sub-query rather than a projection flag, and a whitelist naming one could not say whether the
|
|
@@ -66,9 +56,8 @@ export type QueryPopulate<E> = {
|
|
|
66
56
|
export declare const COUNT_RESULT_KEY = "_count";
|
|
67
57
|
/**
|
|
68
58
|
* How many rows each named relation holds per parent, `true` for all of them or a filter to narrow
|
|
69
|
-
* which ones count
|
|
70
|
-
*
|
|
71
|
-
* a relation of the same name that `$populate` filled with rows.
|
|
59
|
+
* which ones count: a correlated count in the read's own statement, so no related row is loaded. Comes
|
|
60
|
+
* back under `_count`, which keeps it clear of a relation of the same name `$populate` filled.
|
|
72
61
|
*/
|
|
73
62
|
export type QueryCount<E> = {
|
|
74
63
|
[K in ToManyRelationKey<E>]?: BooleanLike | QueryFilter<RelationTarget<E[K]>>;
|
|
@@ -82,7 +71,7 @@ export type QueryConflictPaths<E> = {
|
|
|
82
71
|
/**
|
|
83
72
|
* Options to populate a relation declared as `V`, by its cardinality.
|
|
84
73
|
*/
|
|
85
|
-
export type QueryPopulateRelationOptions<V> =
|
|
74
|
+
export type QueryPopulateRelationOptions<V> = IsMany<V> extends true ? RelationQuery<RelationTarget<V>> : QueryUnique<RelationTarget<V>> & {
|
|
86
75
|
$required?: boolean;
|
|
87
76
|
};
|
|
88
77
|
/**
|
|
@@ -276,14 +265,13 @@ export type Query<E> = {
|
|
|
276
265
|
* Declared beside the type they describe so the two cannot drift, and `satisfies` fails the build
|
|
277
266
|
* rather than the runtime if a clause is ever renamed.
|
|
278
267
|
*
|
|
279
|
-
* `$lock`
|
|
280
|
-
*
|
|
268
|
+
* `$lock` is only in {@link QUERY_STATEMENT_CLAUSES}: neither a wire query nor a relation's query
|
|
269
|
+
* accepts it.
|
|
281
270
|
*/
|
|
282
271
|
export declare const QUERY_OBJECT_CLAUSES: readonly ["$select", "$populate", "$exclude", "$where", "$sort"];
|
|
283
272
|
/**
|
|
284
|
-
* Object clauses only the statement itself takes
|
|
285
|
-
*
|
|
286
|
-
* populated relation's rows are assembled after that, so there is nothing for a nested one to count.
|
|
273
|
+
* Object clauses only the statement itself takes: a populated relation's rows keep their declared type,
|
|
274
|
+
* so a `$count` inside one would have no `_count` to land in.
|
|
287
275
|
*/
|
|
288
276
|
export declare const QUERY_ROOT_OBJECT_CLAUSES: readonly ["$count"];
|
|
289
277
|
export declare const QUERY_NUMBER_CLAUSES: readonly ["$skip", "$limit"];
|
|
@@ -294,6 +282,16 @@ export declare const QUERY_NUMBER_CLAUSES: readonly ["$skip", "$limit"];
|
|
|
294
282
|
*/
|
|
295
283
|
export declare const QUERY_ROOT_NUMBER_CLAUSES: readonly ["$candidates"];
|
|
296
284
|
export declare const QUERY_BOOLEAN_CLAUSES: readonly ["$distinct"];
|
|
285
|
+
/** The clauses that describe the statement, which a populated relation's own query refuses by name. */
|
|
286
|
+
export declare const QUERY_STATEMENT_CLAUSES: readonly ["$lock", "$count", "$candidates"];
|
|
287
|
+
type RelationClause = (typeof QUERY_OBJECT_CLAUSES | typeof QUERY_NUMBER_CLAUSES | typeof QUERY_BOOLEAN_CLAUSES)[number];
|
|
288
|
+
/**
|
|
289
|
+
* A populated relation's own query: the clause groups its runtime check accepts, so the two cannot
|
|
290
|
+
* drift, and a clause added to {@link Query} stays off it until it joins one of them.
|
|
291
|
+
*/
|
|
292
|
+
export type RelationQuery<E = object> = Pick<Query<E>, RelationClause> & {
|
|
293
|
+
$required?: boolean;
|
|
294
|
+
};
|
|
297
295
|
/**
|
|
298
296
|
* options to get a single record.
|
|
299
297
|
*/
|
|
@@ -315,18 +313,7 @@ export type QueryUnique<E> = Pick<QueryOne<E>, '$select' | '$exclude' | '$popula
|
|
|
315
313
|
* populated relation's own query - stays the concrete {@link Query} it is today.
|
|
316
314
|
* @internal
|
|
317
315
|
*/
|
|
318
|
-
type QueryProjection<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E>> =
|
|
319
|
-
$count?: {
|
|
320
|
-
[K in C & keyof QueryCount<E>]?: QueryCount<E>[K];
|
|
321
|
-
};
|
|
322
|
-
};
|
|
323
|
-
/**
|
|
324
|
-
* {@link QueryProjection} without `$count`, which a stream cannot honor. Split out rather than
|
|
325
|
-
* subtracted afterwards: an optional key is a *known* key even when its value maps over `never`, so
|
|
326
|
-
* a statement that must not take the clause has to be built without it in the first place.
|
|
327
|
-
* @internal
|
|
328
|
-
*/
|
|
329
|
-
type QueryStreamProjection<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>> = {
|
|
316
|
+
type QueryProjection<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E>> = {
|
|
330
317
|
$select?: {
|
|
331
318
|
[K in S]?: V;
|
|
332
319
|
} | readonly QueryRaw[];
|
|
@@ -336,12 +323,10 @@ type QueryStreamProjection<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P
|
|
|
336
323
|
$populate?: {
|
|
337
324
|
[K in P]?: QueryPopulate<E>[K];
|
|
338
325
|
};
|
|
326
|
+
$count?: {
|
|
327
|
+
[K in C & keyof QueryCount<E>]?: QueryCount<E>[K];
|
|
328
|
+
};
|
|
339
329
|
};
|
|
340
|
-
/**
|
|
341
|
-
* A {@link QueryProjected} a stream can honor: no `$count`, which is batched over a result set a
|
|
342
|
-
* stream never holds all of.
|
|
343
|
-
*/
|
|
344
|
-
export type QueryStreamProjected<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>> = Except<Query<E>, '$count'> & QueryStreamProjection<E, S, V, X, P>;
|
|
345
330
|
/**
|
|
346
331
|
* A {@link Query} whose projection is captured, so {@link QueryFindResult} can shape the row.
|
|
347
332
|
*/
|
|
@@ -357,15 +342,7 @@ export type QueryOneProjected<E, S extends FieldKey<E>, V, X extends FieldKey<E>
|
|
|
357
342
|
* why `$exclude` is only read on the branch where there is none.
|
|
358
343
|
* @internal
|
|
359
344
|
*/
|
|
360
|
-
type ProjectedKeys<E, S, V, X, P
|
|
361
|
-
/**
|
|
362
|
-
* The id key when it can be named, and nothing when it cannot: {@link IdKey} widens to *every* field
|
|
363
|
-
* for an entity whose id is neither branded nor called `id`/`_id`/`uuid`, and adding that back would
|
|
364
|
-
* hand the caller a row claiming fields the query never fetched. Missing an id costs a `$select`
|
|
365
|
-
* entry; promising absent fields is the bug this type exists to prevent.
|
|
366
|
-
* @internal
|
|
367
|
-
*/
|
|
368
|
-
type NamedIdKey<E> = [FieldKey<E>] extends [IdKey<E>] ? never : IdKey<E>;
|
|
345
|
+
type ProjectedKeys<E, S, V, X, P> = ([V] extends [false | 0] ? Exclude<FieldKey<E>, S> : [S] extends [never] ? Exclude<FieldKey<E>, X> : S) | P;
|
|
369
346
|
/**
|
|
370
347
|
* Whether every entry of the captured map says the same thing: all selected, or all subtracted.
|
|
371
348
|
* @internal
|
|
@@ -394,8 +371,8 @@ type CountedRelations<C extends PropertyKey> = [C] extends [never] ? unknown : {
|
|
|
394
371
|
};
|
|
395
372
|
};
|
|
396
373
|
/** @internal */
|
|
397
|
-
type QueryProjectedRow<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E>> = [S | X] extends [never] ? E : IsUniform<V> extends true ? [PopulatedToMany<E, P>] extends [never] ? Pick<E, ProjectedKeys<E, S, V, X, P
|
|
398
|
-
Pick<E, Exclude<ProjectedKeys<E, S, V, X, P
|
|
374
|
+
type QueryProjectedRow<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E>> = [S | X] extends [never] ? E : IsUniform<V> extends true ? [PopulatedToMany<E, P>] extends [never] ? Pick<E, ProjectedKeys<E, S, V, X, P> & keyof E> : // A populated to-many is always a list, empty where the parent has no children, so it maps
|
|
375
|
+
Pick<E, Exclude<ProjectedKeys<E, S, V, X, P>, PopulatedToMany<E, P>> & keyof E> & {
|
|
399
376
|
[K in PopulatedToMany<E, P>]-?: NonNullable<E[K]>;
|
|
400
377
|
} : E;
|
|
401
378
|
/** The to-many relations a query populated, which come back as lists rather than as optional ones. */
|
package/dist/type/query.js
CHANGED
|
@@ -9,8 +9,8 @@ export const COUNT_RESULT_KEY = '_count';
|
|
|
9
9
|
* Declared beside the type they describe so the two cannot drift, and `satisfies` fails the build
|
|
10
10
|
* rather than the runtime if a clause is ever renamed.
|
|
11
11
|
*
|
|
12
|
-
* `$lock`
|
|
13
|
-
*
|
|
12
|
+
* `$lock` is only in {@link QUERY_STATEMENT_CLAUSES}: neither a wire query nor a relation's query
|
|
13
|
+
* accepts it.
|
|
14
14
|
*/
|
|
15
15
|
export const QUERY_OBJECT_CLAUSES = [
|
|
16
16
|
'$select',
|
|
@@ -20,9 +20,8 @@ export const QUERY_OBJECT_CLAUSES = [
|
|
|
20
20
|
'$sort',
|
|
21
21
|
];
|
|
22
22
|
/**
|
|
23
|
-
* Object clauses only the statement itself takes
|
|
24
|
-
*
|
|
25
|
-
* populated relation's rows are assembled after that, so there is nothing for a nested one to count.
|
|
23
|
+
* Object clauses only the statement itself takes: a populated relation's rows keep their declared type,
|
|
24
|
+
* so a `$count` inside one would have no `_count` to land in.
|
|
26
25
|
*/
|
|
27
26
|
export const QUERY_ROOT_OBJECT_CLAUSES = ['$count'];
|
|
28
27
|
export const QUERY_NUMBER_CLAUSES = ['$skip', '$limit'];
|
|
@@ -33,3 +32,9 @@ export const QUERY_NUMBER_CLAUSES = ['$skip', '$limit'];
|
|
|
33
32
|
*/
|
|
34
33
|
export const QUERY_ROOT_NUMBER_CLAUSES = ['$candidates'];
|
|
35
34
|
export const QUERY_BOOLEAN_CLAUSES = ['$distinct'];
|
|
35
|
+
/** The clauses that describe the statement, which a populated relation's own query refuses by name. */
|
|
36
|
+
export const QUERY_STATEMENT_CLAUSES = [
|
|
37
|
+
'$lock',
|
|
38
|
+
...QUERY_ROOT_OBJECT_CLAUSES,
|
|
39
|
+
...QUERY_ROOT_NUMBER_CLAUSES,
|
|
40
|
+
];
|
|
@@ -37,7 +37,7 @@ export type QueryAggregateOp = (typeof QUERY_AGGREGATE_OPS)[number];
|
|
|
37
37
|
export declare function isQueryAggregateOp(op: string): op is QueryAggregateOp;
|
|
38
38
|
/**
|
|
39
39
|
* DISTINCT-qualified aggregate ops, each mapped to the base op it applies to a field's distinct
|
|
40
|
-
* values: `$countDistinct`
|
|
40
|
+
* values: `$countDistinct` -> `COUNT(DISTINCT col)`, and likewise `$sumDistinct`/`$avgDistinct`. Flat
|
|
41
41
|
* (not a nested `{ $distinct }` argument) so the op is self-documenting and greppable. `$min`/`$max`
|
|
42
42
|
* are omitted: DISTINCT is a no-op for them.
|
|
43
43
|
*/
|
|
@@ -91,11 +91,11 @@ type ExactlyOne<T> = {
|
|
|
91
91
|
* compile error). Only `$count` accepts `'*'` (i.e. `COUNT(*)`); every other op requires a field.
|
|
92
92
|
* DISTINCT variants are flat ops (`$countDistinct`/`$sumDistinct`/`$avgDistinct`) taking a field.
|
|
93
93
|
*
|
|
94
|
-
* @example { $count: '*' }
|
|
95
|
-
* @example { $countDistinct: 'id' }
|
|
96
|
-
* @example { $sum: 'amount' }
|
|
97
|
-
* @example { $sumDistinct: 'amount' }
|
|
98
|
-
* @example { $avg: 'age' }
|
|
94
|
+
* @example { $count: '*' } -> COUNT(*)
|
|
95
|
+
* @example { $countDistinct: 'id' } -> COUNT(DISTINCT "id")
|
|
96
|
+
* @example { $sum: 'amount' } -> SUM("amount")
|
|
97
|
+
* @example { $sumDistinct: 'amount' } -> SUM(DISTINCT "amount")
|
|
98
|
+
* @example { $avg: 'age' } -> AVG("age")
|
|
99
99
|
*/
|
|
100
100
|
export type QueryAggregateFn<E> = ExactlyOne<QueryAggregateArgMap<E>>;
|
|
101
101
|
/** A single-key `{ [op]: unknown }` shape for each op in `Ops`, matched to infer that op's result. */
|
|
@@ -113,7 +113,7 @@ type CountingOp = OpsOf<'$count' | '$countDistinct'>;
|
|
|
113
113
|
*
|
|
114
114
|
* @example
|
|
115
115
|
* ```ts
|
|
116
|
-
* { status: true } //
|
|
116
|
+
* { status: true } // -> GROUP BY "status"
|
|
117
117
|
* ```
|
|
118
118
|
*/
|
|
119
119
|
export type QueryGroupMap<E> = {
|
|
@@ -127,7 +127,7 @@ export type QueryGroupMap<E> = {
|
|
|
127
127
|
* @example
|
|
128
128
|
* ```ts
|
|
129
129
|
* { count: { $count: '*' }, avgAge: { $avg: 'age' } }
|
|
130
|
-
* //
|
|
130
|
+
* // -> COUNT(*) AS "count", AVG("age") AS "avgAge"
|
|
131
131
|
* ```
|
|
132
132
|
*/
|
|
133
133
|
export type QueryAggMap<E> = {
|
|
@@ -172,11 +172,11 @@ export type QueryAggregateResult<E, G, A> = Simplify<{
|
|
|
172
172
|
-readonly [K in keyof A]: QueryAggregateFnResult<E, A[K]>;
|
|
173
173
|
}>;
|
|
174
174
|
/**
|
|
175
|
-
* Erased runtime shape of a HAVING clause (alias
|
|
175
|
+
* Erased runtime shape of a HAVING clause (alias -> comparison), consumed by the dialect builders.
|
|
176
176
|
* Values are `unknown` because the SQL is built generically; the typed, per-column value checking
|
|
177
177
|
* lives in {@link QueryAggregate.$having}.
|
|
178
178
|
*
|
|
179
|
-
* @example { count: { $gt: 5 } }
|
|
179
|
+
* @example { count: { $gt: 5 } } -> HAVING COUNT(*) > 5
|
|
180
180
|
*/
|
|
181
181
|
export type QueryHavingMap = {
|
|
182
182
|
readonly [alias: string]: QueryWhereFieldValue<unknown> | undefined;
|
|
@@ -8,7 +8,7 @@ export function isQueryAggregateOp(op) {
|
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* DISTINCT-qualified aggregate ops, each mapped to the base op it applies to a field's distinct
|
|
11
|
-
* values: `$countDistinct`
|
|
11
|
+
* values: `$countDistinct` -> `COUNT(DISTINCT col)`, and likewise `$sumDistinct`/`$avgDistinct`. Flat
|
|
12
12
|
* (not a nested `{ $distinct }` argument) so the op is self-documenting and greppable. `$min`/`$max`
|
|
13
13
|
* are omitted: DISTINCT is a no-op for them.
|
|
14
14
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EntityData, EntityId, FieldKey, RelationKey, UpdatePayload, WrittenId } from './entity.js';
|
|
2
|
-
import type { QueryConflictPaths, QueryFilter, QueryFindResult, QueryOneProjected, QueryOptions, QueryPage, QueryProjected, QuerySearch,
|
|
2
|
+
import type { QueryConflictPaths, QueryFilter, QueryFindResult, QueryOneProjected, QueryOptions, QueryPage, QueryProjected, QuerySearch, QueryUpsertOneResult, QueryUpsertManyResult } from './query.js';
|
|
3
3
|
import type { QueryAggMap, QueryAggregate, QueryAggregateResult, QueryGroupMap } from './queryAggregate.js';
|
|
4
4
|
import type { Type } from './utility.js';
|
|
5
5
|
import type { QuerierCountedResult, QuerierResult, QuerierTransport } from './wire.js';
|
|
@@ -97,14 +97,14 @@ export interface SharedQuerier<W extends QuerierTransport, O, DO = O> {
|
|
|
97
97
|
*/
|
|
98
98
|
export interface UniversalQuerier extends SharedQuerier<'server', QueryOptions> {
|
|
99
99
|
/**
|
|
100
|
-
* streams the records matching the given search parameters as an async iterable
|
|
101
|
-
*
|
|
100
|
+
* streams the records matching the given search parameters as an async iterable, each with the
|
|
101
|
+
* relations and counts `findMany` reads. Fires no lifecycle hooks, and holds one row at a time, for
|
|
102
102
|
* bulk reads (ETL, exports, migrations).
|
|
103
103
|
* @param entity the target entity
|
|
104
104
|
* @param q the criteria options
|
|
105
105
|
* @return an async iterable of records
|
|
106
106
|
*/
|
|
107
|
-
findManyStream<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never>(entity: Type<E>, q:
|
|
107
|
+
findManyStream<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(entity: Type<E>, q: QueryProjected<E, S, V, X, P, C>, opts?: QueryOptions): AsyncIterable<QueryFindResult<E, S, V, X, P, C>>;
|
|
108
108
|
/**
|
|
109
109
|
* Insert a single record and return its ID (provided, `onInsert`-generated, or
|
|
110
110
|
* database-generated - see {@link UniversalQuerier.insertMany} for the exact semantics).
|
|
@@ -128,7 +128,7 @@ export type ParsedGroupEntry = {
|
|
|
128
128
|
readonly alias: string;
|
|
129
129
|
readonly op: QueryAggregateOp;
|
|
130
130
|
readonly fieldRef: string;
|
|
131
|
-
/** `true` for a flat distinct op (`$countDistinct`, ...)
|
|
131
|
+
/** `true` for a flat distinct op (`$countDistinct`, ...) -> `COUNT(DISTINCT field)`. */
|
|
132
132
|
readonly distinct: boolean;
|
|
133
133
|
};
|
|
134
134
|
/**
|
|
@@ -22,6 +22,11 @@ export declare const COLUMN_TYPES_BY_FAMILY: {
|
|
|
22
22
|
};
|
|
23
23
|
/** The family of a logical field type, or `undefined` where it names none. */
|
|
24
24
|
export declare function columnFamily(type: unknown): ColumnFamily | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Whether a field's column holds whole numbers: a declared integer type, or a `Number` or `BigInt`
|
|
27
|
+
* with no scale, which every engine here stores as BIGINT.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isIntegerColumn(field: Pick<FieldOptions, 'type' | 'columnType' | 'precision' | 'scale'>): boolean;
|
|
25
30
|
/**
|
|
26
31
|
* Whether the field's expression is spliced into each statement that reads it, rather than stored.
|
|
27
32
|
* Every read site asks this - the DDL skip, the projection, the `$where` and `ORDER BY` operands -
|
package/dist/util/field.util.js
CHANGED
|
@@ -46,6 +46,25 @@ for (const family of getKeys(COLUMN_TYPES_BY_FAMILY)) {
|
|
|
46
46
|
export function columnFamily(type) {
|
|
47
47
|
return FAMILY_OF.get(typeof type === 'string' ? type.toLowerCase() : type);
|
|
48
48
|
}
|
|
49
|
+
/** The numeric column types that hold whole numbers. */
|
|
50
|
+
const INTEGER_COLUMN_TYPES = new Set([
|
|
51
|
+
'int',
|
|
52
|
+
'integer',
|
|
53
|
+
'tinyint',
|
|
54
|
+
'smallint',
|
|
55
|
+
'bigint',
|
|
56
|
+
]);
|
|
57
|
+
/**
|
|
58
|
+
* Whether a field's column holds whole numbers: a declared integer type, or a `Number` or `BigInt`
|
|
59
|
+
* with no scale, which every engine here stores as BIGINT.
|
|
60
|
+
*/
|
|
61
|
+
export function isIntegerColumn(field) {
|
|
62
|
+
const type = field.columnType ?? field.type;
|
|
63
|
+
if (typeof type === 'string') {
|
|
64
|
+
return INTEGER_COLUMN_TYPES.has(type.toLowerCase());
|
|
65
|
+
}
|
|
66
|
+
return type === BigInt || (type === Number && !field.precision && !field.scale);
|
|
67
|
+
}
|
|
49
68
|
/**
|
|
50
69
|
* Whether the field's expression is spliced into each statement that reads it, rather than stored.
|
|
51
70
|
* Every read site asks this - the DDL skip, the projection, the `$where` and `ORDER BY` operands -
|
|
@@ -19,6 +19,8 @@ export declare function someValue(obj: object, pred: (value: unknown) => boolean
|
|
|
19
19
|
export declare function isOperatorObject(value: unknown): value is Record<string, unknown>;
|
|
20
20
|
/** Whether every key of the non-empty object `value` is an operator (no plain field names mixed in). */
|
|
21
21
|
export declare function isOperatorOnlyObject(value: unknown): value is Record<string, unknown>;
|
|
22
|
+
/** Whether `value` is an object that is not an array, whose keys can be read. */
|
|
23
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
22
24
|
export declare function getKeys<T extends object>(obj: T): (keyof T & string)[];
|
|
23
25
|
/** The entries of `record` holding a value: a key declared but left `undefined` is no entry at all. */
|
|
24
26
|
export declare function definedEntries<K extends string, V>(record: Partial<Record<K, V>>): [K, V][];
|
package/dist/util/object.util.js
CHANGED
|
@@ -49,6 +49,10 @@ export function isOperatorObject(value) {
|
|
|
49
49
|
export function isOperatorOnlyObject(value) {
|
|
50
50
|
return hasKeys(value) && !Array.isArray(value) && !someKey(value, (key) => !isOperatorKey(key));
|
|
51
51
|
}
|
|
52
|
+
/** Whether `value` is an object that is not an array, whose keys can be read. */
|
|
53
|
+
export function isRecord(value) {
|
|
54
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
55
|
+
}
|
|
52
56
|
export function getKeys(obj) {
|
|
53
57
|
return obj ? Object.keys(obj) : [];
|
|
54
58
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EntityMeta,
|
|
1
|
+
import type { EntityMeta, QueryCount, QueryPopulate, RelationKey, RelationMeta, RelationQuery, QueryWhere } from '../type/index.js';
|
|
2
2
|
export type RelationRequestSummary<E> = {
|
|
3
3
|
readonly requestedKeys: readonly RelationKey<E>[];
|
|
4
4
|
readonly joinableKeys: readonly RelationKey<E>[];
|
|
@@ -32,67 +32,10 @@ export declare function parentJoins(relOpts: Pick<RelationMeta, 'references' | '
|
|
|
32
32
|
* which is the mistake this module exists to prevent.
|
|
33
33
|
*/
|
|
34
34
|
export declare function targetKeyColumns(relOpts: Pick<RelationMeta, 'references'>, parentKeyCount: number): string[];
|
|
35
|
-
/** `{ joined column: true }`: the projection or grouping that keeps a parent's key on the rows read. */
|
|
36
|
-
export declare function joinedColumns(joins: readonly ParentJoin[]): Record<string, true>;
|
|
37
|
-
/**
|
|
38
|
-
* One side's columns: `'parent'` for the columns a parent is keyed by, `'joined'` for the ones a
|
|
39
|
-
* child or tally row carries that key in. Matching the two halves means agreeing on every column, so
|
|
40
|
-
* each side is read through `joins` rather than through the parent's own key list - the same set only
|
|
41
|
-
* for a to-many, and silently a different one otherwise. `keyof ParentJoin` is what keeps the two
|
|
42
|
-
* sides from being spelled apart.
|
|
43
|
-
*
|
|
44
|
-
* Lifted out of `joins` once per relation, not once per row: {@link rowKey} takes the list and reads
|
|
45
|
-
* each row itself, so a page of children costs one key each and nothing else.
|
|
46
|
-
*/
|
|
47
|
-
export declare function keyColumns(joins: readonly ParentJoin[], side: keyof ParentJoin): string[];
|
|
48
|
-
/**
|
|
49
|
-
* `{ joined column: every parent's value for it }`, the filter that fetches a whole page of parents'
|
|
50
|
-
* children in one statement.
|
|
51
|
-
*
|
|
52
|
-
* A composite over-selects, because the lists are independent and a pairing no parent has can still
|
|
53
|
-
* match. Regrouping the rows keys on every column, so those rows find no parent and are dropped -
|
|
54
|
-
* cheaper than the row-value comparison no engine spells the same way.
|
|
55
|
-
*/
|
|
56
|
-
export declare function parentsIn(joins: readonly ParentJoin[], parents: readonly unknown[]): Record<string, unknown[]>;
|
|
57
|
-
/**
|
|
58
|
-
* The parents a bounded to-many read fans out over: the rows themselves, the columns matching them to
|
|
59
|
-
* their children.
|
|
60
|
-
*/
|
|
61
|
-
export type ParentPartition = {
|
|
62
|
-
readonly joins: readonly ParentJoin[];
|
|
63
|
-
readonly parents: readonly unknown[];
|
|
64
|
-
/** The parent's own fields: a `LATERAL` row source has to spell its key column's type. */
|
|
65
|
-
readonly parentFields: Readonly<Record<string, FieldMeta | undefined>>;
|
|
66
|
-
};
|
|
67
|
-
/**
|
|
68
|
-
* Whether a to-many's own query asks for a share *per parent* rather than a slice of the whole page.
|
|
69
|
-
* Only `$limit`/`$skip` do: without one, a single flat statement over an `IN (...)` list is both
|
|
70
|
-
* correct and cheaper.
|
|
71
|
-
*/
|
|
72
|
-
export declare function isBoundedPerParent(query: Pick<RelationQuery, '$limit' | '$skip'>): boolean;
|
|
73
|
-
/**
|
|
74
|
-
* `query` narrowed to one parent's children: what a single branch of a bounded per-parent read asks
|
|
75
|
-
* for. Shared by the backends so how the parent's filter merges into the relation's own is decided
|
|
76
|
-
* once - both spelled it out, and a rule that ever needs more than a spread would have to change twice.
|
|
77
|
-
*/
|
|
78
|
-
export declare function queryChildrenOf<E>(query: Query<E>, joins: readonly ParentJoin[], parent: unknown): Query<E>;
|
|
79
|
-
/**
|
|
80
|
-
* `query` narrowed to the children of a whole page of parents, which is the flat read a relation with
|
|
81
|
-
* no share of its own takes. Over-selects on a composite key exactly as {@link parentsIn} does.
|
|
82
|
-
*/
|
|
83
|
-
export declare function queryChildrenOfAll<E>(query: Query<E>, joins: readonly ParentJoin[], parents: readonly unknown[]): Query<E>;
|
|
84
|
-
/**
|
|
85
|
-
* `query` with `filter` merged into its own `$where`: the one rule for narrowing a relation's query to
|
|
86
|
-
* the parents it is being read for, whether the filter names their keys as values or, for a correlated
|
|
87
|
-
* shape, as a reference to a row source.
|
|
88
|
-
*/
|
|
89
|
-
export declare function queryNarrowedTo<E>(query: Query<E>, filter: Record<string, unknown>): Query<E>;
|
|
90
35
|
/**
|
|
91
36
|
* The `$where` naming exactly the children of the rows `parentIds` identifies: an `IN` over the one
|
|
92
|
-
* column a single key contributes, an OR of key maps for several
|
|
93
|
-
*
|
|
94
|
-
* Exact, unlike {@link parentsIn}: a read absorbs over-selection by regrouping its rows, and a write
|
|
95
|
-
* has nothing to regroup - a pairing no parent has would delete a child of a parent that survives.
|
|
37
|
+
* column a single key contributes, an OR of whole key maps for several - lists of each column apart
|
|
38
|
+
* would pair values no parent has, and a delete would take a child of a parent that survives.
|
|
96
39
|
*/
|
|
97
40
|
export declare function childrenOf(joins: readonly ParentJoin[], parentIds: readonly unknown[]): Record<string, unknown>;
|
|
98
41
|
/**
|
|
@@ -107,11 +50,15 @@ export type JoinedRelationRejectedKey = (typeof JOINED_RELATION_REJECTIONS)[numb
|
|
|
107
50
|
export declare function getRelationRequestSummary<E>(meta: EntityMeta<E>, populate?: QueryPopulate<E>): RelationRequestSummary<E>;
|
|
108
51
|
/** True when `$populate` includes at least one relation key. */
|
|
109
52
|
export declare function populatesRelations<E>(meta: EntityMeta<E>, populate?: QueryPopulate<E>): boolean;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Each relation a `$count` tallies, and the filter narrowing what it counts: the target's, whose type
|
|
55
|
+
* only the metadata knows this far down, as for a relation filter reaching the same subquery.
|
|
56
|
+
*/
|
|
57
|
+
export declare function countedRelations<E>(meta: EntityMeta<E>, counts: QueryCount<E> | undefined): {
|
|
58
|
+
readonly relKey: RelationKey<E>;
|
|
59
|
+
readonly relation: RelationMeta;
|
|
60
|
+
readonly where: QueryWhere<unknown>;
|
|
61
|
+
}[];
|
|
115
62
|
export type ParsedRelationQuery<E extends object = object> = {
|
|
116
63
|
query: RelationQuery<E>;
|
|
117
64
|
required: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { QUERY_BOOLEAN_CLAUSES, QUERY_NUMBER_CLAUSES, QUERY_OBJECT_CLAUSES,
|
|
2
|
-
import { getKeys, someKey } from './object.util.js';
|
|
1
|
+
import { QUERY_BOOLEAN_CLAUSES, QUERY_NUMBER_CLAUSES, QUERY_OBJECT_CLAUSES, QUERY_STATEMENT_CLAUSES, } from '../type/query.js';
|
|
2
|
+
import { getKeys, isRecord, someKey } from './object.util.js';
|
|
3
3
|
/** What a query populating nothing requests, shared: most reads populate nothing, and ask on every one. */
|
|
4
4
|
const NOTHING_REQUESTED = Object.freeze({
|
|
5
5
|
requestedKeys: Object.freeze([]),
|
|
@@ -40,86 +40,19 @@ export function parentJoins(relOpts, parentKeyCount) {
|
|
|
40
40
|
export function targetKeyColumns(relOpts, parentKeyCount) {
|
|
41
41
|
return relOpts.references.slice(parentKeyCount).map(({ local }) => local);
|
|
42
42
|
}
|
|
43
|
-
/** `{ joined column: true }`: the projection or grouping that keeps a parent's key on the rows read. */
|
|
44
|
-
export function joinedColumns(joins) {
|
|
45
|
-
return Object.fromEntries(keyColumns(joins, 'joined').map((column) => [column, true]));
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* One side's columns: `'parent'` for the columns a parent is keyed by, `'joined'` for the ones a
|
|
49
|
-
* child or tally row carries that key in. Matching the two halves means agreeing on every column, so
|
|
50
|
-
* each side is read through `joins` rather than through the parent's own key list - the same set only
|
|
51
|
-
* for a to-many, and silently a different one otherwise. `keyof ParentJoin` is what keeps the two
|
|
52
|
-
* sides from being spelled apart.
|
|
53
|
-
*
|
|
54
|
-
* Lifted out of `joins` once per relation, not once per row: {@link rowKey} takes the list and reads
|
|
55
|
-
* each row itself, so a page of children costs one key each and nothing else.
|
|
56
|
-
*/
|
|
57
|
-
export function keyColumns(joins, side) {
|
|
58
|
-
return joins.map((join) => join[side]);
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* `{ joined column: every parent's value for it }`, the filter that fetches a whole page of parents'
|
|
62
|
-
* children in one statement.
|
|
63
|
-
*
|
|
64
|
-
* A composite over-selects, because the lists are independent and a pairing no parent has can still
|
|
65
|
-
* match. Regrouping the rows keys on every column, so those rows find no parent and are dropped -
|
|
66
|
-
* cheaper than the row-value comparison no engine spells the same way.
|
|
67
|
-
*/
|
|
68
|
-
export function parentsIn(joins, parents) {
|
|
69
|
-
return Object.fromEntries(joins.map(({ parent, joined }) => [joined, parents.map((it) => read(it, parent))]));
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Whether a to-many's own query asks for a share *per parent* rather than a slice of the whole page.
|
|
73
|
-
* Only `$limit`/`$skip` do: without one, a single flat statement over an `IN (...)` list is both
|
|
74
|
-
* correct and cheaper.
|
|
75
|
-
*/
|
|
76
|
-
export function isBoundedPerParent(query) {
|
|
77
|
-
return query.$limit !== undefined || query.$skip !== undefined;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* The `$where` naming exactly one parent's children: every joined column equal to that parent's value.
|
|
81
|
-
* What a per-parent bounded read filters each of its branches by, and the composite half of
|
|
82
|
-
* {@link childrenOf}.
|
|
83
|
-
*/
|
|
84
|
-
function childOf(joins, parent) {
|
|
85
|
-
return Object.fromEntries(joins.map(({ parent: key, joined }) => [joined, read(parent, key)]));
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* `query` narrowed to one parent's children: what a single branch of a bounded per-parent read asks
|
|
89
|
-
* for. Shared by the backends so how the parent's filter merges into the relation's own is decided
|
|
90
|
-
* once - both spelled it out, and a rule that ever needs more than a spread would have to change twice.
|
|
91
|
-
*/
|
|
92
|
-
export function queryChildrenOf(query, joins, parent) {
|
|
93
|
-
return queryNarrowedTo(query, childOf(joins, parent));
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* `query` narrowed to the children of a whole page of parents, which is the flat read a relation with
|
|
97
|
-
* no share of its own takes. Over-selects on a composite key exactly as {@link parentsIn} does.
|
|
98
|
-
*/
|
|
99
|
-
export function queryChildrenOfAll(query, joins, parents) {
|
|
100
|
-
return queryNarrowedTo(query, parentsIn(joins, parents));
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* `query` with `filter` merged into its own `$where`: the one rule for narrowing a relation's query to
|
|
104
|
-
* the parents it is being read for, whether the filter names their keys as values or, for a correlated
|
|
105
|
-
* shape, as a reference to a row source.
|
|
106
|
-
*/
|
|
107
|
-
export function queryNarrowedTo(query, filter) {
|
|
108
|
-
return { ...query, $where: { ...query.$where, ...filter } };
|
|
109
|
-
}
|
|
110
43
|
/**
|
|
111
44
|
* The `$where` naming exactly the children of the rows `parentIds` identifies: an `IN` over the one
|
|
112
|
-
* column a single key contributes, an OR of key maps for several
|
|
113
|
-
*
|
|
114
|
-
* Exact, unlike {@link parentsIn}: a read absorbs over-selection by regrouping its rows, and a write
|
|
115
|
-
* has nothing to regroup - a pairing no parent has would delete a child of a parent that survives.
|
|
45
|
+
* column a single key contributes, an OR of whole key maps for several - lists of each column apart
|
|
46
|
+
* would pair values no parent has, and a delete would take a child of a parent that survives.
|
|
116
47
|
*/
|
|
117
48
|
export function childrenOf(joins, parentIds) {
|
|
118
49
|
const [first] = joins;
|
|
119
50
|
if (joins.length === 1) {
|
|
120
51
|
return { [first.joined]: parentIds };
|
|
121
52
|
}
|
|
122
|
-
return {
|
|
53
|
+
return {
|
|
54
|
+
$or: parentIds.map((id) => Object.fromEntries(joins.map(({ parent, joined }) => [joined, read(id, parent)]))),
|
|
55
|
+
};
|
|
123
56
|
}
|
|
124
57
|
function read(row, key) {
|
|
125
58
|
return row[key];
|
|
@@ -178,8 +111,24 @@ export function populatesRelations(meta, populate) {
|
|
|
178
111
|
return false;
|
|
179
112
|
return someKey(populate, (key) => !!populate[key] && key in meta.relations);
|
|
180
113
|
}
|
|
181
|
-
/**
|
|
182
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Each relation a `$count` tallies, and the filter narrowing what it counts: the target's, whose type
|
|
116
|
+
* only the metadata knows this far down, as for a relation filter reaching the same subquery.
|
|
117
|
+
*/
|
|
118
|
+
export function countedRelations(meta, counts) {
|
|
119
|
+
if (!counts) {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
return getKeys(counts).flatMap((relKey) => {
|
|
123
|
+
const count = counts[relKey];
|
|
124
|
+
const relation = meta.relations[relKey];
|
|
125
|
+
if (!count || !relation) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
const where = typeof count === 'object' ? count.$where : undefined;
|
|
129
|
+
return [{ relKey, relation, where: where ?? {} }];
|
|
130
|
+
});
|
|
131
|
+
}
|
|
183
132
|
// Taken from the clause groups declared beside `Query` itself, so a renamed clause fails to compile
|
|
184
133
|
// here instead of quietly narrowing what a relation query accepts. `$required` is the one key that
|
|
185
134
|
// is not a `Query` clause at all - it says how the relation joins, not what it selects.
|
|
@@ -198,7 +147,7 @@ export function parseRelationQueryValue(value) {
|
|
|
198
147
|
// Caught before the shape check so the message names the key, rather than reporting the whole
|
|
199
148
|
// object as an unrecognized relation query value.
|
|
200
149
|
if (isRecord(value)) {
|
|
201
|
-
const statementOnly =
|
|
150
|
+
const statementOnly = QUERY_STATEMENT_CLAUSES.find((clause) => clause in value);
|
|
202
151
|
if (statementOnly) {
|
|
203
152
|
throw new TypeError(`'${statementOnly}' applies to the whole statement, not to a populated relation. Move it to the top level of the query.`);
|
|
204
153
|
}
|
|
@@ -227,9 +176,6 @@ export function forEachRequestedRelation(meta, populate, fn) {
|
|
|
227
176
|
fn(relKey, populate?.[relKey]);
|
|
228
177
|
}
|
|
229
178
|
}
|
|
230
|
-
function isRecord(value) {
|
|
231
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
232
|
-
}
|
|
233
179
|
function isBooleanLikeValue(value) {
|
|
234
180
|
return value === true || value === false || value === 0 || value === 1;
|
|
235
181
|
}
|
|
@@ -243,7 +189,7 @@ function isValidRelationQueryShape(query) {
|
|
|
243
189
|
if (RELATION_QUERY_BOOLEAN_KEYS.has(key) && !isBooleanLikeValue(value)) {
|
|
244
190
|
return false;
|
|
245
191
|
}
|
|
246
|
-
if (RELATION_QUERY_OBJECT_KEYS.has(key) && !isRecord(value)) {
|
|
192
|
+
if (RELATION_QUERY_OBJECT_KEYS.has(key) && !isRecord(value) && !(key === '$select' && Array.isArray(value))) {
|
|
247
193
|
return false;
|
|
248
194
|
}
|
|
249
195
|
if (RELATION_QUERY_NUMBER_KEYS.has(key) && (typeof value !== 'number' || !Number.isFinite(value))) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A row's key as a string, for matching rows to each other
|
|
2
|
+
* A row's key as a string, for matching rows to each other.
|
|
3
3
|
*
|
|
4
4
|
* Reads the columns off the row rather than taking their values, because every caller matches a
|
|
5
5
|
* whole page of rows against one fixed column list: taking an array would make each of them build
|
|
@@ -11,13 +11,3 @@
|
|
|
11
11
|
* key are not treated as one row.
|
|
12
12
|
*/
|
|
13
13
|
export declare function rowKey(row: unknown, columns: readonly string[]): string;
|
|
14
|
-
/**
|
|
15
|
-
* A lookup keyed by data rather than by a name this code chose, so a key that spells `__proto__` or
|
|
16
|
-
* `constructor` is an ordinary entry instead of the prototype: on `{}` those threw when a bucket was
|
|
17
|
-
* pushed to, and a `_count` tally under one silently read back as an object. Cheaper than a `Map`
|
|
18
|
-
* here, and faster than `{}`, which walks the prototype chain on every miss.
|
|
19
|
-
*
|
|
20
|
-
* It carries none of `Object.prototype`, which no type can say: index it and spread it, but calling
|
|
21
|
-
* `hasOwnProperty` on one type-checks and throws.
|
|
22
|
-
*/
|
|
23
|
-
export declare function dataKeyed<V>(): Record<string, V>;
|
package/dist/util/rowKey.util.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Separates the parts of a composite key: a unit separator, which no column value carries. */
|
|
2
2
|
const KEY_SEPARATOR = '\u001f';
|
|
3
3
|
/**
|
|
4
|
-
* A row's key as a string, for matching rows to each other
|
|
4
|
+
* A row's key as a string, for matching rows to each other.
|
|
5
5
|
*
|
|
6
6
|
* Reads the columns off the row rather than taking their values, because every caller matches a
|
|
7
7
|
* whole page of rows against one fixed column list: taking an array would make each of them build
|
|
@@ -23,18 +23,6 @@ export function rowKey(row, columns) {
|
|
|
23
23
|
}
|
|
24
24
|
return key;
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
27
|
-
* A lookup keyed by data rather than by a name this code chose, so a key that spells `__proto__` or
|
|
28
|
-
* `constructor` is an ordinary entry instead of the prototype: on `{}` those threw when a bucket was
|
|
29
|
-
* pushed to, and a `_count` tally under one silently read back as an object. Cheaper than a `Map`
|
|
30
|
-
* here, and faster than `{}`, which walks the prototype chain on every miss.
|
|
31
|
-
*
|
|
32
|
-
* It carries none of `Object.prototype`, which no type can say: index it and spread it, but calling
|
|
33
|
-
* `hasOwnProperty` on one type-checks and throws.
|
|
34
|
-
*/
|
|
35
|
-
export function dataKeyed() {
|
|
36
|
-
return Object.create(null);
|
|
37
|
-
}
|
|
38
26
|
function keyPart(value) {
|
|
39
27
|
if (value instanceof Date) {
|
|
40
28
|
return value.toISOString();
|