uql-orm 0.69.0 → 0.71.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 +2 -0
- package/dist/browser/querier/httpQuerier.d.ts +7 -7
- package/dist/browser/type/clientQuerier.d.ts +5 -5
- 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 +6 -0
- package/dist/cockroachdb/cockroachDialect.js +10 -2
- package/dist/d1/d1SqliteDialect.d.ts +3 -0
- package/dist/d1/d1SqliteDialect.js +3 -1
- package/dist/dialect/abstractDialect.d.ts +8 -2
- package/dist/dialect/abstractDialect.js +17 -1
- package/dist/dialect/abstractSqlDialect.d.ts +21 -5
- package/dist/dialect/abstractSqlDialect.js +91 -46
- package/dist/dialect/aliases.d.ts +10 -7
- package/dist/dialect/aliases.js +10 -7
- package/dist/dialect/hydrateColumn.d.ts +3 -2
- package/dist/dialect/hydrateColumn.js +10 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +5 -3
- package/dist/dialect/pgLikeSqlDialect.d.ts +17 -5
- package/dist/dialect/pgLikeSqlDialect.js +34 -15
- package/dist/dialect/queryJoins.d.ts +19 -1
- package/dist/dialect/queryJoins.js +54 -11
- package/dist/dialect/vectorCast.d.ts +2 -0
- package/dist/dialect/vectorCast.js +7 -0
- package/dist/dialect/vectorSqlDialect.d.ts +7 -3
- package/dist/dialect/vectorSqlDialect.js +15 -10
- package/dist/entity/decorator/members.d.ts +25 -11
- package/dist/entity/metadata/definition.d.ts +8 -3
- package/dist/libsql/libsqlDialect.d.ts +1 -1
- package/dist/libsql/libsqlDialect.js +3 -3
- package/dist/maria/mariaDialect.d.ts +3 -9
- package/dist/maria/mariaDialect.js +4 -13
- package/dist/maria/mariadbQuerier.js +9 -3
- package/dist/migrate/codegen/entityCodeGenerator.js +4 -2
- package/dist/migrate/ddl/index.d.ts +1 -0
- package/dist/migrate/ddl/index.js +4 -2
- package/dist/migrate/ddl/indexDdl.d.ts +2 -0
- package/dist/migrate/ddl/indexDdl.js +10 -2
- package/dist/migrate/ddl/pgIndexDdl.d.ts +3 -4
- package/dist/migrate/ddl/pgIndexDdl.js +11 -11
- package/dist/migrate/ddl/sqliteIndexDdl.d.ts +11 -0
- package/dist/migrate/ddl/sqliteIndexDdl.js +38 -0
- package/dist/migrate/generator/mongoCommand.d.ts +28 -0
- package/dist/migrate/generator/mongoCommand.js +8 -0
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +2 -0
- package/dist/migrate/generator/mongoSchemaGenerator.js +50 -6
- package/dist/migrate/introspection/mongoIntrospector.js +33 -7
- package/dist/migrate/introspection/postgresIntrospector.js +19 -0
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -0
- package/dist/migrate/introspection/sqliteIntrospector.js +29 -3
- package/dist/migrate/migrator.d.ts +2 -1
- package/dist/migrate/migrator.js +5 -3
- package/dist/mongo/mongoDialect.d.ts +33 -27
- package/dist/mongo/mongoDialect.js +178 -114
- package/dist/mongo/mongodbQuerier.d.ts +0 -2
- package/dist/mongo/mongodbQuerier.js +14 -13
- package/dist/mssql/mssqlDialect.js +4 -2
- package/dist/postgres/postgresDialect.js +2 -2
- package/dist/querier/abstractQuerier.d.ts +16 -11
- package/dist/querier/abstractQuerier.js +28 -8
- package/dist/querier/abstractQuerierPool.d.ts +9 -9
- package/dist/querier/abstractSqlQuerier.d.ts +1 -1
- package/dist/querier/abstractSqlQuerier.js +3 -3
- package/dist/schema/canonicalType.js +10 -4
- package/dist/schema/indexDifferences.d.ts +5 -2
- package/dist/schema/indexDifferences.js +5 -0
- package/dist/schema/schemaASTBuilder.js +3 -2
- package/dist/sqlite/localSqliteQuerierPool.d.ts +10 -11
- package/dist/sqlite/localSqliteQuerierPool.js +8 -11
- package/dist/sqlite/nodeSqliteQuerierPool.d.ts +4 -2
- package/dist/sqlite/nodeSqliteQuerierPool.js +7 -4
- package/dist/sqlite/sqliteDialect.d.ts +9 -1
- package/dist/sqlite/sqliteDialect.js +43 -3
- package/dist/sqlite/sqliteQuerierPool.d.ts +6 -5
- package/dist/sqlite/sqliteQuerierPool.js +10 -7
- package/dist/turso/tursoDialect.d.ts +1 -1
- package/dist/turso/tursoDialect.js +6 -2
- package/dist/turso/tursoLocalDialect.d.ts +1 -1
- package/dist/turso/tursoLocalDialect.js +1 -1
- package/dist/turso/tursoLocalQuerierPool.d.ts +4 -5
- package/dist/turso/tursoLocalQuerierPool.js +3 -10
- package/dist/type/dialect.d.ts +11 -0
- package/dist/type/entity.d.ts +45 -5
- package/dist/type/migration.d.ts +14 -9
- package/dist/type/query.d.ts +6 -0
- package/dist/type/queryAggregate.d.ts +73 -42
- package/dist/type/queryAggregate.js +4 -21
- package/dist/type/universalQuerier.d.ts +9 -9
- package/dist/type/vector.d.ts +17 -0
- package/dist/type/vector.js +6 -0
- package/dist/util/ddlExpression.util.d.ts +2 -0
- package/dist/util/ddlExpression.util.js +5 -0
- package/dist/util/dialect.util.d.ts +17 -3
- package/dist/util/dialect.util.js +40 -6
- package/package.json +5 -3
- package/skills/uql-orm/SKILL.md +142 -0
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { dialectOptionsFrom } from '../dialect/abstractDialect.js';
|
|
2
|
-
import {
|
|
3
|
-
import { applySqlitePragmas } from '../sqlite/sqlitePragmas.js';
|
|
4
|
-
import { SqliteQuerier } from '../sqlite/sqliteQuerier.js';
|
|
2
|
+
import { AbstractLocalSqliteQuerierPool } from '../sqlite/localSqliteQuerierPool.js';
|
|
5
3
|
import { TursoLocalDialect } from './tursoLocalDialect.js';
|
|
6
4
|
/** A pool for the embedded Turso engine, on `uql-orm/turso/local` so its native binaries stay out of edge bundles. */
|
|
7
|
-
export class TursoLocalQuerierPool extends
|
|
5
|
+
export class TursoLocalQuerierPool extends AbstractLocalSqliteQuerierPool {
|
|
8
6
|
filename;
|
|
9
7
|
opts;
|
|
10
8
|
constructor(filename = ':memory:', opts, extra) {
|
|
@@ -12,15 +10,10 @@ export class TursoLocalQuerierPool extends AbstractSharedHandleQuerierPool {
|
|
|
12
10
|
this.filename = filename;
|
|
13
11
|
this.opts = opts;
|
|
14
12
|
}
|
|
15
|
-
async
|
|
13
|
+
async createDb() {
|
|
16
14
|
const { connect } = await import('@tursodatabase/database');
|
|
17
15
|
const db = await connect(this.filename, this.opts);
|
|
18
|
-
// Integers as `bigint`, which the querier decodes exactly past 2^53.
|
|
19
16
|
db.defaultSafeIntegers(true);
|
|
20
|
-
await applySqlitePragmas(db);
|
|
21
17
|
return db;
|
|
22
18
|
}
|
|
23
|
-
buildQuerier(db) {
|
|
24
|
-
return new SqliteQuerier(db, this.dialect, this.extra);
|
|
25
|
-
}
|
|
26
19
|
}
|
package/dist/type/dialect.d.ts
CHANGED
|
@@ -107,6 +107,11 @@ export interface DialectFeatures {
|
|
|
107
107
|
readonly vectorIndexRequiresNotNull: boolean;
|
|
108
108
|
/** Whether the dialect requires/allows (n) length constraints on vector types. */
|
|
109
109
|
readonly vectorSupportsLength: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Whether a vector binds as its packed little-endian float32 bytes, which a blob column holds and the
|
|
112
|
+
* SQLite family and MariaDB read, rather than as `[1,2,3]` text they would reparse per row.
|
|
113
|
+
*/
|
|
114
|
+
readonly vectorBytes: boolean;
|
|
110
115
|
/** Whether the dialect natively supports the TIMESTAMPTZ alias/type. */
|
|
111
116
|
readonly supportsTimestamptz: boolean;
|
|
112
117
|
/**
|
|
@@ -122,6 +127,12 @@ export interface DialectFeatures {
|
|
|
122
127
|
* answer, not the driver's: node-`pg` streams on its own and keeps doing so.
|
|
123
128
|
*/
|
|
124
129
|
readonly serverSideCursors: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Whether an `UPDATE` or `DELETE` can read a relation in its filter. False on MongoDB, whose filter
|
|
132
|
+
* hosts no lookup, and on Turso's engine, which cannot resolve the written table inside a subquery:
|
|
133
|
+
* such a write reads the ids of the rows it names first.
|
|
134
|
+
*/
|
|
135
|
+
readonly correlatedWrites: boolean;
|
|
125
136
|
}
|
|
126
137
|
/** What a SQL engine can do beyond {@link DialectFeatures}, read where a statement is built. */
|
|
127
138
|
export interface SqlDialectFeatures extends DialectFeatures {
|
package/dist/type/entity.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { EnumValues, ForeignKeyAction, IndexType } from '../schema/types.js
|
|
|
2
2
|
import type { FilterOptions, RelationQuery } from './query.js';
|
|
3
3
|
import type { ColumnRef, QueryRaw, RelationAggregate } from './queryRaw.js';
|
|
4
4
|
import type { QueryWhere } from './queryWhere.js';
|
|
5
|
-
import type { Except, IsMany, Json, Scalar, Type, Unpacked } from './utility.js';
|
|
5
|
+
import type { Except, IsMany, Json, Scalar, Type, Unpacked, Writable } from './utility.js';
|
|
6
6
|
import type { VectorDistance, VectorIndexOptions, VectorIndexType } from './vector.js';
|
|
7
7
|
/** Brands the property an entity is identified by, where it is not `id`, `_id` or `uuid`. */
|
|
8
8
|
export declare const idKey: unique symbol;
|
|
@@ -20,6 +20,24 @@ export type Key<E> = keyof E & string;
|
|
|
20
20
|
export type FieldKey<E> = {
|
|
21
21
|
readonly [K in keyof E]-?: [NonNullable<E[K]>] extends [Scalar | readonly Scalar[] | Json | readonly Json[]] ? K : never;
|
|
22
22
|
}[Key<E>];
|
|
23
|
+
/**
|
|
24
|
+
* Whether `A` and `B` are the same type, `readonly` included - which no conditional sees, since
|
|
25
|
+
* assignability ignores the modifier. Two identical generic signatures compare equal only when their
|
|
26
|
+
* deferred bodies do.
|
|
27
|
+
*/
|
|
28
|
+
type IfEquals<A, B, Yes, No> = (<T>() => T extends A ? 1 : 2) extends <T>() => (T extends B ? 1 : 2) ? Yes : No;
|
|
29
|
+
/**
|
|
30
|
+
* The fields a caller writes: every one the class does not declare `readonly`. A field the database
|
|
31
|
+
* writes - a relation aggregate, a stored generated column, a trigger-kept stamp - is `readonly`, and
|
|
32
|
+
* its value never reaches the database, so a write payload leaves it out rather than dropping it.
|
|
33
|
+
*/
|
|
34
|
+
export type WritableKey<E> = {
|
|
35
|
+
readonly [K in FieldKey<E>]-?: IfEquals<Pick<E, K>, Writable<Pick<E, K>>, K, never>;
|
|
36
|
+
}[FieldKey<E>];
|
|
37
|
+
/** A whole-record write as a caller supplies one: {@link EntityData} without the fields it cannot write. */
|
|
38
|
+
export type EntityWrite<E> = EntityData<E, WritableKey<E>>;
|
|
39
|
+
/** A partial write as a caller supplies one: {@link UpdatePayload} without them. */
|
|
40
|
+
export type UpdateWrite<E, Raw = QueryRaw> = UpdatePayload<E, Raw, WritableKey<E>>;
|
|
23
41
|
/** The relation names of an entity: every key but its fields and its methods, so the two sets cannot drift. */
|
|
24
42
|
export type RelationKey<E> = Exclude<Key<E>, FieldKey<E> | MethodKey<E>>;
|
|
25
43
|
/**
|
|
@@ -267,10 +285,21 @@ export type TsTypeOf<T> = T extends StringConstructor ? string : T extends Numbe
|
|
|
267
285
|
*/
|
|
268
286
|
export type FieldOptionsFor<V, E = unknown> = (FieldOptions<NonNullable<V>, E> & {
|
|
269
287
|
readonly type: TypeFor<V>;
|
|
288
|
+
readonly isId: true;
|
|
270
289
|
}) | (FieldOptions<NonNullable<V>, E> & {
|
|
290
|
+
readonly type: TypeFor<V>;
|
|
291
|
+
} & DeclaresNotNull<V>) | (FieldOptions<NonNullable<V>, E> & {
|
|
271
292
|
readonly references: EntityGetter;
|
|
272
293
|
readonly type?: TypeFor<V>;
|
|
273
|
-
}) | AggregateOptionsFor<V, E>;
|
|
294
|
+
} & DeclaresNotNull<V>) | AggregateOptionsFor<V, E>;
|
|
295
|
+
/**
|
|
296
|
+
* A column holds `null` unless `nullable: false` says otherwise, and a read hydrates one, so a property
|
|
297
|
+
* that does not admit it says so here. The decorators state the same rule the other way round, against
|
|
298
|
+
* the property they are applied to; a key needs neither, being NOT NULL on every engine.
|
|
299
|
+
*/
|
|
300
|
+
type DeclaresNotNull<V> = null extends V ? unknown : {
|
|
301
|
+
readonly nullable: false;
|
|
302
|
+
};
|
|
274
303
|
/**
|
|
275
304
|
* A field a relation aggregate computes: the aggregate types it, so it declares no `type`, and only the
|
|
276
305
|
* two a row change turns into a delta - `count` and `sum` - may be `stored`.
|
|
@@ -475,15 +504,26 @@ export type HookRegistration = {
|
|
|
475
504
|
readonly methodName: string;
|
|
476
505
|
};
|
|
477
506
|
/**
|
|
478
|
-
* An index type with
|
|
479
|
-
*
|
|
507
|
+
* An index type with what it needs: a vector index has to name its metric, since engines default to a
|
|
508
|
+
* different one than the queries use; an Atlas `vectorSearch` index may, else takes its field's, else
|
|
509
|
+
* cosine; a `fulltext` one may name the text-search `config` it parses with; and any other index neither.
|
|
480
510
|
*/
|
|
481
511
|
export type IndexTypeOptions = {
|
|
482
512
|
type: VectorIndexType;
|
|
483
513
|
distance: VectorDistance;
|
|
514
|
+
config?: never;
|
|
515
|
+
} | {
|
|
516
|
+
type: 'vectorSearch';
|
|
517
|
+
distance?: VectorDistance;
|
|
518
|
+
config?: never;
|
|
519
|
+
} | {
|
|
520
|
+
type: 'fulltext';
|
|
521
|
+
distance?: never;
|
|
522
|
+
config?: string;
|
|
484
523
|
} | {
|
|
485
|
-
type?: Exclude<IndexType, VectorIndexType>;
|
|
524
|
+
type?: Exclude<IndexType, VectorIndexType | 'vectorSearch' | 'fulltext'>;
|
|
486
525
|
distance?: never;
|
|
526
|
+
config?: never;
|
|
487
527
|
};
|
|
488
528
|
/** One index entry as the migration builder takes it: a column name, `raw`, or an object when it needs more. */
|
|
489
529
|
export type IndexColumnInput = string | QueryRaw | EntityIndexColumn;
|
package/dist/type/migration.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import type { VectorCast } from '../dialect/vectorCast.js';
|
|
2
1
|
import type { AnyMigrationOperation } from '../migrate/builder/types.js';
|
|
3
2
|
import type { IndexFacet } from '../schema/indexDifferences.js';
|
|
4
3
|
import type { SchemaAST } from '../schema/schemaAST.js';
|
|
5
4
|
import type { ColumnNode, ForeignKeyAction, IndexType, TableNode } from '../schema/types.js';
|
|
6
|
-
import type { EntityMeta, EntityWhereMeta, FieldOptions, IndexColumnSchema, LoggingOptions, Querier, SqlQuerier, Type, VectorIndexOptions } from './index.js';
|
|
5
|
+
import type { EntityMeta, EntityWhereMeta, FieldOptions, IndexColumnSchema, IndexedVectorField, LoggingOptions, Querier, SqlQuerier, Type, VectorIndexOptions } from './index.js';
|
|
7
6
|
/**
|
|
8
7
|
* Defines a migration using a simple object literal. `Q` is `MongoQuerier` for a MongoDB migration.
|
|
9
8
|
*/
|
|
10
9
|
export interface MigrationDefinition<Q extends Querier = SqlQuerier> {
|
|
11
10
|
readonly name?: string;
|
|
11
|
+
/**
|
|
12
|
+
* `false` runs this migration outside a transaction, for a statement an engine refuses inside one -
|
|
13
|
+
* `CREATE INDEX CONCURRENTLY` on Postgres, the index a busy table needs. The cost is the rollback: a
|
|
14
|
+
* failure part-way leaves the statements before it applied and the migration unlogged. MongoDB
|
|
15
|
+
* creates collections outside any transaction already, so it changes nothing there.
|
|
16
|
+
*/
|
|
17
|
+
readonly transaction?: boolean;
|
|
12
18
|
up(querier: Q): Promise<void>;
|
|
13
19
|
down(querier: Q): Promise<void>;
|
|
14
20
|
}
|
|
@@ -126,7 +132,12 @@ export interface TableSchema {
|
|
|
126
132
|
/**
|
|
127
133
|
* Represents an index in a database table
|
|
128
134
|
*/
|
|
129
|
-
export interface IndexSchema extends VectorIndexOptions {
|
|
135
|
+
export interface IndexSchema extends VectorIndexOptions, IndexedVectorField {
|
|
136
|
+
/**
|
|
137
|
+
* A `fulltext` index's text-search configuration, which `$text` parses with on the Postgres family
|
|
138
|
+
* (`'english'`); `'simple'` where unstated. Other engines take their language from elsewhere.
|
|
139
|
+
*/
|
|
140
|
+
readonly config?: string;
|
|
130
141
|
readonly name: string;
|
|
131
142
|
/**
|
|
132
143
|
* What the index is over, in order. Named `entries` and not `columns` because an entry need not be
|
|
@@ -142,12 +153,6 @@ export interface IndexSchema extends VectorIndexOptions {
|
|
|
142
153
|
readonly where?: string;
|
|
143
154
|
/** Non-key columns stored in the index (Postgres-wire `INCLUDE`). */
|
|
144
155
|
readonly include?: readonly string[];
|
|
145
|
-
/**
|
|
146
|
-
* The indexed column's vector type, which pgvector's operator-class names are built from
|
|
147
|
-
* (`halfvec_cosine_ops`). Absent for a non-vector index, and for an index whose column types are
|
|
148
|
-
* unknown, where `vector` is assumed.
|
|
149
|
-
*/
|
|
150
|
-
readonly vectorType?: VectorCast;
|
|
151
156
|
}
|
|
152
157
|
/**
|
|
153
158
|
* A foreign key constraint, wherever one is described: read back by introspection, planned into a
|
package/dist/type/query.d.ts
CHANGED
|
@@ -16,6 +16,12 @@ export type QueryOptions = {
|
|
|
16
16
|
* already-deleted rows are removed too. No effect on entities without a soft-delete field.
|
|
17
17
|
*/
|
|
18
18
|
hardDelete?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* `updateMany`/`deleteMany` only: address every row of the table on purpose. Without it a bulk write
|
|
21
|
+
* that names none - no `$where` and no `$limit` - is refused, since a forgotten filter and the whole
|
|
22
|
+
* table look alike. The entity's own filters never count as naming one.
|
|
23
|
+
*/
|
|
24
|
+
unfiltered?: boolean;
|
|
19
25
|
/**
|
|
20
26
|
* prefix the query with this.
|
|
21
27
|
*/
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { FieldKey } from './entity.js';
|
|
1
|
+
import type { FieldKey, RelationKey, RelationTarget } from './entity.js';
|
|
2
2
|
import type { QueryPager, QuerySelect, QuerySortDirection } from './query.js';
|
|
3
|
+
import type { QueryRaw } from './queryRaw.js';
|
|
3
4
|
import type { QueryWhere, QueryWhereFieldValue } from './queryWhere.js';
|
|
4
|
-
import type { RejectKeys } from './utility.js';
|
|
5
|
+
import type { IsMany, RejectKeys } from './utility.js';
|
|
5
6
|
/** The columns `$group` names by a literal `true`, so an uninferred `$group`, its own constraint, names none. */
|
|
6
7
|
type GroupedKeys<G> = {
|
|
7
8
|
[K in keyof G]: G[K] extends true ? K : never;
|
|
@@ -23,22 +24,14 @@ export type QueryAggregateOp = (typeof QUERY_AGGREGATE_OPS)[number];
|
|
|
23
24
|
*/
|
|
24
25
|
export declare function isQueryAggregateOp(op: string): op is QueryAggregateOp;
|
|
25
26
|
/**
|
|
26
|
-
* DISTINCT
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* are omitted: DISTINCT is a no-op for them.
|
|
27
|
+
* `$countDistinct` -> `COUNT(DISTINCT col)`. Flat (not a nested `{ $distinct }` argument) so the op is
|
|
28
|
+
* self-documenting and greppable. The other ops have no DISTINCT variant: `$sum`/`$avg` over deduplicated
|
|
29
|
+
* values is rarely what totalling or averaging means, and DISTINCT is a no-op for `$min`/`$max`.
|
|
30
30
|
*/
|
|
31
|
-
|
|
32
|
-
readonly $countDistinct: '$count';
|
|
33
|
-
readonly $sumDistinct: '$sum';
|
|
34
|
-
readonly $avgDistinct: '$avg';
|
|
35
|
-
};
|
|
36
|
-
/** DISTINCT-qualified aggregate operators (the keys of {@link QUERY_AGGREGATE_DISTINCT_OP_BASE}). */
|
|
37
|
-
export type QueryAggregateDistinctOp = keyof typeof QUERY_AGGREGATE_DISTINCT_OP_BASE;
|
|
31
|
+
export type QueryAggregateDistinctOp = '$countDistinct';
|
|
38
32
|
/**
|
|
39
|
-
* Resolve an aggregate op key into its base op and whether it is DISTINCT-qualified
|
|
40
|
-
*
|
|
41
|
-
* (`$min`/`$max` have no distinct variant).
|
|
33
|
+
* Resolve an aggregate op key into its base op and whether it is DISTINCT-qualified: `$countDistinct`
|
|
34
|
+
* resolves to `$count` with `distinct: true`, a plain op to itself with `distinct: false`. Throws otherwise.
|
|
42
35
|
*/
|
|
43
36
|
export declare function resolveAggregateOp(key: string): {
|
|
44
37
|
op: QueryAggregateOp;
|
|
@@ -59,9 +52,8 @@ export type QueryFieldRef<E, F extends keyof E = FieldKey<E>> = ExactlyOne<Requi
|
|
|
59
52
|
/** The argument of an aggregate function: a field, or `'*'` (only meaningful for `COUNT(*)`). */
|
|
60
53
|
export type QueryAggregateArg<E> = QueryFieldRef<E> | '*';
|
|
61
54
|
/**
|
|
62
|
-
* Fields `SUM`/`AVG` can total. Restricted to numeric columns because
|
|
63
|
-
*
|
|
64
|
-
* produces the value the signature promises.
|
|
55
|
+
* Fields `SUM`/`AVG` can total. Restricted to numeric columns because totalling a text or date one is
|
|
56
|
+
* either an engine error or a coercion, and neither produces the value the signature promises.
|
|
65
57
|
*/
|
|
66
58
|
type NumericFieldKey<E> = {
|
|
67
59
|
readonly [K in FieldKey<E>]: [NonNullable<E[K]>] extends [number | bigint] ? K : never;
|
|
@@ -74,8 +66,12 @@ type AggregateOp = QueryAggregateOp | QueryAggregateDistinctOp;
|
|
|
74
66
|
* where `Extract` would quietly drop the renamed member and leave the subset wrong but valid.
|
|
75
67
|
*/
|
|
76
68
|
type OpsOf<K extends AggregateOp> = K;
|
|
69
|
+
/** Ops that add a column up, which keeps the column's own type: a `bigint` column totals to a `bigint`. */
|
|
70
|
+
type SummingOp = OpsOf<'$sum'>;
|
|
71
|
+
/** Ops that mean a column, which the engine floats, so the result is a `number` however wide the column. */
|
|
72
|
+
type AveragingOp = OpsOf<'$avg'>;
|
|
77
73
|
/** Ops that total a column, so their argument has to be numeric. */
|
|
78
|
-
type TotallingOp =
|
|
74
|
+
type TotallingOp = SummingOp | AveragingOp;
|
|
79
75
|
/**
|
|
80
76
|
* Every aggregate op mapped to the argument it accepts: `$count` a field or `'*'` (`COUNT(*)`),
|
|
81
77
|
* the totalling ops a numeric field, `$min`/`$max`/`$countDistinct` any field.
|
|
@@ -83,19 +79,56 @@ type TotallingOp = OpsOf<'$sum' | '$avg' | '$sumDistinct' | '$avgDistinct'>;
|
|
|
83
79
|
type QueryAggregateArgMap<E> = Record<'$count', QueryAggregateArg<E>> & Record<TotallingOp, QueryFieldRef<E, NumericFieldKey<E>>> & Record<Exclude<AggregateOp, '$count' | TotallingOp>, QueryFieldRef<E>>;
|
|
84
80
|
/**
|
|
85
81
|
* An aggregate over one field, exactly one op per entry: `{ $sum: { amount: true } }` is `SUM("amount")`,
|
|
86
|
-
* `{ $countDistinct: { id: true } }` is `COUNT(DISTINCT "id")`, and only `$count` takes `'*'`.
|
|
82
|
+
* `{ $countDistinct: { id: true } }` is `COUNT(DISTINCT "id")`, and only `$count` takes `'*'`. Its own
|
|
83
|
+
* `$where` narrows the rows it reads to those matching, over the entity's own fields.
|
|
87
84
|
*/
|
|
88
|
-
export type QueryAggregateFn<E> = ExactlyOne<QueryAggregateArgMap<E
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
export type QueryAggregateFn<E> = ExactlyOne<QueryAggregateArgMap<E>> & {
|
|
86
|
+
readonly $where?: QueryAggregateWhere<E>;
|
|
87
|
+
};
|
|
88
|
+
/** What an aggregate's own `$where` reads: the entity's fields, since a relation there is a subquery inside it. */
|
|
89
|
+
type QueryAggregateWhere<E> = QueryWhere<E, QueryRaw, FieldKey<E>>;
|
|
90
|
+
/** An aggregate's `$where` naming a key it does not have, refused: a captured `$select` skips the excess-property check. */
|
|
91
|
+
type AggregateWhereKeys<E, Fn> = Fn extends {
|
|
92
|
+
readonly $where: infer W;
|
|
93
|
+
} ? {
|
|
94
|
+
readonly $where: RejectKeys<Exclude<keyof W, keyof QueryAggregateWhere<E>>>;
|
|
95
|
+
} : unknown;
|
|
96
|
+
/** A single-key `{ [op]: Arg }` shape for each op in `Ops`, matched to infer that op's result or its argument. */
|
|
97
|
+
type FnWithOp<Ops extends string, Arg = unknown> = {
|
|
91
98
|
[K in Ops]: {
|
|
92
|
-
readonly [P in K]:
|
|
99
|
+
readonly [P in K]: Arg;
|
|
93
100
|
};
|
|
94
101
|
}[Ops];
|
|
95
102
|
/** Ops that count rows. Alone among the ops they answer `0`, never NULL, over an empty group. */
|
|
96
103
|
type CountingOp = OpsOf<'$count' | '$countDistinct'>;
|
|
97
|
-
/**
|
|
98
|
-
|
|
104
|
+
/** Ops that read back as the column they aggregate, rather than widening or floating it. */
|
|
105
|
+
type ColumnTypedOp = SummingOp | OpsOf<'$min' | '$max'>;
|
|
106
|
+
/**
|
|
107
|
+
* A field a group key reads, by the path to it: `{ orderId: true }`, or through a to-one relation,
|
|
108
|
+
* `{ transaction: { orderId: true } }`. A to-many is `never`, since joining one multiplies the rows.
|
|
109
|
+
*/
|
|
110
|
+
export type QueryGroupRef<E, K extends keyof E = FieldKey<E> | RelationKey<E>> = ExactlyOne<Required<{
|
|
111
|
+
[P in K]: P extends RelationKey<E> ? IsMany<E[P]> extends true ? never : QueryGroupRef<RelationTarget<E[P]>> : true;
|
|
112
|
+
}>>;
|
|
113
|
+
/** The columns to group by: a field switched on, `{ status: true }`, or an alias for a field a path reads. */
|
|
114
|
+
export type QueryGroupMap<E> = {
|
|
115
|
+
readonly [key: string]: true | QueryGroupRef<E>;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* A captured `$group` against its schema: a field key takes `true` and keeps its link, any other key is an
|
|
119
|
+
* alias for a path. A key switched on that is no field is refused by name, which the intersection alone lets by.
|
|
120
|
+
*/
|
|
121
|
+
type QueryGroupSchema<E, G> = Readonly<QuerySelect<E, FieldKey<E>, true>> & {
|
|
122
|
+
readonly [K in Exclude<NamedKeys<G>, FieldKey<E>>]: QueryGroupRef<E>;
|
|
123
|
+
} & RejectKeys<Exclude<GroupedKeys<G>, FieldKey<E>>>;
|
|
124
|
+
/**
|
|
125
|
+
* The value a path reads: the field at its end as its column holds it, `null` only where that does, since
|
|
126
|
+
* the path joins the rows it reads. `any` answers `unknown`: TypeScript checks a deferred type by
|
|
127
|
+
* instantiating it with `any`, which would walk every relation of the entity on every aggregate call.
|
|
128
|
+
*/
|
|
129
|
+
type GroupRefValue<E, Ref> = 0 extends 1 & Ref ? unknown : {
|
|
130
|
+
[K in keyof Ref & keyof E]: Ref[K] extends true ? Exclude<E[K], undefined> : GroupRefValue<RelationTarget<E[K]>, Ref[K]>;
|
|
131
|
+
}[keyof Ref & keyof E];
|
|
99
132
|
/** Computed columns by the alias each is read back under: `{ count: { $count: '*' }, avgAge: { $avg: { age: true } } }`. */
|
|
100
133
|
export type QueryAggMap<E> = {
|
|
101
134
|
readonly [alias: string]: QueryAggregateFn<E>;
|
|
@@ -103,14 +136,11 @@ export type QueryAggMap<E> = {
|
|
|
103
136
|
/** The entity type of an aggregated field reference `F`, or `unknown` if it is not a known field. */
|
|
104
137
|
type FieldValueType<E, F> = F extends keyof E ? E[F] : unknown;
|
|
105
138
|
/**
|
|
106
|
-
* A computed column's type: a count is a `number`; every other aggregate is `null` over no rows, a
|
|
107
|
-
*
|
|
139
|
+
* A computed column's type: a count is a `number`; every other aggregate is `null` over no rows, a mean
|
|
140
|
+
* a `number` whatever it read, and a total, a `$min` or a `$max` the column's own type - so a `bigint`
|
|
141
|
+
* column totals to a `bigint`, which is what the driver decodes rather than rounding through a float.
|
|
108
142
|
*/
|
|
109
|
-
type QueryAggregateFnResult<E, Fn> = Fn extends FnWithOp<CountingOp> ? number : Fn extends FnWithOp<
|
|
110
|
-
readonly $min: infer F;
|
|
111
|
-
} | {
|
|
112
|
-
readonly $max: infer F;
|
|
113
|
-
} ? FieldValueType<E, keyof F> | null : unknown;
|
|
143
|
+
type QueryAggregateFnResult<E, Fn> = Fn extends FnWithOp<CountingOp> ? number : Fn extends FnWithOp<AveragingOp> ? number | null : Fn extends FnWithOp<ColumnTypedOp, infer F> ? FieldValueType<E, keyof F> | null : unknown;
|
|
114
144
|
/**
|
|
115
145
|
* Flattens an intersection into a single object literal for readable editor hovers.
|
|
116
146
|
* @internal
|
|
@@ -118,8 +148,10 @@ type QueryAggregateFnResult<E, Fn> = Fn extends FnWithOp<CountingOp> ? number :
|
|
|
118
148
|
type Simplify<T> = {
|
|
119
149
|
[K in keyof T]: T[K];
|
|
120
150
|
} & {};
|
|
121
|
-
/** An aggregate's row: each grouped column with its entity type, each computed one with its aggregate's. */
|
|
151
|
+
/** An aggregate's row: each grouped column with its entity type, each alias with its path's, each computed one with its aggregate's. */
|
|
122
152
|
export type QueryAggregateResult<E, G, A> = Simplify<Pick<E, GroupedKeys<G> & FieldKey<E>> & {
|
|
153
|
+
-readonly [K in Exclude<NamedKeys<G>, FieldKey<E>>]: GroupRefValue<E, G[K]>;
|
|
154
|
+
} & {
|
|
123
155
|
-readonly [K in keyof A]: QueryAggregateFnResult<E, A[K]>;
|
|
124
156
|
}>;
|
|
125
157
|
/** A `HAVING` as the dialects read it, erased; {@link QueryAggregate.$having} is where it is typed. `{ count: { $gt: 5 } }` */
|
|
@@ -136,19 +168,18 @@ export type QueryAggregate<E, G extends QueryGroupMap<E> = QueryGroupMap<E>, A e
|
|
|
136
168
|
*/
|
|
137
169
|
readonly $where?: QueryWhere<E>;
|
|
138
170
|
/**
|
|
139
|
-
* Columns to group by
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* map meets its schema, {@link QueryGroupMap}, so each key keeps its link to the entity property.
|
|
171
|
+
* Columns to group by: `{ status: true }`, or an alias for a to-one relation's field by the path to
|
|
172
|
+
* it, `{ orderId: { transaction: { orderId: true } } }`. The captured map meets its schema, so a field
|
|
173
|
+
* key keeps its link to the entity property, and a typo matches neither form.
|
|
143
174
|
*/
|
|
144
|
-
readonly $group?: G &
|
|
175
|
+
readonly $group?: G & QueryGroupSchema<E, G>;
|
|
145
176
|
/**
|
|
146
177
|
* The computed columns by alias, the captured map meeting its schema so field keys stay linked. An alias
|
|
147
178
|
* repeating a `$group` column is refused, since both would come back under one name.
|
|
148
179
|
*/
|
|
149
180
|
readonly $select?: A & {
|
|
150
|
-
readonly [K in keyof A]: QueryAggregateFn<E>;
|
|
151
|
-
} & RejectKeys<NamedKeys<A> &
|
|
181
|
+
readonly [K in keyof A]: QueryAggregateFn<E> & AggregateWhereKeys<E, A[K]>;
|
|
182
|
+
} & RejectKeys<NamedKeys<A> & NamedKeys<G>>;
|
|
152
183
|
/** Filtering after grouping, by a result column, each value typed as that column is. */
|
|
153
184
|
readonly $having?: {
|
|
154
185
|
readonly [K in keyof QueryAggregateResult<E, G, A>]?: QueryWhereFieldValue<QueryAggregateResult<E, G, A>[K]>;
|
|
@@ -7,29 +7,12 @@ export function isQueryAggregateOp(op) {
|
|
|
7
7
|
return QUERY_AGGREGATE_OPS.includes(op);
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* (not a nested `{ $distinct }` argument) so the op is self-documenting and greppable. `$min`/`$max`
|
|
13
|
-
* are omitted: DISTINCT is a no-op for them.
|
|
14
|
-
*/
|
|
15
|
-
const QUERY_AGGREGATE_DISTINCT_OP_BASE = {
|
|
16
|
-
$countDistinct: '$count',
|
|
17
|
-
$sumDistinct: '$sum',
|
|
18
|
-
$avgDistinct: '$avg',
|
|
19
|
-
};
|
|
20
|
-
/** Whether `key` is a DISTINCT-qualified aggregate operator (narrows for a cast-free base lookup). */
|
|
21
|
-
function isQueryAggregateDistinctOp(key) {
|
|
22
|
-
// `Object.hasOwn`, not `key in`: the latter matches inherited members like `toString`.
|
|
23
|
-
return Object.hasOwn(QUERY_AGGREGATE_DISTINCT_OP_BASE, key);
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Resolve an aggregate op key into its base op and whether it is DISTINCT-qualified. A flat distinct
|
|
27
|
-
* op resolves to its base op with `distinct: true`; a plain op to `distinct: false`. Throws otherwise
|
|
28
|
-
* (`$min`/`$max` have no distinct variant).
|
|
10
|
+
* Resolve an aggregate op key into its base op and whether it is DISTINCT-qualified: `$countDistinct`
|
|
11
|
+
* resolves to `$count` with `distinct: true`, a plain op to itself with `distinct: false`. Throws otherwise.
|
|
29
12
|
*/
|
|
30
13
|
export function resolveAggregateOp(key) {
|
|
31
|
-
if (
|
|
32
|
-
return { op:
|
|
14
|
+
if (key === '$countDistinct') {
|
|
15
|
+
return { op: '$count', distinct: true };
|
|
33
16
|
}
|
|
34
17
|
if (isQueryAggregateOp(key)) {
|
|
35
18
|
return { op: key, distinct: false };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EntityId, EntityWrite, FieldKey, RelationKey, UpdateWrite, WrittenId } from './entity.js';
|
|
2
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';
|
|
@@ -31,9 +31,9 @@ export interface SharedQuerier<W extends QuerierTransport, O, DO = O> {
|
|
|
31
31
|
/** Whether any record matches: a count capped at one row, so the engine stops at the first match. */
|
|
32
32
|
exists<E extends object>(entity: Type<E>, q?: QueryFilter<E, QuerierRaw<W>>, opts?: O): QuerierResult<W, boolean>;
|
|
33
33
|
/** Update the record with the given primary key; resolves to the number of affected rows. */
|
|
34
|
-
updateOneById<E extends object>(entity: Type<E>, id: EntityId<E>, payload:
|
|
34
|
+
updateOneById<E extends object>(entity: Type<E>, id: EntityId<E>, payload: UpdateWrite<E, QuerierRaw<W>>, opts?: O): QuerierResult<W, number>;
|
|
35
35
|
/** Update the records matching the query; resolves to the number of affected rows. */
|
|
36
|
-
updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E, QuerierRaw<W>>, payload:
|
|
36
|
+
updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E, QuerierRaw<W>>, payload: UpdateWrite<E, QuerierRaw<W>>, opts?: O): QuerierResult<W, number>;
|
|
37
37
|
/**
|
|
38
38
|
* delete or SoftDelete a record.
|
|
39
39
|
* @param entity the entity to persist on
|
|
@@ -59,31 +59,31 @@ export interface UniversalQuerier extends SharedQuerier<'server', QueryOptions>
|
|
|
59
59
|
*/
|
|
60
60
|
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>>;
|
|
61
61
|
/** Insert a record and resolve to its id. See {@link UniversalQuerier.insertMany}. */
|
|
62
|
-
insertOne<E extends object>(entity: Type<E>, payload:
|
|
62
|
+
insertOne<E extends object>(entity: Type<E>, payload: EntityWrite<E>): Promise<WrittenId<E> | undefined>;
|
|
63
63
|
/**
|
|
64
64
|
* Insert records in as few statements as the bind limit allows, resolving to their ids in payload order.
|
|
65
65
|
* Ids are exact everywhere but MySQL, which infers them from its header and reports `undefined` rather
|
|
66
66
|
* than a guess where it cannot: a batch naming some keys, or a key that is not `AUTO_INCREMENT`.
|
|
67
67
|
*/
|
|
68
|
-
insertMany<E extends object>(entity: Type<E>, payload:
|
|
68
|
+
insertMany<E extends object>(entity: Type<E>, payload: EntityWrite<E>[]): Promise<(WrittenId<E> | undefined)[]>;
|
|
69
69
|
/** Insert or update a record by its conflict paths; resolves to its id and whether it was created. */
|
|
70
|
-
upsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload:
|
|
70
|
+
upsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityWrite<E>): Promise<QueryUpsertOneResult<E>>;
|
|
71
71
|
/** Insert or update records by their conflict paths; resolves to their ids in payload order. */
|
|
72
|
-
upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload:
|
|
72
|
+
upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityWrite<E>[]): Promise<QueryUpsertManyResult<E>>;
|
|
73
73
|
/**
|
|
74
74
|
* insert or update a record.
|
|
75
75
|
* @param entity the entity to persist on
|
|
76
76
|
* @param payload the data to be persisted
|
|
77
77
|
* @return the ID
|
|
78
78
|
*/
|
|
79
|
-
saveOne<E extends object>(entity: Type<E>, payload:
|
|
79
|
+
saveOne<E extends object>(entity: Type<E>, payload: EntityWrite<E>): Promise<WrittenId<E> | undefined>;
|
|
80
80
|
/**
|
|
81
81
|
* Insert or update records.
|
|
82
82
|
* @param entity the entity to persist on
|
|
83
83
|
* @param payload the data to be persisted
|
|
84
84
|
* @return the IDs
|
|
85
85
|
*/
|
|
86
|
-
saveMany<E extends object>(entity: Type<E>, payload:
|
|
86
|
+
saveMany<E extends object>(entity: Type<E>, payload: EntityWrite<E>[]): Promise<(WrittenId<E> | undefined)[]>;
|
|
87
87
|
/**
|
|
88
88
|
* Restore soft-deleted records (sets the soft-delete field back to `null`). Throws if the
|
|
89
89
|
* entity has no soft-delete field.
|
package/dist/type/vector.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { VectorCast } from '../dialect/vectorCast.js';
|
|
1
2
|
import type { IndexType } from '../schema/types.js';
|
|
2
3
|
/**
|
|
3
4
|
* A vector search's metric: `cosine` (the default), `l2`, `inner` product or `l1`. No hamming: every
|
|
@@ -55,6 +56,22 @@ export type VectorIndexOptions = {
|
|
|
55
56
|
/** IVFFlat: number of inverted lists. */
|
|
56
57
|
lists?: number;
|
|
57
58
|
};
|
|
59
|
+
/** The metric a search or an index measures by where nothing names one: every engine with vectors has it. */
|
|
60
|
+
export declare const DEFAULT_VECTOR_DISTANCE: VectorDistance;
|
|
61
|
+
/** The metric an index is built for: its own, else {@link DEFAULT_VECTOR_DISTANCE}. */
|
|
62
|
+
export declare function indexDistance(index: {
|
|
63
|
+
readonly distance?: VectorDistance;
|
|
64
|
+
}): VectorDistance;
|
|
65
|
+
/**
|
|
66
|
+
* What a vector index's DDL reads off the field it indexes, never declared on the index itself, so the
|
|
67
|
+
* two cannot disagree. Absent for a non-vector index, and for one whose field is unknown.
|
|
68
|
+
*/
|
|
69
|
+
export type IndexedVectorField = {
|
|
70
|
+
/** The field's vector type, which pgvector's operator classes are named after (`halfvec_cosine_ops`); `vector` where absent. */
|
|
71
|
+
readonly vectorType?: VectorCast;
|
|
72
|
+
/** The field's dimensions, which an Atlas vector search index states. */
|
|
73
|
+
readonly dimensions?: number;
|
|
74
|
+
};
|
|
58
75
|
/**
|
|
59
76
|
* Index types whose emitted DDL depends on the distance metric. The runtime list is the source, so
|
|
60
77
|
* the type and every dialect's "do I have this one?" answer cannot drift from each other.
|
package/dist/type/vector.js
CHANGED
|
@@ -5,6 +5,12 @@ export function unsupportedVectorMetric(dialectName, distance, indexName) {
|
|
|
5
5
|
const where = indexName === undefined ? '' : ` (index "${indexName}")`;
|
|
6
6
|
return new TypeError(`${dialectName} does not support vector distance metric: ${distance}${where}`);
|
|
7
7
|
}
|
|
8
|
+
/** The metric a search or an index measures by where nothing names one: every engine with vectors has it. */
|
|
9
|
+
export const DEFAULT_VECTOR_DISTANCE = 'cosine';
|
|
10
|
+
/** The metric an index is built for: its own, else {@link DEFAULT_VECTOR_DISTANCE}. */
|
|
11
|
+
export function indexDistance(index) {
|
|
12
|
+
return index.distance ?? DEFAULT_VECTOR_DISTANCE;
|
|
13
|
+
}
|
|
8
14
|
/**
|
|
9
15
|
* Index types whose emitted DDL depends on the distance metric. The runtime list is the source, so
|
|
10
16
|
* the type and every dialect's "do I have this one?" answer cannot drift from each other.
|
|
@@ -10,3 +10,5 @@ export declare function declaredIndexes<E>(meta: EntityMeta<E>): EntityIndexMeta
|
|
|
10
10
|
export declare function renderIndexColumn(entry: EntityIndexColumn, render: (sql: QueryRaw) => string): IndexColumnSchema;
|
|
11
11
|
/** What an unnamed index's name is built from: each entry's column, or `expr<n>` for an expression, which has none. */
|
|
12
12
|
export declare function indexNameParts(entries: readonly EntityIndexColumn[]): string[];
|
|
13
|
+
/** The name an index is created and read by: its own, else one derived from its table and its entries' columns. */
|
|
14
|
+
export declare function declaredIndexName(name: string | undefined, table: string, entries: readonly EntityIndexColumn[]): string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ColumnRef, QueryRaw, } from '../type/index.js';
|
|
2
2
|
import { definedEntries } from './object.util.js';
|
|
3
|
+
import { derivedIndexName } from './sql.util.js';
|
|
3
4
|
/**
|
|
4
5
|
* Reduces an authored index entry to the form metadata keeps, so a column, an expression and an options
|
|
5
6
|
* object reach the schema as one: a column read off the refs as its key, any other `raw` as it is.
|
|
@@ -30,3 +31,7 @@ export function renderIndexColumn(entry, render) {
|
|
|
30
31
|
export function indexNameParts(entries) {
|
|
31
32
|
return entries.map((entry, at) => (typeof entry.column === 'string' ? entry.column : `expr${at}`));
|
|
32
33
|
}
|
|
34
|
+
/** The name an index is created and read by: its own, else one derived from its table and its entries' columns. */
|
|
35
|
+
export function declaredIndexName(name, table, entries) {
|
|
36
|
+
return name ?? derivedIndexName(table, indexNameParts(entries));
|
|
37
|
+
}
|
|
@@ -71,6 +71,10 @@ export declare function findVectorSort<E>(sort: QuerySortMap<E> | undefined): {
|
|
|
71
71
|
key: string;
|
|
72
72
|
search: QueryVectorSearch;
|
|
73
73
|
} | undefined;
|
|
74
|
+
/** `$candidates`, checked: it can be spelled into a statement, and `/http` input is untyped. */
|
|
75
|
+
export declare function vectorCandidates(q: {
|
|
76
|
+
readonly $candidates?: number;
|
|
77
|
+
}): number | undefined;
|
|
74
78
|
/**
|
|
75
79
|
* The vector index declared on `key`, if any. Answers both "is there an ANN index to tune here" and
|
|
76
80
|
* "which kind", which decide the name Atlas is queried by and the setting Postgres is tuned with.
|
|
@@ -104,16 +108,20 @@ export declare function applyFilters<E>(meta: EntityMeta<E>, whereMap: QueryWher
|
|
|
104
108
|
/**
|
|
105
109
|
* Parsed entry from a `$group` map - either a raw group key or an aggregate function call.
|
|
106
110
|
*/
|
|
107
|
-
export type ParsedGroupEntry = {
|
|
111
|
+
export type ParsedGroupEntry<E = object> = {
|
|
108
112
|
readonly kind: 'key';
|
|
109
113
|
readonly alias: string;
|
|
114
|
+
/** The field it reads, behind the to-one relations leading to it: `['transaction', 'orderId']`. */
|
|
115
|
+
readonly path: readonly string[];
|
|
110
116
|
} | {
|
|
111
117
|
readonly kind: 'fn';
|
|
112
118
|
readonly alias: string;
|
|
113
119
|
readonly op: QueryAggregateOp;
|
|
114
120
|
readonly fieldRef: string;
|
|
115
|
-
/** `true` for
|
|
121
|
+
/** `true` for `$countDistinct`: `COUNT(DISTINCT field)`. */
|
|
116
122
|
readonly distinct: boolean;
|
|
123
|
+
/** The rows it reads, where not all of the statement's. */
|
|
124
|
+
readonly where?: QueryWhere<E>;
|
|
117
125
|
};
|
|
118
126
|
/**
|
|
119
127
|
* The `$size` of a relation condition, `{ comments: { $size: { $gte: 2 } } }`, or `undefined` where it
|
|
@@ -130,7 +138,7 @@ export declare function parseSortByCount(val: unknown): unknown;
|
|
|
130
138
|
* Parse the `$group` (grouped columns) and `$select` (computed aggregates) maps into structured
|
|
131
139
|
* entries consumable by any dialect. Grouped columns come first, then computed columns.
|
|
132
140
|
*/
|
|
133
|
-
export declare function parseGroupMap<E>(group?: QueryGroupMap<E>, select?: QueryAggMap<E>): ParsedGroupEntry[];
|
|
141
|
+
export declare function parseGroupMap<E>(group?: QueryGroupMap<E>, select?: QueryAggMap<E>): ParsedGroupEntry<E>[];
|
|
134
142
|
/**
|
|
135
143
|
* Whether `value` is a map of comparison operators rather than a value to compare against. Only a
|
|
136
144
|
* plain object qualifies: `Date`, `QueryRaw`, `Uint8Array` and arrays are all `typeof 'object'`, and
|
|
@@ -155,6 +163,12 @@ export declare function assertNonNegativeInteger(value: number, clause: string):
|
|
|
155
163
|
export declare function throwUnknownAggregateColumn(key: string, clause: string): never;
|
|
156
164
|
/** {@link throwUnknownAggregateColumn} over every key of a clause, for backends that check up front. */
|
|
157
165
|
export declare function assertAggregateColumns(clauseMap: object, emitted: ReadonlySet<string>, clause: string): void;
|
|
166
|
+
/** The text-search config a fulltext index builds with, which a search it serves has to parse with too. */
|
|
167
|
+
export declare function fulltextConfig(index: {
|
|
168
|
+
readonly config?: string;
|
|
169
|
+
}): string;
|
|
170
|
+
/** The fulltext index over exactly `fields`, in order, which a search of them is served by. */
|
|
171
|
+
export declare function fulltextIndexOver<E>(meta: EntityMeta<E>, fields: readonly string[]): EntityIndexMeta<E> | undefined;
|
|
158
172
|
/**
|
|
159
173
|
* The fields a `$text` searches: those it names, or else the columns of the entity's fulltext index,
|
|
160
174
|
* the declaration MySQL's `MATCH` has to name exactly and a MongoDB text index already is. Refused
|