@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/dist/src/mapper/select/WhereBuilder.d.ts +2 -0
- package/dist/src/mapper/select/WhereBuilder.d.ts.map +1 -1
- package/dist/src/mapper/select/WhereBuilder.js +43 -4
- package/dist/src/mapper/select/WhereBuilder.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/tests/cases/unit/whereBuilder.test.d.ts +2 -0
- package/dist/tests/cases/unit/whereBuilder.test.d.ts.map +1 -0
- package/dist/tests/cases/unit/whereBuilder.test.js +75 -0
- package/dist/tests/cases/unit/whereBuilder.test.js.map +1 -0
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/mapper/select/WhereBuilder.ts +53 -6
- package/tests/cases/unit/whereBuilder.test.ts +84 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/engine-content-api",
|
|
3
|
-
"version": "1.2.0-alpha.
|
|
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.
|
|
14
|
-
"@contember/dic": "^1.2.0-alpha.
|
|
15
|
-
"@contember/engine-common": "^1.2.0-alpha.
|
|
16
|
-
"@contember/graphql-utils": "^1.2.0-alpha.
|
|
17
|
-
"@contember/schema": "^1.2.0-alpha.
|
|
18
|
-
"@contember/schema-utils": "^1.2.0-alpha.
|
|
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.
|
|
24
|
-
"@contember/engine-api-tester": "^1.2.0-alpha.
|
|
25
|
-
"@contember/schema-definition": "^1.2.0-alpha.
|
|
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 {
|
|
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 =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
+
})
|