@contember/engine-content-api 1.2.0-alpha.3 → 1.2.0-alpha.6

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.
Files changed (164) hide show
  1. package/dist/src/acl/EvaluatedPredicateReplacer.d.ts +10 -0
  2. package/dist/src/acl/EvaluatedPredicateReplacer.d.ts.map +1 -0
  3. package/dist/src/acl/EvaluatedPredicateReplacer.js +40 -0
  4. package/dist/src/acl/EvaluatedPredicateReplacer.js.map +1 -0
  5. package/dist/src/acl/PredicateFactory.d.ts +4 -2
  6. package/dist/src/acl/PredicateFactory.d.ts.map +1 -1
  7. package/dist/src/acl/PredicateFactory.js +24 -6
  8. package/dist/src/acl/PredicateFactory.js.map +1 -1
  9. package/dist/src/acl/PredicatesInjector.d.ts +1 -1
  10. package/dist/src/acl/PredicatesInjector.d.ts.map +1 -1
  11. package/dist/src/acl/PredicatesInjector.js +14 -11
  12. package/dist/src/acl/PredicatesInjector.js.map +1 -1
  13. package/dist/src/acl/VariableInjector.d.ts +0 -1
  14. package/dist/src/acl/VariableInjector.d.ts.map +1 -1
  15. package/dist/src/acl/VariableInjector.js +4 -16
  16. package/dist/src/acl/VariableInjector.js.map +1 -1
  17. package/dist/src/acl/VariableUtils.d.ts +2 -13
  18. package/dist/src/acl/VariableUtils.d.ts.map +1 -1
  19. package/dist/src/acl/VariableUtils.js +6 -23
  20. package/dist/src/acl/VariableUtils.js.map +1 -1
  21. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducerExecutionHandler.js +1 -1
  22. package/dist/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducerExecutionHandler.js.map +1 -1
  23. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyCountVisitor.d.ts.map +1 -1
  24. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyCountVisitor.js +25 -13
  25. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyCountVisitor.js.map +1 -1
  26. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyNodesVisitor.d.ts.map +1 -1
  27. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyNodesVisitor.js +26 -13
  28. package/dist/src/extensions/paginatedHasMany/PaginatedHasManyNodesVisitor.js.map +1 -1
  29. package/dist/src/input-validation/EntityRulesResolver.js +3 -3
  30. package/dist/src/input-validation/EntityRulesResolver.js.map +1 -1
  31. package/dist/src/input-validation/ValidationContext.d.ts +2 -2
  32. package/dist/src/input-validation/ValidationContext.d.ts.map +1 -1
  33. package/dist/src/input-validation/ValidationContext.js.map +1 -1
  34. package/dist/src/input-validation/ValidationDataSelector.d.ts.map +1 -1
  35. package/dist/src/input-validation/ValidationDataSelector.js +1 -1
  36. package/dist/src/input-validation/ValidationDataSelector.js.map +1 -1
  37. package/dist/src/mapper/Mapper.d.ts +4 -4
  38. package/dist/src/mapper/Mapper.d.ts.map +1 -1
  39. package/dist/src/mapper/Mapper.js +7 -7
  40. package/dist/src/mapper/Mapper.js.map +1 -1
  41. package/dist/src/mapper/MapperContainer.d.ts.map +1 -1
  42. package/dist/src/mapper/MapperContainer.js +5 -2
  43. package/dist/src/mapper/MapperContainer.js.map +1 -1
  44. package/dist/src/mapper/select/RelationFetcher.d.ts +40 -5
  45. package/dist/src/mapper/select/RelationFetcher.d.ts.map +1 -1
  46. package/dist/src/mapper/select/RelationFetcher.js +19 -6
  47. package/dist/src/mapper/select/RelationFetcher.js.map +1 -1
  48. package/dist/src/mapper/select/WhereBuilder.d.ts +3 -1
  49. package/dist/src/mapper/select/WhereBuilder.d.ts.map +1 -1
  50. package/dist/src/mapper/select/WhereBuilder.js +4 -2
  51. package/dist/src/mapper/select/WhereBuilder.js.map +1 -1
  52. package/dist/src/mapper/select/handlers/FieldsVisitor.d.ts +2 -2
  53. package/dist/src/mapper/select/handlers/FieldsVisitor.d.ts.map +1 -1
  54. package/dist/src/mapper/select/handlers/FieldsVisitor.js +31 -18
  55. package/dist/src/mapper/select/handlers/FieldsVisitor.js.map +1 -1
  56. package/dist/src/mapper/select/optimizer/ConditionOptimizer.d.ts +5 -0
  57. package/dist/src/mapper/select/optimizer/ConditionOptimizer.d.ts.map +1 -0
  58. package/dist/src/mapper/select/optimizer/ConditionOptimizer.js +33 -0
  59. package/dist/src/mapper/select/optimizer/ConditionOptimizer.js.map +1 -0
  60. package/dist/src/mapper/select/optimizer/WhereOptimizer.d.ts +11 -0
  61. package/dist/src/mapper/select/optimizer/WhereOptimizer.d.ts.map +1 -0
  62. package/dist/src/mapper/select/optimizer/WhereOptimizer.js +57 -0
  63. package/dist/src/mapper/select/optimizer/WhereOptimizer.js.map +1 -0
  64. package/dist/src/mapper/select/optimizer/helpers.d.ts +10 -0
  65. package/dist/src/mapper/select/optimizer/helpers.d.ts.map +1 -0
  66. package/dist/src/mapper/select/optimizer/helpers.js +65 -0
  67. package/dist/src/mapper/select/optimizer/helpers.js.map +1 -0
  68. package/dist/src/resolvers/MutationResolver.js +1 -1
  69. package/dist/src/resolvers/MutationResolver.js.map +1 -1
  70. package/dist/src/resolvers/ReadHelpers.js +3 -3
  71. package/dist/src/resolvers/ReadHelpers.js.map +1 -1
  72. package/dist/src/resolvers/ReadResolver.js +2 -2
  73. package/dist/src/resolvers/ReadResolver.js.map +1 -1
  74. package/dist/src/tsconfig.tsbuildinfo +1 -1
  75. package/dist/tests/cases/integration/graphQlDb/insert/insertSequence.test.d.ts +2 -0
  76. package/dist/tests/cases/integration/graphQlDb/insert/insertSequence.test.d.ts.map +1 -0
  77. package/dist/tests/cases/integration/graphQlDb/insert/insertSequence.test.js +38 -0
  78. package/dist/tests/cases/integration/graphQlDb/insert/insertSequence.test.js.map +1 -0
  79. package/dist/tests/cases/integration/graphQlRequests/delete/aclDelete.test.js +1 -6
  80. package/dist/tests/cases/integration/graphQlRequests/delete/aclDelete.test.js.map +1 -1
  81. package/dist/tests/cases/integration/graphQlRequests/insert/acl/setField.test.js +2 -12
  82. package/dist/tests/cases/integration/graphQlRequests/insert/acl/setField.test.js.map +1 -1
  83. package/dist/tests/cases/integration/graphQlRequests/insert/aclInsert.test.js +1 -6
  84. package/dist/tests/cases/integration/graphQlRequests/insert/aclInsert.test.js.map +1 -1
  85. package/dist/tests/cases/integration/graphQlRequests/query/acl/fieldRestrictedByLocale.test.js +7 -26
  86. package/dist/tests/cases/integration/graphQlRequests/query/acl/fieldRestrictedByLocale.test.js.map +1 -1
  87. package/dist/tests/cases/integration/graphQlRequests/query/acl/nestedPredicate.test.js +1 -6
  88. package/dist/tests/cases/integration/graphQlRequests/query/acl/nestedPredicate.test.js.map +1 -1
  89. package/dist/tests/cases/integration/graphQlRequests/query/acl/predicateElimination.test.d.ts +2 -0
  90. package/dist/tests/cases/integration/graphQlRequests/query/acl/predicateElimination.test.d.ts.map +1 -0
  91. package/dist/tests/cases/integration/graphQlRequests/query/acl/predicateElimination.test.js +137 -0
  92. package/dist/tests/cases/integration/graphQlRequests/query/acl/predicateElimination.test.js.map +1 -0
  93. package/dist/tests/cases/integration/graphQlRequests/query/filter/complexCondition.test.js +1 -1
  94. package/dist/tests/cases/integration/graphQlRequests/query/filter/composedFilter.test.js +1 -1
  95. package/dist/tests/cases/integration/graphQlRequests/query/filter/composedFilter.test.js.map +1 -1
  96. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyInverse.test.js +73 -0
  97. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyInverse.test.js.map +1 -1
  98. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.js +73 -0
  99. package/dist/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.js.map +1 -1
  100. package/dist/tests/cases/integration/graphQlRequests/update/acl/aclUpdate.test.js +6 -26
  101. package/dist/tests/cases/integration/graphQlRequests/update/acl/aclUpdate.test.js.map +1 -1
  102. package/dist/tests/cases/unit/aclVariablesFactory.test.js +4 -32
  103. package/dist/tests/cases/unit/aclVariablesFactory.test.js.map +1 -1
  104. package/dist/tests/cases/unit/conditionOptimizer.test.d.ts +2 -0
  105. package/dist/tests/cases/unit/conditionOptimizer.test.d.ts.map +1 -0
  106. package/dist/tests/cases/unit/conditionOptimizer.test.js +113 -0
  107. package/dist/tests/cases/unit/conditionOptimizer.test.js.map +1 -0
  108. package/dist/tests/cases/unit/orderbyHelper.test.js +1 -0
  109. package/dist/tests/cases/unit/orderbyHelper.test.js.map +1 -1
  110. package/dist/tests/cases/unit/predicatesFactory.test.d.ts +2 -0
  111. package/dist/tests/cases/unit/predicatesFactory.test.d.ts.map +1 -0
  112. package/dist/tests/cases/unit/predicatesFactory.test.js +103 -0
  113. package/dist/tests/cases/unit/predicatesFactory.test.js.map +1 -0
  114. package/dist/tests/cases/unit/predicatesInjector.test.js +9 -18
  115. package/dist/tests/cases/unit/predicatesInjector.test.js.map +1 -1
  116. package/dist/tests/cases/unit/variableInjector.test.js +2 -13
  117. package/dist/tests/cases/unit/variableInjector.test.js.map +1 -1
  118. package/dist/tests/cases/unit/whereOptimizer.test.d.ts +2 -0
  119. package/dist/tests/cases/unit/whereOptimizer.test.d.ts.map +1 -0
  120. package/dist/tests/cases/unit/whereOptimizer.test.js +81 -0
  121. package/dist/tests/cases/unit/whereOptimizer.test.js.map +1 -0
  122. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  123. package/package.json +10 -10
  124. package/src/acl/EvaluatedPredicateReplacer.ts +38 -0
  125. package/src/acl/PredicateFactory.ts +30 -7
  126. package/src/acl/PredicatesInjector.ts +18 -10
  127. package/src/acl/VariableInjector.ts +2 -17
  128. package/src/acl/VariableUtils.ts +7 -28
  129. package/src/extensions/hasManyToHasOneReducer/HasManyToHasOneReducerExecutionHandler.ts +3 -3
  130. package/src/extensions/paginatedHasMany/PaginatedHasManyCountVisitor.ts +26 -28
  131. package/src/extensions/paginatedHasMany/PaginatedHasManyNodesVisitor.ts +27 -31
  132. package/src/input-validation/EntityRulesResolver.ts +3 -3
  133. package/src/input-validation/ValidationContext.ts +2 -2
  134. package/src/input-validation/ValidationDataSelector.ts +2 -2
  135. package/src/mapper/Mapper.ts +10 -6
  136. package/src/mapper/MapperContainer.ts +7 -3
  137. package/src/mapper/select/RelationFetcher.ts +95 -34
  138. package/src/mapper/select/WhereBuilder.ts +5 -2
  139. package/src/mapper/select/handlers/FieldsVisitor.ts +31 -24
  140. package/src/mapper/select/optimizer/ConditionOptimizer.ts +35 -0
  141. package/src/mapper/select/optimizer/WhereOptimizer.ts +67 -0
  142. package/src/mapper/select/optimizer/helpers.ts +54 -0
  143. package/src/resolvers/MutationResolver.ts +1 -1
  144. package/src/resolvers/ReadHelpers.ts +3 -3
  145. package/src/resolvers/ReadResolver.ts +2 -2
  146. package/tests/cases/integration/graphQlDb/insert/insertSequence.test.ts +34 -0
  147. package/tests/cases/integration/graphQlRequests/delete/aclDelete.test.ts +1 -6
  148. package/tests/cases/integration/graphQlRequests/insert/acl/setField.test.ts +2 -12
  149. package/tests/cases/integration/graphQlRequests/insert/aclInsert.test.ts +1 -6
  150. package/tests/cases/integration/graphQlRequests/query/acl/fieldRestrictedByLocale.test.ts +7 -26
  151. package/tests/cases/integration/graphQlRequests/query/acl/nestedPredicate.test.ts +1 -6
  152. package/tests/cases/integration/graphQlRequests/query/acl/predicateElimination.test.ts +130 -0
  153. package/tests/cases/integration/graphQlRequests/query/filter/complexCondition.test.ts +1 -1
  154. package/tests/cases/integration/graphQlRequests/query/filter/composedFilter.test.ts +1 -1
  155. package/tests/cases/integration/graphQlRequests/query/relations/manyHasManyInverse.test.ts +74 -0
  156. package/tests/cases/integration/graphQlRequests/query/relations/manyHasManyOwning.test.ts +77 -0
  157. package/tests/cases/integration/graphQlRequests/update/acl/aclUpdate.test.ts +7 -27
  158. package/tests/cases/unit/aclVariablesFactory.test.ts +5 -34
  159. package/tests/cases/unit/conditionOptimizer.test.ts +129 -0
  160. package/tests/cases/unit/orderbyHelper.test.ts +1 -0
  161. package/tests/cases/unit/predicatesFactory.test.ts +97 -0
  162. package/tests/cases/unit/predicatesInjector.test.ts +9 -18
  163. package/tests/cases/unit/variableInjector.test.ts +2 -13
  164. package/tests/cases/unit/whereOptimizer.test.ts +77 -0
@@ -12,6 +12,52 @@ import { Mapper } from '../Mapper'
12
12
 
13
13
  export type GroupedCounts = Record<Input.PrimaryValue, number>
14
14
 
15
+ type CountOneHasManyGroupsArgs = {
16
+ mapper: Mapper
17
+ filter: Input.OptionalWhere | undefined
18
+ targetEntity: Model.Entity
19
+ targetRelation: Model.ManyHasOneRelation
20
+ ids: Input.PrimaryValue[]
21
+ }
22
+
23
+ type FetchOneHasManyGroupsArgs = {
24
+ mapper: Mapper
25
+ objectNode: ObjectNode<Input.ListQueryInput>
26
+ targetEntity: Model.Entity
27
+ relation: Model.OneHasManyRelation
28
+ targetRelation: Model.ManyHasOneRelation
29
+ ids: Input.PrimaryValue[]
30
+ }
31
+
32
+ type ManyHasManyBaseArgs =
33
+ & {
34
+ mapper: Mapper
35
+ targetEntity: Model.Entity
36
+ ids: Input.PrimaryValue[]
37
+ } & (
38
+ | {
39
+ directionFrom: 'owning'
40
+ sourceRelation: Model.ManyHasManyOwningRelation
41
+ targetRelation: Model.ManyHasManyInverseRelation | null
42
+ }
43
+ | {
44
+ directionFrom: 'inverse'
45
+ sourceRelation: Model.ManyHasManyInverseRelation
46
+ targetRelation: Model.ManyHasManyOwningRelation
47
+ }
48
+ )
49
+ type CountManyHasManyGroupArgs =
50
+ & ManyHasManyBaseArgs
51
+ & {
52
+ filter: Input.OptionalWhere | undefined
53
+ }
54
+
55
+ type FetchManyHasManyGroupsArgs =
56
+ & ManyHasManyBaseArgs
57
+ & {
58
+ field: ObjectNode<Input.ListQueryInput>
59
+ }
60
+
15
61
  export class RelationFetcher {
16
62
  constructor(
17
63
  private readonly whereBuilder: WhereBuilder,
@@ -20,25 +66,25 @@ export class RelationFetcher {
20
66
  private readonly pathFactory: PathFactory,
21
67
  ) {}
22
68
 
23
- public async countOneHasManyGroups(
24
- mapper: Mapper,
25
- filter: Input.OptionalWhere | undefined,
26
- targetEntity: Model.Entity,
27
- targetRelation: Model.ManyHasOneRelation,
28
- ids: Input.PrimaryValue[],
29
- ): Promise<GroupedCounts> {
69
+ public async countOneHasManyGroups({
70
+ mapper,
71
+ filter,
72
+ targetEntity,
73
+ targetRelation,
74
+ ids,
75
+ }: CountOneHasManyGroupsArgs): Promise<GroupedCounts> {
30
76
  const filterWithParent = this.createOneHasManyFilter(targetEntity, targetRelation, ids, filter)
31
77
  return mapper.countGrouped(targetEntity, filterWithParent, targetRelation)
32
78
  }
33
79
 
34
- public async fetchOneHasManyGroups(
35
- mapper: Mapper,
36
- objectNode: ObjectNode<Input.ListQueryInput>,
37
- targetEntity: Model.Entity,
38
- relation: Model.OneHasManyRelation,
39
- targetRelation: Model.ManyHasOneRelation,
40
- ids: Input.PrimaryValue[],
41
- ): Promise<SelectGroupedObjects> {
80
+ public async fetchOneHasManyGroups({
81
+ mapper,
82
+ objectNode,
83
+ targetEntity,
84
+ relation,
85
+ targetRelation,
86
+ ids,
87
+ }: FetchOneHasManyGroupsArgs): Promise<SelectGroupedObjects> {
42
88
  const filter = this.createOneHasManyFilter(targetEntity, targetRelation, ids, objectNode.args.filter)
43
89
  const objectNodeWithWhere = objectNode.withArg<Input.ListQueryInput>('filter', filter)
44
90
  const objectNodeWithOrder = OrderByHelper.appendDefaultOrderBy(
@@ -65,14 +111,17 @@ export class RelationFetcher {
65
111
  }
66
112
  }
67
113
 
68
- public async countManyHasManyGroups(
69
- mapper: Mapper,
70
- filter: Input.OptionalWhere | undefined,
71
- targetEntity: Model.Entity,
72
- owningRelation: Model.ManyHasManyOwningRelation,
73
- ids: Input.PrimaryValue[],
74
- joiningColumns: JoiningColumns,
75
- ): Promise<GroupedCounts> {
114
+ public async countManyHasManyGroups({
115
+ mapper,
116
+ filter,
117
+ targetEntity,
118
+ directionFrom,
119
+ sourceRelation,
120
+ targetRelation,
121
+ ids,
122
+ }: CountManyHasManyGroupArgs): Promise<GroupedCounts> {
123
+ const owningRelation = directionFrom === 'owning' ? sourceRelation : targetRelation
124
+ const joiningColumns = this.resolveJoiningColumns({ directionFrom, owningRelation })
76
125
  const qb = this.buildJunctionQb(owningRelation, ids, joiningColumns, targetEntity, { filter })
77
126
  .select(['junction_', joiningColumns.sourceColumn.columnName])
78
127
  .select(expr => expr.raw('count(*)'), 'row_count')
@@ -85,16 +134,18 @@ export class RelationFetcher {
85
134
  return Object.fromEntries(result)
86
135
  }
87
136
 
88
- public async fetchManyHasManyGroups(
89
- mapper: Mapper,
90
- field: ObjectNode<Input.ListQueryInput>,
91
- targetEntity: Model.Entity,
92
- fromRelation: Model.ManyHasManyOwningRelation | Model.ManyHasManyInverseRelation,
93
- owningRelation: Model.ManyHasManyOwningRelation,
94
- joiningColumns: JoiningColumns,
95
- ids: Input.PrimaryValue[],
96
- ): Promise<SelectGroupedObjects> {
97
- const defaultOrderBy = fromRelation.orderBy || []
137
+ public async fetchManyHasManyGroups({
138
+ mapper,
139
+ field,
140
+ sourceRelation,
141
+ targetEntity,
142
+ targetRelation,
143
+ directionFrom,
144
+ ids,
145
+ }: FetchManyHasManyGroupsArgs): Promise<SelectGroupedObjects> {
146
+ const owningRelation = directionFrom === 'owning' ? sourceRelation : targetRelation
147
+ const joiningColumns = this.resolveJoiningColumns({ directionFrom, owningRelation })
148
+ const defaultOrderBy = sourceRelation.orderBy || []
98
149
  const objectNode = OrderByHelper.appendDefaultOrderBy(targetEntity, field, defaultOrderBy)
99
150
 
100
151
  const junctionValues = await this.fetchJunction(
@@ -122,11 +173,21 @@ export class RelationFetcher {
122
173
  },
123
174
  })
124
175
  .withField(primaryField)
125
- const result = await mapper.select(targetEntity, queryWithWhere)
176
+ const result = await mapper.select(targetEntity, queryWithWhere, targetRelation)
126
177
 
127
178
  return this.buildManyHasManyGroups(targetEntity, joiningColumns, result, junctionValues)
128
179
  }
129
180
 
181
+ private resolveJoiningColumns({ directionFrom, owningRelation }: { owningRelation: Model.ManyHasManyOwningRelation; directionFrom: 'owning' | 'inverse' }): JoiningColumns {
182
+ return directionFrom === 'owning' ? {
183
+ sourceColumn: owningRelation.joiningTable.joiningColumn,
184
+ targetColumn: owningRelation.joiningTable.inverseJoiningColumn,
185
+ } : {
186
+ sourceColumn: owningRelation.joiningTable.inverseJoiningColumn,
187
+ targetColumn: owningRelation.joiningTable.joiningColumn,
188
+ }
189
+ }
190
+
130
191
  private buildManyHasManyGroups(
131
192
  entity: Model.Entity,
132
193
  joiningColumns: JoiningColumns,
@@ -5,6 +5,7 @@ import { Path, PathFactory } from './Path'
5
5
  import { JoinBuilder } from './JoinBuilder'
6
6
  import { ConditionBuilder } from './ConditionBuilder'
7
7
  import { ConditionBuilder as SqlConditionBuilder, Operator, QueryBuilder, SelectBuilder } from '@contember/database'
8
+ import { WhereOptimizer } from './optimizer/WhereOptimizer'
8
9
 
9
10
  export class WhereBuilder {
10
11
  constructor(
@@ -12,6 +13,7 @@ export class WhereBuilder {
12
13
  private readonly joinBuilder: JoinBuilder,
13
14
  private readonly conditionBuilder: ConditionBuilder,
14
15
  private readonly pathFactory: PathFactory,
16
+ private readonly whereOptimizer: WhereOptimizer,
15
17
  ) {}
16
18
 
17
19
  public build(
@@ -31,9 +33,10 @@ export class WhereBuilder {
31
33
  callback: (clauseCb: (clause: SqlConditionBuilder) => SqlConditionBuilder) => SelectBuilder<SelectBuilder.Result>,
32
34
  allowManyJoin: boolean = false,
33
35
  ) {
36
+ const optimizedWhere = this.whereOptimizer.optimize(where, entity)
34
37
  const joinList: WhereJoinDefinition[] = []
35
38
 
36
- const qbWithWhere = callback(clause => this.buildInternal(clause, entity, path, where, allowManyJoin, joinList))
39
+ const qbWithWhere = callback(clause => this.buildInternal(clause, entity, path, optimizedWhere, allowManyJoin, joinList))
37
40
  return joinList.reduce<SelectBuilder<SelectBuilder.Result>>(
38
41
  (qb, { path, entity, relationName }) => this.joinBuilder.join(qb, path, entity, relationName),
39
42
  qbWithWhere,
@@ -44,7 +47,7 @@ export class WhereBuilder {
44
47
  conditionBuilder: SqlConditionBuilder,
45
48
  entity: Model.Entity,
46
49
  path: Path,
47
- where: Input.OptionalWhere,
50
+ where: Input.Where,
48
51
  allowManyJoin: boolean,
49
52
  joinList: WhereJoinDefinition[],
50
53
  ): SqlConditionBuilder {
@@ -4,7 +4,6 @@ import { RelationFetcher } from '../RelationFetcher'
4
4
  import { SelectExecutionHandlerContext } from '../SelectExecutionHandler'
5
5
  import { PredicateFactory } from '../../../acl'
6
6
  import { WhereBuilder } from '../WhereBuilder'
7
- import { JoiningColumns } from '../../types'
8
7
 
9
8
  export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.ColumnVisitor<void> {
10
9
  constructor(
@@ -56,11 +55,6 @@ export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.C
56
55
  targetEntity: Model.Entity,
57
56
  targetRelation: Model.ManyHasManyOwningRelation,
58
57
  ): void {
59
- const joiningTable = targetRelation.joiningTable
60
- const columns: JoiningColumns = {
61
- sourceColumn: joiningTable.inverseJoiningColumn,
62
- targetColumn: joiningTable.joiningColumn,
63
- }
64
58
  const field = this.executionContext.objectNode
65
59
  if (!field) {
66
60
  throw new Error()
@@ -69,13 +63,15 @@ export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.C
69
63
  entity.primary,
70
64
  async ids =>
71
65
  this.relationFetcher.fetchManyHasManyGroups(
72
- this.mapper,
73
- field,
74
- targetEntity,
75
- relation,
76
- targetRelation,
77
- columns,
78
- ids,
66
+ {
67
+ mapper: this.mapper,
68
+ field: field,
69
+ targetEntity: targetEntity,
70
+ sourceRelation: relation,
71
+ targetRelation,
72
+ directionFrom: 'inverse',
73
+ ids: ids,
74
+ },
79
75
  ),
80
76
  [],
81
77
  )
@@ -85,12 +81,8 @@ export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.C
85
81
  entity: Model.Entity,
86
82
  relation: Model.ManyHasManyOwningRelation,
87
83
  targetEntity: Model.Entity,
84
+ targetRelation: Model.ManyHasManyInverseRelation | null,
88
85
  ): void {
89
- const joiningTable = relation.joiningTable
90
- const columns: JoiningColumns = {
91
- sourceColumn: joiningTable.joiningColumn,
92
- targetColumn: joiningTable.inverseJoiningColumn,
93
- }
94
86
  const field = this.executionContext.objectNode
95
87
  if (!field) {
96
88
  throw new Error()
@@ -99,7 +91,15 @@ export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.C
99
91
  this.executionContext.addData(
100
92
  entity.primary,
101
93
  async ids =>
102
- this.relationFetcher.fetchManyHasManyGroups(this.mapper, field, targetEntity, relation, relation, columns, ids),
94
+ this.relationFetcher.fetchManyHasManyGroups({
95
+ mapper: this.mapper,
96
+ field: field,
97
+ targetEntity: targetEntity,
98
+ sourceRelation: relation,
99
+ targetRelation,
100
+ directionFrom: 'owning',
101
+ ids: ids,
102
+ }),
103
103
  [],
104
104
  )
105
105
  }
@@ -118,7 +118,14 @@ export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.C
118
118
  this.executionContext.addData(
119
119
  entity.primary,
120
120
  async ids =>
121
- this.relationFetcher.fetchOneHasManyGroups(this.mapper, field, targetEntity, relation, targetRelation, ids),
121
+ this.relationFetcher.fetchOneHasManyGroups({
122
+ mapper: this.mapper,
123
+ objectNode: field,
124
+ targetEntity: targetEntity,
125
+ relation: relation,
126
+ targetRelation: targetRelation,
127
+ ids: ids,
128
+ }),
122
129
  [],
123
130
  )
124
131
  }
@@ -148,7 +155,7 @@ export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.C
148
155
  }
149
156
  const objectWithWhere = field.withArg('filter', where)
150
157
 
151
- return this.mapper.select(targetEntity, objectWithWhere, targetRelation.name)
158
+ return this.mapper.select(targetEntity, objectWithWhere, targetRelation, targetRelation.name)
152
159
  },
153
160
  null,
154
161
  )
@@ -177,13 +184,13 @@ export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.C
177
184
  }
178
185
  const objectWithWhere = objectNode.withArg('filter', where)
179
186
 
180
- return this.mapper.select(targetEntity, objectWithWhere, targetEntity.primary)
187
+ return this.mapper.select(targetEntity, objectWithWhere, targetRelation, targetEntity.primary)
181
188
  },
182
189
  null,
183
190
  )
184
191
  }
185
192
 
186
- public visitManyHasOne(entity: Model.Entity, relation: Model.ManyHasOneRelation, targetEntity: Model.Entity): void {
193
+ public visitManyHasOne(entity: Model.Entity, relation: Model.ManyHasOneRelation, targetEntity: Model.Entity, targetRelation: Model.OneHasManyRelation | null): void {
187
194
  this.executionContext.addData(
188
195
  relation.name,
189
196
  async ids => {
@@ -201,7 +208,7 @@ export class FieldsVisitor implements Model.RelationByTypeVisitor<void>, Model.C
201
208
  }
202
209
  const objectWithWhere = objectNode.withArg('filter', where)
203
210
 
204
- return this.mapper.select(targetEntity, objectWithWhere, targetEntity.primary)
211
+ return this.mapper.select(targetEntity, objectWithWhere, targetRelation, targetEntity.primary)
205
212
  },
206
213
  null,
207
214
  )
@@ -0,0 +1,35 @@
1
+ import { Input } from '@contember/schema'
2
+ import { optimizeAnd, optimizeNot, optimizeOr } from './helpers'
3
+
4
+ type ObjectEntry<T> = { [K in keyof Required<T>]: [K, T[K]] }[keyof T]
5
+
6
+ export class ConditionOptimizer {
7
+ public optimize(condition: Input.Condition): Omit<Input.Condition, 'always' | 'never'> | boolean {
8
+ if (condition.never) {
9
+ return false
10
+ }
11
+
12
+ return optimizeAnd(
13
+ (Object.entries(condition) as ObjectEntry<typeof condition>[]).map(([key, value]) => {
14
+ if (value === undefined || value === null) {
15
+ return undefined
16
+
17
+ } else if (key === 'always') {
18
+ return true
19
+
20
+ } else if (key === 'and') {
21
+ return optimizeAnd(value.map(it => this.optimize(it)))
22
+
23
+ } else if (key === 'or') {
24
+ return optimizeOr(value.map(it => this.optimize(it)))
25
+
26
+ } else if (key === 'not') {
27
+ return optimizeNot(this.optimize(value))
28
+
29
+ } else {
30
+ return { [key]: value }
31
+ }
32
+ }),
33
+ )
34
+ }
35
+ }
@@ -0,0 +1,67 @@
1
+ import { Input, Model } from '@contember/schema'
2
+ import { ConditionOptimizer } from './ConditionOptimizer'
3
+ import { acceptFieldVisitor } from '@contember/schema-utils'
4
+ import { optimizeAnd, optimizeNot, optimizeOr } from './helpers'
5
+
6
+ export class WhereOptimizer {
7
+ constructor(
8
+ private readonly model: Model.Schema,
9
+ private readonly conditionOptimizer: ConditionOptimizer,
10
+ ) {
11
+ }
12
+
13
+ public optimize(where: Input.OptionalWhere, entity: Model.Entity): Input.Where {
14
+ const result = this.optimizeWhere(where, entity)
15
+
16
+ if (typeof result === 'boolean') {
17
+ return { [entity.primary]: { [result ? 'always' : 'never']: true } }
18
+ }
19
+
20
+ return result
21
+ }
22
+
23
+ private optimizeWhere(where: Input.OptionalWhere, entity: Model.Entity): Input.Where | boolean {
24
+ return optimizeAnd(
25
+ Object.entries(where).map(([key, value]) => {
26
+ if (value === undefined || value === null) {
27
+ return undefined
28
+
29
+ } else if (key === 'and') {
30
+ return optimizeAnd((value as readonly Input.Where[]).map(it => this.optimizeWhere(it, entity)))
31
+
32
+ } else if (key === 'or') {
33
+ return optimizeOr((value as readonly Input.Where[]).map(it => this.optimizeWhere(it, entity)))
34
+
35
+ } else if (key === 'not') {
36
+ return optimizeNot(this.optimizeWhere(value as Input.Where, entity))
37
+
38
+ } else {
39
+ return this.resolveFieldValue(entity, key, value)
40
+ }
41
+ }),
42
+ )
43
+ }
44
+
45
+ private resolveFieldValue(entity: Model.Entity, key: string, value: Input.OptionalWhere[string]) {
46
+ return acceptFieldVisitor<Input.Where | boolean>(this.model, entity, key, {
47
+ visitColumn: () => {
48
+ const optimizedCondition = this.conditionOptimizer.optimize(value as Input.Condition)
49
+
50
+ if (typeof optimizedCondition === 'boolean') {
51
+ return optimizedCondition
52
+ }
53
+
54
+ return { [key]: optimizedCondition }
55
+ },
56
+ visitRelation: (entity, relation, targetEntity) => {
57
+ const optimizedWhere = this.optimizeWhere(value as Input.Where, targetEntity)
58
+
59
+ if (typeof optimizedWhere === 'boolean') {
60
+ return optimizedWhere
61
+ }
62
+
63
+ return { [key]: optimizedWhere }
64
+ },
65
+ })
66
+ }
67
+ }
@@ -0,0 +1,54 @@
1
+ export const optimizeOr = <P extends Record<string, unknown>>(operands: readonly (P | boolean)[]): P | { or?: P[] } | boolean => {
2
+ const normalized: P[] = []
3
+
4
+ for (const operand of operands) {
5
+ if (operand === true) {
6
+ return true
7
+ } else if (operand !== false) {
8
+ normalized.push(...Array.isArray(operand.or) ? operand.or : [operand])
9
+ }
10
+ }
11
+
12
+ if (normalized.length > 1) {
13
+ return { or: normalized }
14
+ } else if (normalized.length === 1) {
15
+ return normalized[0]
16
+ } else {
17
+ return {}
18
+ }
19
+ }
20
+
21
+ export const optimizeAnd = <P extends Record<string, unknown>>(operands: readonly (P | boolean | undefined)[]): P | { and?: P[] } | boolean => {
22
+ const normalized: P[] = []
23
+ let hasAlways = false
24
+
25
+ for (const operand of operands) {
26
+ if (operand === true) {
27
+ hasAlways = true
28
+ } else if (operand === false) {
29
+ return false
30
+ } else if (operand !== undefined) {
31
+ normalized.push(...Array.isArray(operand.and) ? operand.and : [operand])
32
+ }
33
+ }
34
+
35
+ if (normalized.length > 1) {
36
+ return { and: normalized }
37
+ } else if (normalized.length === 1) {
38
+ return normalized[0]
39
+ } else if (hasAlways) {
40
+ return true
41
+ } else {
42
+ return {}
43
+ }
44
+ }
45
+
46
+ export const optimizeNot = <P extends Record<string, unknown>>(operand: P | boolean): P['not'] | { not: P } | boolean => {
47
+ if (typeof operand === 'boolean') {
48
+ return !operand
49
+ } else if (operand.not) {
50
+ return operand.not as P['not']
51
+ } else {
52
+ return { not: operand }
53
+ }
54
+ }
@@ -457,7 +457,7 @@ export class MutationResolver {
457
457
  throw new Error('MutationResolver: expected ObjectNode')
458
458
  }
459
459
  const objectWithArgs = singleNodeQuery.withArgs({ by: where })
460
- nodes[singleNodeQuery.alias] = await mapper.selectUnique(entity, objectWithArgs)
460
+ nodes[singleNodeQuery.alias] = await mapper.selectUnique(entity, objectWithArgs, null)
461
461
  }
462
462
  return nodes
463
463
  }
@@ -15,7 +15,7 @@ export const paginate = async (
15
15
  const totalCount = paginationHelper.requiresTotalCount
16
16
  ? await mapper.count(entity, queryAst.args.filter || {})
17
17
  : undefined
18
- const nodes = paginationHelper.nodeField ? await mapper.select(entity, paginationHelper.nodeField) : undefined
18
+ const nodes = paginationHelper.nodeField ? await mapper.select(entity, paginationHelper.nodeField, null) : undefined
19
19
 
20
20
  return paginationHelper.createResponse(totalCount, nodes)
21
21
  }
@@ -37,9 +37,9 @@ export const executeReadOperations = async (
37
37
  trxResult[field.alias] = await (() => {
38
38
  switch (meta.operation) {
39
39
  case Operation.get:
40
- return mapper.selectUnique(meta.entity, field)
40
+ return mapper.selectUnique(meta.entity, field, null)
41
41
  case Operation.list:
42
- return mapper.select(meta.entity, field)
42
+ return mapper.select(meta.entity, field, null)
43
43
  case Operation.paginate:
44
44
  return paginate(mapper, meta.entity, field)
45
45
  case Operation.create:
@@ -33,12 +33,12 @@ export class ReadResolver {
33
33
 
34
34
  public async resolveListQuery(entity: Model.Entity, info: GraphQLResolveInfo) {
35
35
  const queryAst: ObjectNode<Input.ListQueryInput> = this.queryAstFactory.create(info)
36
- return await this.mapperFactory(this.db).select(entity, queryAst)
36
+ return await this.mapperFactory(this.db).select(entity, queryAst, null)
37
37
  }
38
38
 
39
39
  public async resolveGetQuery(entity: Model.Entity, info: GraphQLResolveInfo) {
40
40
  const queryAst: ObjectNode<Input.UniqueQueryInput> = this.queryAstFactory.create(info)
41
- return await this.mapperFactory(this.db).selectUnique(entity, queryAst)
41
+ return await this.mapperFactory(this.db).selectUnique(entity, queryAst, null)
42
42
  }
43
43
 
44
44
  public async resolvePaginationQuery(entity: Model.Entity, info: GraphQLResolveInfo) {
@@ -0,0 +1,34 @@
1
+ import { SchemaDefinition as def } from '@contember/schema-definition'
2
+ import { GQL } from '../../../../src/tags'
3
+ import { test } from 'vitest'
4
+ import { executeDbTest } from '@contember/engine-api-tester'
5
+
6
+ namespace SeqModel {
7
+ export class Order {
8
+ seqId = def.intColumn().sequence().notNull()
9
+ }
10
+ }
11
+
12
+ test('insert author with id', async () => {
13
+ const schema = def.createModel(SeqModel)
14
+ await executeDbTest({
15
+ schema: schema,
16
+ query: GQL`
17
+ mutation {
18
+ createOrder(data: {}) {
19
+ node {
20
+ seqId
21
+ }
22
+ }
23
+ }`,
24
+ seed: [],
25
+ return: {
26
+ createOrder: {
27
+ node: {
28
+ seqId: 1,
29
+ },
30
+ },
31
+ },
32
+ })
33
+ })
34
+
@@ -32,12 +32,7 @@ test('delete post with acl', async () => {
32
32
  },
33
33
  },
34
34
  variables: {
35
- locale_variable: {
36
- definition: {
37
- type: Acl.VariableType.condition,
38
- },
39
- value: [{ in: ['cs'] }],
40
- },
35
+ locale_variable: { in: ['cs'] },
41
36
  },
42
37
  executes: [
43
38
  ...sqlTransaction([
@@ -34,12 +34,7 @@ test('set a name - allowed', async () => {
34
34
  },
35
35
  },
36
36
  variables: {
37
- name_variable: {
38
- definition: {
39
- type: Acl.VariableType.condition,
40
- },
41
- value: [{ in: ['John', 'Jack'] }],
42
- },
37
+ name_variable: { in: ['John', 'Jack'] },
43
38
  },
44
39
  executes: [
45
40
  ...sqlTransaction([
@@ -94,12 +89,7 @@ test('set a name - denied', async () => {
94
89
  },
95
90
  },
96
91
  variables: {
97
- name_variable: {
98
- definition: {
99
- type: Acl.VariableType.condition,
100
- },
101
- value: [{ in: ['John', 'Jack'] }],
102
- },
92
+ name_variable: { in: ['John', 'Jack'] },
103
93
  },
104
94
  executes: [
105
95
  ...failedTransaction([
@@ -27,12 +27,7 @@ test('insert author (with acl)', async () => {
27
27
  },
28
28
  },
29
29
  variables: {
30
- name_variable: {
31
- definition: {
32
- type: Acl.VariableType.condition,
33
- },
34
- value: [{ in: ['John', 'Jack'] }],
35
- },
30
+ name_variable: { in: ['John', 'Jack'] },
36
31
  },
37
32
  query: GQL`
38
33
  mutation {