@contember/engine-content-api 1.2.0-alpha.11 → 1.2.0-alpha.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contember/engine-content-api",
3
- "version": "1.2.0-alpha.11",
3
+ "version": "1.2.0-alpha.12",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/src/index.js",
6
6
  "typings": "dist/src/index.d.ts",
@@ -10,19 +10,19 @@
10
10
  "test": "vitest --no-threads"
11
11
  },
12
12
  "dependencies": {
13
- "@contember/database": "^1.2.0-alpha.11",
14
- "@contember/dic": "^1.2.0-alpha.11",
15
- "@contember/engine-common": "^1.2.0-alpha.11",
16
- "@contember/graphql-utils": "^1.2.0-alpha.11",
17
- "@contember/schema": "^1.2.0-alpha.11",
18
- "@contember/schema-utils": "^1.2.0-alpha.11",
13
+ "@contember/database": "^1.2.0-alpha.12",
14
+ "@contember/dic": "^1.2.0-alpha.12",
15
+ "@contember/engine-common": "^1.2.0-alpha.12",
16
+ "@contember/graphql-utils": "^1.2.0-alpha.12",
17
+ "@contember/schema": "^1.2.0-alpha.12",
18
+ "@contember/schema-utils": "^1.2.0-alpha.12",
19
19
  "@graphql-tools/schema": "^8.3.5",
20
20
  "graphql-tag": "^2.12.5"
21
21
  },
22
22
  "devDependencies": {
23
- "@contember/database-tester": "^1.2.0-alpha.11",
24
- "@contember/engine-api-tester": "^1.2.0-alpha.11",
25
- "@contember/schema-definition": "^1.2.0-alpha.11",
23
+ "@contember/database-tester": "^1.2.0-alpha.12",
24
+ "@contember/engine-api-tester": "^1.2.0-alpha.12",
25
+ "@contember/schema-definition": "^1.2.0-alpha.12",
26
26
  "@graphql-codegen/cli": "^2.6.2",
27
27
  "@graphql-codegen/typescript": "^2.5.1",
28
28
  "@graphql-codegen/typescript-operations": "^2.4.2",
@@ -1,10 +1,16 @@
1
1
  import { isIt } from '../../utils'
2
- import { acceptFieldVisitor } from '@contember/schema-utils'
2
+ import { acceptFieldVisitor, isColumn } from '@contember/schema-utils'
3
3
  import { Input, Model } from '@contember/schema'
4
4
  import { Path, PathFactory } from './Path'
5
5
  import { JoinBuilder } from './JoinBuilder'
6
6
  import { ConditionBuilder } from './ConditionBuilder'
7
- import { ConditionBuilder as SqlConditionBuilder, Operator, QueryBuilder, SelectBuilder } from '@contember/database'
7
+ import {
8
+ ConditionBuilder as SqlConditionBuilder,
9
+ Literal,
10
+ Operator,
11
+ QueryBuilder,
12
+ SelectBuilder, wrapIdentifier,
13
+ } from '@contember/database'
8
14
  import { WhereOptimizer } from './optimizer/WhereOptimizer'
9
15
 
10
16
  export class WhereBuilder {
@@ -172,10 +178,17 @@ export class WhereBuilder {
172
178
 
173
179
  const relationWhere = where[fieldName] as Input.Where
174
180
 
175
- const qb = SelectBuilder.create()
176
- .select(it => it.raw('1'))
177
- .from(context.targetEntity.tableName, 'sub_')
178
- .where(it => it.columnsEq([tableName, entity.primaryColumn], ['sub_', context.targetRelation.joiningColumn.columnName]))
181
+ const qb = this.hasRootIsNull(relationWhere, context.targetEntity)
182
+ ? SelectBuilder.create()
183
+ .select(it => it.raw('1'))
184
+ .from(new Literal(`(select ${wrapIdentifier(tableName)}.${wrapIdentifier(entity.primaryColumn)})`), 'sub2_')
185
+ .leftJoin(context.targetEntity.tableName, 'sub_', it =>
186
+ it.columnsEq(['sub2_', entity.primaryColumn], ['sub_', context.targetRelation.joiningColumn.columnName]),
187
+ )
188
+ : SelectBuilder.create()
189
+ .select(it => it.raw('1'))
190
+ .from(context.targetEntity.tableName, 'sub_')
191
+ .where(it => it.columnsEq([tableName, entity.primaryColumn], ['sub_', context.targetRelation.joiningColumn.columnName]))
179
192
 
180
193
  return conditionBuilder.exists(
181
194
  this.buildInternal(
@@ -192,6 +205,7 @@ export class WhereBuilder {
192
205
  return conditionBuilder
193
206
  }
194
207
 
208
+
195
209
  private createManyHasManySubquery(
196
210
  outerColumn: QueryBuilder.ColumnIdentifier,
197
211
  relationWhere: Input.Where,
@@ -271,6 +285,39 @@ export class WhereBuilder {
271
285
  }
272
286
  return condition
273
287
  }
288
+
289
+ private hasRootIsNull(where: Input.Where, entity: Model.Entity): boolean {
290
+ for (const key in where) {
291
+ if (key === 'and' || key === 'or') {
292
+ if (where[key]?.some(it => this.hasRootIsNull(it, entity))) {
293
+ return true
294
+ }
295
+ } else if (key === 'not') {
296
+ if (where.not && this.hasRootIsNull(where.not, entity)) {
297
+ return true
298
+ }
299
+ } else if (isColumn(entity.fields[key]) && this.conditionHasIsNull(where[key] as Input.Condition)) {
300
+ return true
301
+ }
302
+ }
303
+ return false
304
+ }
305
+
306
+ private conditionHasIsNull(cond: Input.Condition): boolean {
307
+ if (cond.isNull !== undefined) {
308
+ return true
309
+ }
310
+ if (cond.and && cond.and.some(it => this.conditionHasIsNull(it))) {
311
+ return true
312
+ }
313
+ if (cond.or && cond.or.some(it => this.conditionHasIsNull(it))) {
314
+ return true
315
+ }
316
+ if (cond.not && this.conditionHasIsNull(cond.not)) {
317
+ return true
318
+ }
319
+ return false
320
+ }
274
321
  }
275
322
 
276
323
  export type WhereJoinDefinition = { path: Path; entity: Model.Entity; relationName: string }
@@ -0,0 +1,84 @@
1
+ import { describe, it, assert } from 'vitest'
2
+ import { ConditionOptimizer } from '../../../src/mapper/select/optimizer/ConditionOptimizer'
3
+ import { createSchema, SchemaDefinition as def } from '@contember/schema-definition'
4
+ import { WhereOptimizer } from '../../../src/mapper/select/optimizer/WhereOptimizer'
5
+ import { ConditionBuilder, JoinBuilder, PathFactory, WhereBuilder } from '../../../src/mapper'
6
+ import { Compiler, SelectBuilder } from '@contember/database'
7
+ import { Input, Schema } from '@contember/schema'
8
+
9
+ namespace WhereBuilderModel {
10
+ export class Author {
11
+ name = def.stringColumn()
12
+ isPublic = def.boolColumn().notNull()
13
+ articles = def.oneHasMany(Article, 'author')
14
+ }
15
+
16
+ export class Article {
17
+ title = def.stringColumn()
18
+ isPublic = def.boolColumn().notNull()
19
+ author = def.manyHasOne(Author, 'articles')
20
+ }
21
+ }
22
+
23
+ const createWhere = (schema: Schema, where: Input.Where) => {
24
+ const pathFactory = new PathFactory()
25
+ const joinBuilder = new JoinBuilder(schema.model)
26
+ const conditionBuilder = new ConditionBuilder()
27
+ const whereOptimizer = new WhereOptimizer(schema.model, new ConditionOptimizer())
28
+ const whereBuilder = new WhereBuilder(schema.model, joinBuilder, conditionBuilder, pathFactory, whereOptimizer)
29
+
30
+ const qb = SelectBuilder.create()
31
+ .from('author', 'root_')
32
+
33
+ return whereBuilder.build(qb, schema.model.entities.Author, pathFactory.create([]), where).options.where.compile().sql
34
+ }
35
+
36
+ describe('where builder', () => {
37
+ it('where on has-many relation with AND outside', () => {
38
+ const schema = createSchema(WhereBuilderModel)
39
+ const where = createWhere(schema, {
40
+ and: [
41
+ {
42
+ articles: { isPublic: { eq: true } },
43
+ },
44
+ {
45
+ articles: { title: { eq: 'Hello' } },
46
+ },
47
+ ],
48
+ })
49
+ assert.equal(
50
+ where,
51
+ ' where exists (select 1 from "__SCHEMA__"."article" as "sub_" where "root_"."id" = "sub_"."author_id" and "sub_"."is_public" = ?) and exists (select 1 from "__SCHEMA__"."article" as "sub_" where "root_"."id" = "sub_"."author_id" and "sub_"."title" = ?)',
52
+ )
53
+ })
54
+
55
+ it('where on has-many relation with AND inside', () => {
56
+ const schema = createSchema(WhereBuilderModel)
57
+ const where = createWhere(schema, {
58
+ articles: {
59
+ and: [
60
+ { isPublic: { eq: true } },
61
+ { title: { eq: 'Hello' } },
62
+ ],
63
+ },
64
+ })
65
+ assert.equal(
66
+ where,
67
+ ' where exists (select 1 from "__SCHEMA__"."article" as "sub_" where "root_"."id" = "sub_"."author_id" and "sub_"."is_public" = ? and "sub_"."title" = ?)',
68
+ )
69
+ })
70
+
71
+
72
+ it('where on has-many relation with isNull', () => {
73
+ const schema = createSchema(WhereBuilderModel)
74
+ const where = createWhere(schema, {
75
+ articles: {
76
+ id: { isNull: true },
77
+ },
78
+ })
79
+ assert.equal(
80
+ where,
81
+ ' where exists (select 1 from (select "root_"."id") as "sub2_" left join "__SCHEMA__"."article" as "sub_" on "sub2_"."id" = "sub_"."author_id" where "sub_"."id" is null)',
82
+ )
83
+ })
84
+ })