@query-graph/core 1.0.0 → 1.1.1
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 +116 -0
- package/composition.js +126 -0
- package/definition.d.ts +117 -39
- package/definition.js +3 -0
- package/dsl.d.ts +1108 -99
- package/dsl.js +3 -0
- package/index.d.ts +33 -0
- package/index.js +56 -50
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -496,6 +496,63 @@ JavaScript number и десятичная строка, а list-параметр
|
|
|
496
496
|
типа. Эти проверки существуют только в декларациях TypeScript; сериализация
|
|
497
497
|
definition, N-API вызов, runtime-валидация Rust и SQL compilation не меняются.
|
|
498
498
|
|
|
499
|
+
### Тип результата проекции
|
|
500
|
+
|
|
501
|
+
DSL сохраняет scalar type и nullability каждого projection expression в compile-time metadata.
|
|
502
|
+
`ResultOf` строит из этих сведений вложенный тип результата по projection paths:
|
|
503
|
+
|
|
504
|
+
```ts
|
|
505
|
+
import type { QueryOperation, ResultOf } from '@query-graph/core/dsl'
|
|
506
|
+
|
|
507
|
+
const operation = {
|
|
508
|
+
select: ['id', 'profile.displayName'],
|
|
509
|
+
parameters: { userId: 42 },
|
|
510
|
+
} as const satisfies QueryOperation<typeof definition>
|
|
511
|
+
|
|
512
|
+
type UserRow = ResultOf<typeof definition, typeof operation>
|
|
513
|
+
// {
|
|
514
|
+
// id: number | string
|
|
515
|
+
// profile: { displayName: string | null }
|
|
516
|
+
// }
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
Если второй аргумент не передан, в результат входят только поля с `default: true`.
|
|
520
|
+
`ResultOf` также принимает тип, возвращенный `registerDefinition`, или mapped graph:
|
|
521
|
+
|
|
522
|
+
```ts
|
|
523
|
+
type DefaultUserRow = ResultOf<typeof graph>
|
|
524
|
+
// { id: number | string }
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Nullability учитывает nullable-поле и всю цепочку optional relations до его source.
|
|
528
|
+
Required relation после optional relation не делает значение обязательным. Семантика
|
|
529
|
+
выражений тоже сохраняется: например, `coalesce(nullableField, 'fallback')` выводится
|
|
530
|
+
как non-null `string`.
|
|
531
|
+
|
|
532
|
+
Типы значений по умолчанию нейтральны к DB driver: `int64` и `decimal` представлены
|
|
533
|
+
как `number | string`, а `date`, `dateTime` и `binary` - как `string`. Адаптер
|
|
534
|
+
исполнения может передать третьим аргументом собственную полную scalar map:
|
|
535
|
+
|
|
536
|
+
```ts
|
|
537
|
+
import type { DefaultScalarOutputTypeMap, ResultOf } from '@query-graph/core/dsl'
|
|
538
|
+
|
|
539
|
+
type DriverScalars = Omit<DefaultScalarOutputTypeMap, 'int64' | 'dateTime'> & {
|
|
540
|
+
int64: bigint
|
|
541
|
+
dateTime: Date
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
type DriverUserRow = ResultOf<typeof definition, typeof operation, DriverScalars>
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Для точного учета явного `select` operation должна сохранять literal tuple через
|
|
548
|
+
`as const` или `satisfies`. Это только TypeScript API: wire definition, N-API и
|
|
549
|
+
Rust planner не получают дополнительного runtime state.
|
|
550
|
+
|
|
551
|
+
`ResultOf` описывает логическую форму одной строки, восстановленную из путей
|
|
552
|
+
проекции. Он не выполняет запрос и не превращает relation `many` в массив.
|
|
553
|
+
Группировка строк, дедупликация и гидратация коллекций остаются ответственностью
|
|
554
|
+
потребителя модуля.
|
|
555
|
+
|
|
499
556
|
## Диагностика
|
|
500
557
|
|
|
501
558
|
Ошибки N-API имеют имя `QueryGraphError` и сохраняют обычный `message` и stack.
|
|
@@ -659,3 +716,62 @@ relation `many` для него допустим. Для пагинации вл
|
|
|
659
716
|
|
|
660
717
|
Полный пример графа, отображения, операции, сгенерированного SQL и замера
|
|
661
718
|
производительности компиляции находится в `benchmark/bench.ts`.
|
|
719
|
+
|
|
720
|
+
## Batch-связи и двухфазное выполнение
|
|
721
|
+
|
|
722
|
+
Поля из отдельного запроса подключаются без изменения обычного relational-графа:
|
|
723
|
+
|
|
724
|
+
```ts
|
|
725
|
+
const childrenQuery = batchQuery({
|
|
726
|
+
graph: childrenGraph,
|
|
727
|
+
key: {
|
|
728
|
+
path: 'parentId',
|
|
729
|
+
parameter: parentIds,
|
|
730
|
+
},
|
|
731
|
+
})
|
|
732
|
+
|
|
733
|
+
const children = batchRelation({
|
|
734
|
+
name: 'children',
|
|
735
|
+
from: 'id',
|
|
736
|
+
query: childrenQuery,
|
|
737
|
+
cardinality: 'many',
|
|
738
|
+
})
|
|
739
|
+
|
|
740
|
+
const graph = composeGraph({ root: rootGraph, relations: [children] })
|
|
741
|
+
const plan = graph.compileOraclePlan({
|
|
742
|
+
select: ['id', 'children.id'],
|
|
743
|
+
parameters: {},
|
|
744
|
+
limit: 20,
|
|
745
|
+
})
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
`batchQuery` принадлежит child-модулю и один раз закрепляет его graph,
|
|
749
|
+
projection path ключа и list-параметр. Поэтому разные root-графы могут
|
|
750
|
+
переиспользовать query, не повторяя внутренний контракт child-запроса.
|
|
751
|
+
|
|
752
|
+
`composeGraph` оставляет в JavaScript только декларативный фасад. Проверка
|
|
753
|
+
совместимости ключей и параметров, разделение operation на root и batch-шаги и
|
|
754
|
+
компиляция SQL выполняются в Rust. `compileOraclePlan` или
|
|
755
|
+
`compileSqlServerPlan` сразу компилирует только `plan.root`; child SQL создаётся
|
|
756
|
+
при вызове `compileBatch`. Plan сохраняет выбранные dialect и version, поэтому
|
|
757
|
+
отложенный statement компилируется с теми же настройками, что и root.
|
|
758
|
+
|
|
759
|
+
Для подготовки child bindings каждый шаг в `plan.batches` также содержит имя
|
|
760
|
+
ключевого параметра `keyParameter` и снимок статических `parameters`. Executor
|
|
761
|
+
объединяет их с `{ [batch.keyParameter]: keys }` и сопоставляет полученные значения
|
|
762
|
+
с возвращёнными `statement.bindings`.
|
|
763
|
+
|
|
764
|
+
Executor потребителя выполняет `plan.root`, собирает уникальные ненулевые
|
|
765
|
+
значения `parentKey` из `plan.batches`, вызывает
|
|
766
|
+
`plan.compileBatch('children', parentIds)`, выполняет child statement и
|
|
767
|
+
связывает строки по `childKey`. `cardinality: 'one'` означает объект или `null`,
|
|
768
|
+
а `many` — массив. Query-graph не подключается к БД, не дедуплицирует ключи, не
|
|
769
|
+
выполняет statements и не гидратирует результат; при пустом наборе ключей
|
|
770
|
+
executor должен пропустить child-запрос.
|
|
771
|
+
|
|
772
|
+
Ordering, offset и limit корневой operation применяются только к `plan.root`.
|
|
773
|
+
Batch-связь может задать собственный `ordering`, но child pagination не
|
|
774
|
+
добавляется. Batch-поля выбираются только явно (`children.id`); без prefixed
|
|
775
|
+
path соответствующего шага в `plan.batches` нет. Флаги injection в metadata
|
|
776
|
+
показывают executor, какие ключи были добавлены исключительно для связывания и
|
|
777
|
+
не должны попасть в публичный результат.
|
package/composition.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const BATCH_QUERY_KEYS = new Set(['graph', 'key'])
|
|
4
|
+
const BATCH_QUERY_KEY_KEYS = new Set(['path', 'parameter'])
|
|
5
|
+
const BATCH_RELATION_KEYS = new Set(['name', 'from', 'query', 'cardinality', 'parameters', 'ordering'])
|
|
6
|
+
const COMPOSE_KEYS = new Set(['root', 'relations'])
|
|
7
|
+
const batchQueryBrand = Symbol('BatchQuery')
|
|
8
|
+
const batchBrand = Symbol('BatchRelation')
|
|
9
|
+
|
|
10
|
+
function configurationOf(factory, value, allowedKeys) {
|
|
11
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
12
|
+
throw new TypeError(`${factory} expects a configuration object`)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const unknownKeys = Reflect.ownKeys(value).filter((key) => typeof key !== 'string' || !allowedKeys.has(key))
|
|
16
|
+
if (unknownKeys.length > 0) {
|
|
17
|
+
const label = unknownKeys.length === 1 ? 'field' : 'fields'
|
|
18
|
+
const keys = unknownKeys.map((key) => JSON.stringify(String(key))).join(', ')
|
|
19
|
+
throw new TypeError(`${factory} received unknown configuration ${label} ${keys}`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return value
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function nonEmptyString(value, label) {
|
|
26
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
27
|
+
throw new TypeError(`${label} must be a non-empty string`)
|
|
28
|
+
}
|
|
29
|
+
return value
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function relationalGraph(value, label) {
|
|
33
|
+
if (value === null || typeof value !== 'object' || typeof value.compose !== 'function') {
|
|
34
|
+
throw new TypeError(`${label} must be a RelationalQueryGraph`)
|
|
35
|
+
}
|
|
36
|
+
return value
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function batchQueryOf(value, label) {
|
|
40
|
+
if (value === null || typeof value !== 'object' || value[batchQueryBrand] !== true) {
|
|
41
|
+
throw new TypeError(`${label} must be created by batchQuery`)
|
|
42
|
+
}
|
|
43
|
+
return value
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function batchQuery(value) {
|
|
47
|
+
const configuration = configurationOf('batchQuery', value, BATCH_QUERY_KEYS)
|
|
48
|
+
const graph = relationalGraph(configuration.graph, 'batchQuery.graph')
|
|
49
|
+
const keyConfiguration = configurationOf('batchQuery.key', configuration.key, BATCH_QUERY_KEY_KEYS)
|
|
50
|
+
const path = nonEmptyString(keyConfiguration.path, 'batchQuery.key.path')
|
|
51
|
+
const parameter = nonEmptyString(
|
|
52
|
+
typeof keyConfiguration.parameter === 'string' ? keyConfiguration.parameter : keyConfiguration.parameter?.name,
|
|
53
|
+
'batchQuery.key.parameter',
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return Object.freeze({
|
|
57
|
+
graph,
|
|
58
|
+
key: Object.freeze({
|
|
59
|
+
path,
|
|
60
|
+
parameter,
|
|
61
|
+
}),
|
|
62
|
+
[batchQueryBrand]: true,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function batchRelation(value) {
|
|
67
|
+
const configuration = configurationOf('batchRelation', value, BATCH_RELATION_KEYS)
|
|
68
|
+
const name = nonEmptyString(configuration.name, 'batchRelation.name')
|
|
69
|
+
if (name.includes('.')) {
|
|
70
|
+
throw new TypeError('batchRelation.name must be a single projection path segment')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const from = nonEmptyString(configuration.from, 'batchRelation.from')
|
|
74
|
+
const query = batchQueryOf(configuration.query, 'batchRelation.query')
|
|
75
|
+
|
|
76
|
+
if (configuration.cardinality !== 'one' && configuration.cardinality !== 'many') {
|
|
77
|
+
throw new TypeError("batchRelation.cardinality must be 'one' or 'many'")
|
|
78
|
+
}
|
|
79
|
+
if (
|
|
80
|
+
configuration.parameters !== undefined &&
|
|
81
|
+
(configuration.parameters === null ||
|
|
82
|
+
typeof configuration.parameters !== 'object' ||
|
|
83
|
+
Array.isArray(configuration.parameters))
|
|
84
|
+
) {
|
|
85
|
+
throw new TypeError('batchRelation.parameters must be an object')
|
|
86
|
+
}
|
|
87
|
+
if (configuration.ordering !== undefined) {
|
|
88
|
+
nonEmptyString(configuration.ordering, 'batchRelation.ordering')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return Object.freeze({
|
|
92
|
+
name,
|
|
93
|
+
from,
|
|
94
|
+
query,
|
|
95
|
+
cardinality: configuration.cardinality,
|
|
96
|
+
parameters: Object.freeze({ ...configuration.parameters }),
|
|
97
|
+
...(configuration.ordering === undefined ? {} : { ordering: configuration.ordering }),
|
|
98
|
+
[batchBrand]: true,
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function composeGraph(value) {
|
|
103
|
+
const configuration = configurationOf('composeGraph', value, COMPOSE_KEYS)
|
|
104
|
+
const root = relationalGraph(configuration.root, 'composeGraph.root')
|
|
105
|
+
if (!Array.isArray(configuration.relations)) {
|
|
106
|
+
throw new TypeError('composeGraph.relations must be an array')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return configuration.relations.reduce((graph, relation, index) => {
|
|
110
|
+
if (relation?.[batchBrand] !== true) {
|
|
111
|
+
throw new TypeError(`composeGraph.relations[${index}] must be created by batchRelation`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return graph.withBatchRelation(relation.query.graph, {
|
|
115
|
+
name: relation.name,
|
|
116
|
+
from: relation.from,
|
|
117
|
+
to: relation.query.key.path,
|
|
118
|
+
parameter: relation.query.key.parameter,
|
|
119
|
+
cardinality: relation.cardinality,
|
|
120
|
+
parameters: relation.parameters,
|
|
121
|
+
...(relation.ordering === undefined ? {} : { ordering: relation.ordering }),
|
|
122
|
+
})
|
|
123
|
+
}, root.compose())
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
module.exports = { batchQuery, batchRelation, composeGraph }
|
package/definition.d.ts
CHANGED
|
@@ -2,19 +2,27 @@ export * from './dsl.js'
|
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
4
|
ConstraintDefinition,
|
|
5
|
-
DimensionDefinition,
|
|
6
5
|
Expression,
|
|
7
6
|
ExpressionInput,
|
|
7
|
+
ExpressionNullability,
|
|
8
|
+
ExpressionScalarType,
|
|
8
9
|
FieldExpression,
|
|
10
|
+
FieldSpecIsNullable,
|
|
9
11
|
FieldSpecMap,
|
|
12
|
+
FieldSpecScalarType,
|
|
10
13
|
JoinProjectionPath,
|
|
11
|
-
MeasureDefinition,
|
|
12
14
|
ParameterRef,
|
|
13
|
-
ProjectionFieldDefinition,
|
|
14
15
|
RelationCardinality,
|
|
15
16
|
RelationRef,
|
|
16
17
|
RelationSelection,
|
|
18
|
+
ScalarType,
|
|
19
|
+
SourceFieldNullability,
|
|
20
|
+
SourceReferenceKey,
|
|
17
21
|
SourceRef,
|
|
22
|
+
TypedExpression,
|
|
23
|
+
TypedDimensionDefinition,
|
|
24
|
+
TypedMeasureDefinition,
|
|
25
|
+
TypedProjectionField,
|
|
18
26
|
} from './dsl.js'
|
|
19
27
|
|
|
20
28
|
export const GRAPH_DEFINITION_VERSION: 9
|
|
@@ -23,25 +31,41 @@ export function field<
|
|
|
23
31
|
const Key extends string,
|
|
24
32
|
const Fields extends FieldSpecMap,
|
|
25
33
|
const Name extends Extract<keyof Fields, string>,
|
|
26
|
-
>(
|
|
34
|
+
>(
|
|
35
|
+
source: SourceRef<Key, Fields>,
|
|
36
|
+
name: Name,
|
|
37
|
+
): TypedExpression<
|
|
38
|
+
FieldExpression<Key, Name>,
|
|
39
|
+
FieldSpecScalarType<Fields[Name]>,
|
|
40
|
+
SourceFieldNullability<Key, FieldSpecIsNullable<Fields[Name]>>
|
|
41
|
+
>
|
|
27
42
|
export function field<const Source extends string, const Name extends string>(
|
|
28
43
|
source: Source,
|
|
29
44
|
name: Name,
|
|
30
|
-
): FieldExpression<Source, Name
|
|
45
|
+
): TypedExpression<FieldExpression<Source, Name>, ScalarType, SourceFieldNullability<Source, boolean>>
|
|
31
46
|
|
|
32
|
-
export interface RelationOptions
|
|
33
|
-
|
|
34
|
-
|
|
47
|
+
export interface RelationOptions<
|
|
48
|
+
Required extends boolean = boolean,
|
|
49
|
+
Cardinality extends RelationCardinality = RelationCardinality,
|
|
50
|
+
> {
|
|
51
|
+
required?: Required
|
|
52
|
+
cardinality?: Cardinality
|
|
35
53
|
selection?: RelationSelection
|
|
36
54
|
}
|
|
37
55
|
|
|
38
|
-
export function relation<
|
|
56
|
+
export function relation<
|
|
57
|
+
const Name extends string,
|
|
58
|
+
const From extends string | SourceRef,
|
|
59
|
+
const To extends string | SourceRef,
|
|
60
|
+
const Required extends boolean = false,
|
|
61
|
+
const Cardinality extends RelationCardinality = 'one',
|
|
62
|
+
>(
|
|
39
63
|
name: Name,
|
|
40
|
-
from:
|
|
41
|
-
to:
|
|
64
|
+
from: From,
|
|
65
|
+
to: To,
|
|
42
66
|
on: Expression,
|
|
43
|
-
options?: RelationOptions,
|
|
44
|
-
): RelationRef<Name>
|
|
67
|
+
options?: RelationOptions<Required, Cardinality>,
|
|
68
|
+
): RelationRef<Name, SourceReferenceKey<From>, SourceReferenceKey<To>, Required, Cardinality>
|
|
45
69
|
|
|
46
70
|
export interface ConstraintOptions {
|
|
47
71
|
when?: string | ParameterRef
|
|
@@ -49,39 +73,93 @@ export interface ConstraintOptions {
|
|
|
49
73
|
|
|
50
74
|
export function constraint(predicate: Expression, options?: ConstraintOptions): ConstraintDefinition
|
|
51
75
|
|
|
52
|
-
export interface ProjectionOptions {
|
|
53
|
-
default?:
|
|
76
|
+
export interface ProjectionOptions<SelectedByDefault extends boolean = boolean> {
|
|
77
|
+
default?: SelectedByDefault
|
|
54
78
|
}
|
|
55
79
|
|
|
56
|
-
export function project<
|
|
80
|
+
export function project<
|
|
81
|
+
const Path extends string,
|
|
82
|
+
const Value extends ExpressionInput,
|
|
83
|
+
const SelectedByDefault extends boolean = false,
|
|
84
|
+
>(
|
|
57
85
|
path: Path,
|
|
58
|
-
expression:
|
|
59
|
-
options?: ProjectionOptions
|
|
60
|
-
):
|
|
61
|
-
|
|
86
|
+
expression: Value,
|
|
87
|
+
options?: ProjectionOptions<SelectedByDefault>,
|
|
88
|
+
): TypedProjectionField<
|
|
89
|
+
Path,
|
|
90
|
+
Extract<ExpressionScalarType<Value>, ScalarType>,
|
|
91
|
+
ExpressionNullability<Value>,
|
|
92
|
+
SelectedByDefault
|
|
93
|
+
>
|
|
94
|
+
export function project<
|
|
95
|
+
const Path extends readonly string[],
|
|
96
|
+
const Value extends ExpressionInput,
|
|
97
|
+
const SelectedByDefault extends boolean = false,
|
|
98
|
+
>(
|
|
62
99
|
path: Path,
|
|
63
|
-
expression:
|
|
64
|
-
options?: ProjectionOptions
|
|
65
|
-
):
|
|
100
|
+
expression: Value,
|
|
101
|
+
options?: ProjectionOptions<SelectedByDefault>,
|
|
102
|
+
): TypedProjectionField<
|
|
103
|
+
JoinProjectionPath<Path>,
|
|
104
|
+
Extract<ExpressionScalarType<Value>, ScalarType>,
|
|
105
|
+
ExpressionNullability<Value>,
|
|
106
|
+
SelectedByDefault
|
|
107
|
+
>
|
|
66
108
|
|
|
67
|
-
export function dimension<
|
|
109
|
+
export function dimension<
|
|
110
|
+
const Path extends string,
|
|
111
|
+
const Value extends ExpressionInput,
|
|
112
|
+
const SelectedByDefault extends boolean = false,
|
|
113
|
+
>(
|
|
68
114
|
path: Path,
|
|
69
|
-
expression:
|
|
70
|
-
options?: ProjectionOptions
|
|
71
|
-
):
|
|
72
|
-
|
|
115
|
+
expression: Value,
|
|
116
|
+
options?: ProjectionOptions<SelectedByDefault>,
|
|
117
|
+
): TypedDimensionDefinition<
|
|
118
|
+
Path,
|
|
119
|
+
Extract<ExpressionScalarType<Value>, ScalarType>,
|
|
120
|
+
ExpressionNullability<Value>,
|
|
121
|
+
SelectedByDefault
|
|
122
|
+
>
|
|
123
|
+
export function dimension<
|
|
124
|
+
const Path extends readonly string[],
|
|
125
|
+
const Value extends ExpressionInput,
|
|
126
|
+
const SelectedByDefault extends boolean = false,
|
|
127
|
+
>(
|
|
73
128
|
path: Path,
|
|
74
|
-
expression:
|
|
75
|
-
options?: ProjectionOptions
|
|
76
|
-
):
|
|
129
|
+
expression: Value,
|
|
130
|
+
options?: ProjectionOptions<SelectedByDefault>,
|
|
131
|
+
): TypedDimensionDefinition<
|
|
132
|
+
JoinProjectionPath<Path>,
|
|
133
|
+
Extract<ExpressionScalarType<Value>, ScalarType>,
|
|
134
|
+
ExpressionNullability<Value>,
|
|
135
|
+
SelectedByDefault
|
|
136
|
+
>
|
|
77
137
|
|
|
78
|
-
export function measure<
|
|
138
|
+
export function measure<
|
|
139
|
+
const Path extends string,
|
|
140
|
+
const Value extends ExpressionInput,
|
|
141
|
+
const SelectedByDefault extends boolean = false,
|
|
142
|
+
>(
|
|
79
143
|
path: Path,
|
|
80
|
-
expression:
|
|
81
|
-
options?: ProjectionOptions
|
|
82
|
-
):
|
|
83
|
-
|
|
144
|
+
expression: Value,
|
|
145
|
+
options?: ProjectionOptions<SelectedByDefault>,
|
|
146
|
+
): TypedMeasureDefinition<
|
|
147
|
+
Path,
|
|
148
|
+
Extract<ExpressionScalarType<Value>, ScalarType>,
|
|
149
|
+
ExpressionNullability<Value>,
|
|
150
|
+
SelectedByDefault
|
|
151
|
+
>
|
|
152
|
+
export function measure<
|
|
153
|
+
const Path extends readonly string[],
|
|
154
|
+
const Value extends ExpressionInput,
|
|
155
|
+
const SelectedByDefault extends boolean = false,
|
|
156
|
+
>(
|
|
84
157
|
path: Path,
|
|
85
|
-
expression:
|
|
86
|
-
options?: ProjectionOptions
|
|
87
|
-
):
|
|
158
|
+
expression: Value,
|
|
159
|
+
options?: ProjectionOptions<SelectedByDefault>,
|
|
160
|
+
): TypedMeasureDefinition<
|
|
161
|
+
JoinProjectionPath<Path>,
|
|
162
|
+
Extract<ExpressionScalarType<Value>, ScalarType>,
|
|
163
|
+
ExpressionNullability<Value>,
|
|
164
|
+
SelectedByDefault
|
|
165
|
+
>
|
package/definition.js
CHANGED
|
@@ -102,6 +102,9 @@ exports.relation = relation
|
|
|
102
102
|
exports.constraint = constraint
|
|
103
103
|
exports.ordering = objectDsl.ordering
|
|
104
104
|
exports.project = project
|
|
105
|
+
exports.batchQuery = objectDsl.batchQuery
|
|
106
|
+
exports.batchRelation = objectDsl.batchRelation
|
|
107
|
+
exports.composeGraph = objectDsl.composeGraph
|
|
105
108
|
exports.dimension = dimension
|
|
106
109
|
exports.measure = measure
|
|
107
110
|
exports.asc = objectDsl.asc
|