@entity-access/entity-access 1.0.252 → 1.0.253
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/.vscode/launch.json +2 -1
- package/dist/common/symbols/symbols.d.ts +1 -0
- package/dist/common/symbols/symbols.d.ts.map +1 -1
- package/dist/common/symbols/symbols.js +1 -0
- package/dist/common/symbols/symbols.js.map +1 -1
- package/dist/decorators/ForeignKey.d.ts +8 -7
- package/dist/decorators/ForeignKey.d.ts.map +1 -1
- package/dist/decorators/ForeignKey.js +43 -8
- package/dist/decorators/ForeignKey.js.map +1 -1
- package/dist/decorators/IColumn.d.ts +6 -3
- package/dist/decorators/IColumn.d.ts.map +1 -1
- package/dist/decorators/Relate.d.ts.map +1 -1
- package/dist/decorators/Relate.js +8 -6
- package/dist/decorators/Relate.js.map +1 -1
- package/dist/entity-query/EntityType.d.ts +5 -1
- package/dist/entity-query/EntityType.d.ts.map +1 -1
- package/dist/entity-query/EntityType.js +57 -25
- package/dist/entity-query/EntityType.js.map +1 -1
- package/dist/migrations/postgres/PostgresAutomaticMigrations.js +1 -1
- package/dist/migrations/postgres/PostgresAutomaticMigrations.js.map +1 -1
- package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js +1 -1
- package/dist/migrations/sql-server/SqlServerAutomaticMigrations.js.map +1 -1
- package/dist/model/EntityModel.js +2 -2
- package/dist/model/EntityModel.js.map +1 -1
- package/dist/model/EntitySource.d.ts +6 -1
- package/dist/model/EntitySource.d.ts.map +1 -1
- package/dist/model/EntitySource.js.map +1 -1
- package/dist/model/SourceExpression.d.ts +1 -22
- package/dist/model/SourceExpression.d.ts.map +1 -1
- package/dist/model/SourceExpression.js +116 -98
- package/dist/model/SourceExpression.js.map +1 -1
- package/dist/model/changes/ChangeEntry.d.ts.map +1 -1
- package/dist/model/changes/ChangeEntry.js +62 -25
- package/dist/model/changes/ChangeEntry.js.map +1 -1
- package/dist/model/changes/ChangeSet.d.ts +2 -1
- package/dist/model/changes/ChangeSet.d.ts.map +1 -1
- package/dist/model/changes/ChangeSet.js +4 -3
- package/dist/model/changes/ChangeSet.js.map +1 -1
- package/dist/model/identity/IdentityMap.d.ts +23 -0
- package/dist/model/identity/IdentityMap.d.ts.map +1 -0
- package/dist/model/identity/IdentityMap.js +113 -0
- package/dist/model/identity/IdentityMap.js.map +1 -0
- package/dist/model/identity/RelationMapper.d.ts +2 -3
- package/dist/model/identity/RelationMapper.d.ts.map +1 -1
- package/dist/model/identity/RelationMapper.js +60 -27
- package/dist/model/identity/RelationMapper.js.map +1 -1
- package/dist/model/identity/SearchIndex.d.ts +17 -0
- package/dist/model/identity/SearchIndex.d.ts.map +1 -0
- package/dist/model/identity/SearchIndex.js +109 -0
- package/dist/model/identity/SearchIndex.js.map +1 -0
- package/dist/model/verification/VerificationSession.d.ts +1 -1
- package/dist/model/verification/VerificationSession.d.ts.map +1 -1
- package/dist/model/verification/VerificationSession.js +18 -16
- package/dist/model/verification/VerificationSession.js.map +1 -1
- package/dist/query/ast/ExpressionToSql.d.ts.map +1 -1
- package/dist/query/ast/ExpressionToSql.js +74 -52
- package/dist/query/ast/ExpressionToSql.js.map +1 -1
- package/dist/query/expander/QueryExpander.d.ts.map +1 -1
- package/dist/query/expander/QueryExpander.js +41 -10
- package/dist/query/expander/QueryExpander.js.map +1 -1
- package/dist/tests/db-tests/tests/multi-fk-tests.d.ts +3 -0
- package/dist/tests/db-tests/tests/multi-fk-tests.d.ts.map +1 -0
- package/dist/tests/db-tests/tests/multi-fk-tests.js +38 -0
- package/dist/tests/db-tests/tests/multi-fk-tests.js.map +1 -0
- package/dist/tests/expressions/left-joins/child-joins.js +7 -7
- package/dist/tests/model/ShoppingContext.d.ts +9 -0
- package/dist/tests/model/ShoppingContext.d.ts.map +1 -1
- package/dist/tests/model/ShoppingContext.js +34 -0
- package/dist/tests/model/ShoppingContext.js.map +1 -1
- package/dist/tests/security/tests/include-items.d.ts.map +1 -1
- package/dist/tests/security/tests/include-items.js +1 -0
- package/dist/tests/security/tests/include-items.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/common/symbols/symbols.ts +2 -1
- package/src/decorators/ForeignKey.ts +66 -28
- package/src/decorators/IColumn.ts +4 -3
- package/src/decorators/Relate.ts +8 -6
- package/src/entity-query/EntityType.ts +60 -26
- package/src/migrations/postgres/PostgresAutomaticMigrations.ts +1 -1
- package/src/migrations/sql-server/SqlServerAutomaticMigrations.ts +1 -1
- package/src/model/EntityModel.ts +2 -2
- package/src/model/EntitySource.ts +6 -1
- package/src/model/SourceExpression.ts +132 -132
- package/src/model/changes/ChangeEntry.ts +68 -25
- package/src/model/changes/ChangeSet.ts +4 -3
- package/src/model/identity/IdentityMap.ts +126 -0
- package/src/model/identity/RelationMapper.ts +71 -27
- package/src/model/identity/SearchIndex.ts +120 -0
- package/src/model/verification/VerificationSession.ts +19 -16
- package/src/query/ast/ExpressionToSql.ts +77 -61
- package/src/query/expander/QueryExpander.ts +52 -28
- package/src/tests/db-tests/tests/multi-fk-tests.ts +46 -0
- package/src/tests/expressions/left-joins/child-joins.ts +7 -7
- package/src/tests/model/ShoppingContext.ts +32 -0
- package/src/tests/security/tests/include-items.ts +1 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import EntityAccessError from "../../common/EntityAccessError.js";
|
|
1
2
|
import QueryCompiler from "../../compiler/QueryCompiler.js";
|
|
2
3
|
import EntityType, { IEntityProperty } from "../../entity-query/EntityType.js";
|
|
3
4
|
import EntityQuery from "../../model/EntityQuery.js";
|
|
@@ -225,33 +226,27 @@ export default class ExpressionToSql extends Visitor<ITextQuery> {
|
|
|
225
226
|
|
|
226
227
|
this.scope.create({ parameter: select.sourceParameter, model: relatedModel, selectStatement: select });
|
|
227
228
|
select[filteredSymbol] = true;
|
|
228
|
-
const targetKey = MemberExpression.create({
|
|
229
|
-
target: parameter,
|
|
230
|
-
property: Identifier.create({
|
|
231
|
-
value: targetType.keys[0].columnName
|
|
232
|
-
})
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
const relatedKey = MemberExpression.create({
|
|
236
|
-
target: select.sourceParameter,
|
|
237
|
-
property: Identifier.create({
|
|
238
|
-
value: relation.relation.fkColumn.columnName
|
|
239
|
-
})
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const join = Expression.equal(targetKey, relatedKey);
|
|
244
229
|
|
|
245
230
|
let where = select.where;
|
|
246
231
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
232
|
+
for (const { fkColumn, relatedKeyColumn } of relation.relation.fkMap) {
|
|
233
|
+
const targetKey = MemberExpression.create({
|
|
234
|
+
target: parameter,
|
|
235
|
+
property: Identifier.create({
|
|
236
|
+
value: relatedKeyColumn.columnName
|
|
237
|
+
})
|
|
252
238
|
});
|
|
253
|
-
|
|
254
|
-
|
|
239
|
+
|
|
240
|
+
const relatedKey = MemberExpression.create({
|
|
241
|
+
target: select.sourceParameter,
|
|
242
|
+
property: Identifier.create({
|
|
243
|
+
value: fkColumn.columnName
|
|
244
|
+
})
|
|
245
|
+
});
|
|
246
|
+
const join = Expression.equal(targetKey, relatedKey);
|
|
247
|
+
where = where
|
|
248
|
+
? Expression.logicalAnd(where, join)
|
|
249
|
+
: join;
|
|
255
250
|
}
|
|
256
251
|
|
|
257
252
|
select.where = where;
|
|
@@ -282,36 +277,33 @@ export default class ExpressionToSql extends Visitor<ITextQuery> {
|
|
|
282
277
|
this.scope.alias(param1, select.sourceParameter, select);
|
|
283
278
|
select.sourceParameter = param1;
|
|
284
279
|
select[filteredSymbol] = true;
|
|
285
|
-
const targetKey = MemberExpression.create({
|
|
286
|
-
target: parameter,
|
|
287
|
-
property: Identifier.create({
|
|
288
|
-
value: targetType.keys[0].columnName
|
|
289
|
-
})
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
const relatedKey = MemberExpression.create({
|
|
293
|
-
target: param1,
|
|
294
|
-
property: Identifier.create({
|
|
295
|
-
value: relation.relation.fkColumn.columnName
|
|
296
|
-
})
|
|
297
|
-
});
|
|
298
280
|
|
|
281
|
+
let where = select.where;
|
|
282
|
+
where = where
|
|
283
|
+
? Expression.logicalAnd(where, body.body)
|
|
284
|
+
: body.body;
|
|
299
285
|
|
|
300
|
-
const
|
|
301
|
-
Expression.equal(targetKey, relatedKey),
|
|
302
|
-
body.body
|
|
303
|
-
);
|
|
286
|
+
for (const { fkColumn, relatedKeyColumn } of relation.relation.relatedRelation.fkMap) {
|
|
304
287
|
|
|
305
|
-
|
|
288
|
+
const targetKey = MemberExpression.create({
|
|
289
|
+
target: parameter,
|
|
290
|
+
property: Identifier.create({
|
|
291
|
+
value: relatedKeyColumn.columnName
|
|
292
|
+
})
|
|
293
|
+
});
|
|
306
294
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
295
|
+
const relatedKey = MemberExpression.create({
|
|
296
|
+
target: param1,
|
|
297
|
+
property: Identifier.create({
|
|
298
|
+
value: fkColumn.columnName
|
|
299
|
+
})
|
|
312
300
|
});
|
|
313
|
-
|
|
314
|
-
|
|
301
|
+
|
|
302
|
+
const join = Expression.equal(targetKey, relatedKey);
|
|
303
|
+
|
|
304
|
+
where = where
|
|
305
|
+
? Expression.logicalAnd(where, join)
|
|
306
|
+
: join;
|
|
315
307
|
}
|
|
316
308
|
|
|
317
309
|
select.where = where;
|
|
@@ -723,16 +715,18 @@ export default class ExpressionToSql extends Visitor<ITextQuery> {
|
|
|
723
715
|
}
|
|
724
716
|
if (relation) {
|
|
725
717
|
|
|
726
|
-
const { fkColumn } = relation;
|
|
727
|
-
|
|
728
718
|
if (!relation.isCollection) {
|
|
729
719
|
|
|
730
|
-
let columnName = fkColumn.columnName;
|
|
731
|
-
// for inverse relation, we need to
|
|
732
|
-
// use primary key of current model
|
|
733
|
-
if (relation.isInverseRelation) {
|
|
734
|
-
|
|
735
|
-
}
|
|
720
|
+
// let columnName = fkColumn.columnName;
|
|
721
|
+
// // for inverse relation, we need to
|
|
722
|
+
// // use primary key of current model
|
|
723
|
+
// if (relation.isInverseRelation) {
|
|
724
|
+
// columnName = peModel.keys[0].columnName;
|
|
725
|
+
// }
|
|
726
|
+
|
|
727
|
+
const fkMap = relation.fkMap ?? relation.relatedRelation.fkMap;
|
|
728
|
+
|
|
729
|
+
const isNullable = fkMap.some(({ fkColumn }) => fkColumn.nullable);
|
|
736
730
|
|
|
737
731
|
const select = scope?.selectStatement ?? this.source?.selectStatement;
|
|
738
732
|
if (select) {
|
|
@@ -743,20 +737,42 @@ export default class ExpressionToSql extends Visitor<ITextQuery> {
|
|
|
743
737
|
this.scope.create({ parameter: join.as as ParameterExpression, model: join.model, selectStatement: select });
|
|
744
738
|
return join.as;
|
|
745
739
|
}
|
|
746
|
-
const joinType = select.preferLeftJoins ? "LEFT" : (
|
|
740
|
+
const joinType = select.preferLeftJoins ? "LEFT" : (isNullable ? "LEFT" : "INNER");
|
|
747
741
|
const joinParameter = ParameterExpression.create({
|
|
748
742
|
name: relation.relatedEntity.name[0],
|
|
749
743
|
model: relation.relatedEntity
|
|
750
744
|
});
|
|
745
|
+
let where: Expression;
|
|
746
|
+
for (const {fkColumn, relatedKeyColumn} of fkMap) {
|
|
747
|
+
let peColumn;
|
|
748
|
+
let joinColumn;
|
|
749
|
+
if (fkColumn.entityType === pe.model) {
|
|
750
|
+
peColumn = fkColumn;
|
|
751
|
+
} else if (relatedKeyColumn.entityType === pe.model) {
|
|
752
|
+
peColumn = relatedKeyColumn;
|
|
753
|
+
} else {
|
|
754
|
+
throw new EntityAccessError(`Invalid configuration`);
|
|
755
|
+
}
|
|
756
|
+
if (fkColumn.entityType === joinParameter.model) {
|
|
757
|
+
joinColumn = fkColumn;
|
|
758
|
+
} else if (relatedKeyColumn.entityType === joinParameter.model) {
|
|
759
|
+
joinColumn = relatedKeyColumn;
|
|
760
|
+
} else {
|
|
761
|
+
throw new EntityAccessError(`Invalid configuration`);
|
|
762
|
+
}
|
|
763
|
+
const joinOn = Expression.equal(
|
|
764
|
+
Expression.member(pe, peColumn.columnName),
|
|
765
|
+
Expression.member(joinParameter, joinColumn.columnName));
|
|
766
|
+
where = where
|
|
767
|
+
? Expression.logicalAnd(where, joinOn)
|
|
768
|
+
: joinOn;
|
|
769
|
+
}
|
|
751
770
|
join = JoinExpression.create({
|
|
752
771
|
as: joinParameter,
|
|
753
772
|
joinType,
|
|
754
773
|
model: joinParameter.model,
|
|
755
774
|
source: Expression.identifier(relation.relatedEntity.name),
|
|
756
|
-
where
|
|
757
|
-
Expression.member(pe, columnName),
|
|
758
|
-
Expression.member(joinParameter, relation.relatedEntity.keys[0].columnName)
|
|
759
|
-
)
|
|
775
|
+
where
|
|
760
776
|
});
|
|
761
777
|
select.joins.push(join);
|
|
762
778
|
this.scope.create({ parameter: joinParameter, model: relation.relatedEntity, selectStatement: select});
|
|
@@ -98,7 +98,7 @@ export class QueryExpander {
|
|
|
98
98
|
// let where: Expression;
|
|
99
99
|
// let joinWhere: Expression;
|
|
100
100
|
|
|
101
|
-
const fk = relation.
|
|
101
|
+
const fk = relation.fkMap ?? relation.relatedRelation.fkMap;
|
|
102
102
|
|
|
103
103
|
key += "." + relation.name;
|
|
104
104
|
|
|
@@ -107,15 +107,17 @@ export class QueryExpander {
|
|
|
107
107
|
return relationSet;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
let where: Expression;
|
|
111
|
+
|
|
110
112
|
if(relation.isInverseRelation) {
|
|
111
113
|
|
|
112
|
-
const keyColumn = model.keys[0].columnName;
|
|
113
|
-
let columnName = fk.columnName;
|
|
114
|
-
// for inverse relation, we need to
|
|
115
|
-
// use primary key of current model
|
|
116
|
-
if (!relation.isCollection) {
|
|
117
|
-
|
|
118
|
-
}
|
|
114
|
+
// const keyColumn = model.keys[0].columnName;
|
|
115
|
+
// let columnName = fk.columnName;
|
|
116
|
+
// // for inverse relation, we need to
|
|
117
|
+
// // use primary key of current model
|
|
118
|
+
// if (!relation.isCollection) {
|
|
119
|
+
// columnName = select.model.keys[0].columnName;
|
|
120
|
+
// }
|
|
119
121
|
|
|
120
122
|
|
|
121
123
|
const joins = (select.joins ??= []);
|
|
@@ -124,21 +126,32 @@ export class QueryExpander {
|
|
|
124
126
|
// This join has to be INNER JOIN as we are only interested
|
|
125
127
|
// in the results that matches parent query exactly
|
|
126
128
|
|
|
129
|
+
for (const { fkColumn, relatedKeyColumn } of relation.relatedRelation.fkMap) {
|
|
130
|
+
const joinColumn = fkColumn.entityType === joinParameter.model ? fkColumn : relatedKeyColumn;
|
|
131
|
+
const relatedColumn = relatedKeyColumn.entityType === select.sourceParameter.model ? relatedKeyColumn : fkColumn;
|
|
132
|
+
const joinOn = Expression.equal(
|
|
133
|
+
Expression.member(joinParameter, Expression.identifier(joinColumn.columnName)),
|
|
134
|
+
Expression.member(select.sourceParameter, Expression.identifier(relatedColumn.columnName))
|
|
135
|
+
);
|
|
136
|
+
where = where ? Expression.logicalAnd(where, joinOn) : joinOn;
|
|
137
|
+
}
|
|
138
|
+
|
|
127
139
|
joins.push(JoinExpression.create({
|
|
128
140
|
joinType: "INNER",
|
|
129
141
|
source: { ... parent },
|
|
130
142
|
as: joinParameter,
|
|
131
143
|
model: parent.model,
|
|
132
|
-
where
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
)
|
|
144
|
+
where
|
|
145
|
+
// where: Expression.equal(
|
|
146
|
+
// Expression.member(
|
|
147
|
+
// joinParameter,
|
|
148
|
+
// Expression.identifier(keyColumn)
|
|
149
|
+
// ),
|
|
150
|
+
// Expression.member(
|
|
151
|
+
// select.sourceParameter,
|
|
152
|
+
// Expression.identifier(columnName)
|
|
153
|
+
// )
|
|
154
|
+
// )
|
|
142
155
|
}));
|
|
143
156
|
|
|
144
157
|
// if (parent.where) {
|
|
@@ -192,22 +205,33 @@ export class QueryExpander {
|
|
|
192
205
|
// This join has to be INNER JOIN as we are only interested
|
|
193
206
|
// in the results that matches parent query exactly
|
|
194
207
|
|
|
208
|
+
for (const { fkColumn, relatedKeyColumn } of relation.fkMap) {
|
|
209
|
+
const joinOn = Expression.equal(
|
|
210
|
+
Expression.member(selectJoinParameter,
|
|
211
|
+
Expression.identifier(fkColumn.columnName)),
|
|
212
|
+
Expression.member(select.sourceParameter,
|
|
213
|
+
Expression.identifier(relatedKeyColumn.columnName))
|
|
214
|
+
);
|
|
215
|
+
where = where ? Expression.logicalAnd(where, joinOn) : joinOn;
|
|
216
|
+
}
|
|
217
|
+
|
|
195
218
|
selectJoins.push(JoinExpression.create({
|
|
196
219
|
joinType: "INNER",
|
|
197
220
|
source: { ... parent },
|
|
198
221
|
as: selectJoinParameter,
|
|
199
222
|
model: parent.model,
|
|
223
|
+
where
|
|
200
224
|
// model,
|
|
201
|
-
where: Expression.equal(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
)
|
|
225
|
+
// where: Expression.equal(
|
|
226
|
+
// Expression.member(
|
|
227
|
+
// selectJoinParameter,
|
|
228
|
+
// Expression.identifier(fk.columnName)
|
|
229
|
+
// ),
|
|
230
|
+
// Expression.member(
|
|
231
|
+
// select.sourceParameter,
|
|
232
|
+
// Expression.identifier(relation.relatedEntity.keys[0].columnName)
|
|
233
|
+
// )
|
|
234
|
+
// )
|
|
211
235
|
}));
|
|
212
236
|
|
|
213
237
|
this.include.push(select);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { TestConfig } from "../../TestConfig.js";
|
|
3
|
+
import { createContext } from "../../model/createContext.js";
|
|
4
|
+
|
|
5
|
+
export default async function(this: TestConfig) {
|
|
6
|
+
|
|
7
|
+
if (!this.db) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const context = await createContext(this.driver);
|
|
12
|
+
|
|
13
|
+
const { userID } = await context.users.asQuery().first();
|
|
14
|
+
const { categoryID } = await context.categories.asQuery().first();
|
|
15
|
+
|
|
16
|
+
await context.userCategories.saveDirect({
|
|
17
|
+
mode: "insert",
|
|
18
|
+
changes: {
|
|
19
|
+
userID,
|
|
20
|
+
categoryID
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
await context.userCategoryTags.saveDirect({
|
|
25
|
+
mode: "insert",
|
|
26
|
+
changes: {
|
|
27
|
+
categoryID,
|
|
28
|
+
userID,
|
|
29
|
+
tag: "A"
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
await context.userCategoryTags.saveDirect({
|
|
33
|
+
mode: "insert",
|
|
34
|
+
changes: {
|
|
35
|
+
categoryID,
|
|
36
|
+
userID,
|
|
37
|
+
tag: "B"
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const first = await context.userCategories.asQuery()
|
|
42
|
+
.include((x) => x.tags)
|
|
43
|
+
.first();
|
|
44
|
+
|
|
45
|
+
assert.notEqual(void 0, first.tags);
|
|
46
|
+
}
|
|
@@ -14,7 +14,7 @@ FROM products AS p1
|
|
|
14
14
|
WHERE EXISTS (SELECT
|
|
15
15
|
1
|
|
16
16
|
FROM order_items AS o
|
|
17
|
-
WHERE (
|
|
17
|
+
WHERE (o.product_id = $1) AND (p1.product_id = o.product_id))`;
|
|
18
18
|
|
|
19
19
|
const sql2 = `SELECT
|
|
20
20
|
p1.product_id,
|
|
@@ -26,10 +26,10 @@ FROM products AS p1
|
|
|
26
26
|
WHERE EXISTS (SELECT
|
|
27
27
|
1
|
|
28
28
|
FROM order_items AS o
|
|
29
|
-
WHERE (
|
|
29
|
+
WHERE (o.product_id = $1) AND (p1.product_id = o.product_id)) AND EXISTS (SELECT
|
|
30
30
|
1
|
|
31
31
|
FROM order_items AS o1
|
|
32
|
-
WHERE (
|
|
32
|
+
WHERE (o1.amount > $2) AND (p1.product_id = o1.product_id))`;
|
|
33
33
|
|
|
34
34
|
const sql3 = `SELECT
|
|
35
35
|
p1.product_id,
|
|
@@ -41,11 +41,11 @@ FROM products AS p1
|
|
|
41
41
|
WHERE EXISTS (SELECT
|
|
42
42
|
1
|
|
43
43
|
FROM order_items AS o
|
|
44
|
-
WHERE (
|
|
44
|
+
WHERE (o.product_id = $1) AND (p1.product_id = o.product_id)) AND EXISTS (SELECT
|
|
45
45
|
1
|
|
46
46
|
FROM order_items AS o1
|
|
47
47
|
INNER JOIN orders AS o2 ON o1.order_id = o2.order_id
|
|
48
|
-
WHERE (
|
|
48
|
+
WHERE (o2.order_date > $2) AND (p1.product_id = o1.product_id))`;
|
|
49
49
|
|
|
50
50
|
const productJoin = `SELECT
|
|
51
51
|
p1.product_id,
|
|
@@ -77,10 +77,10 @@ p1.product_description
|
|
|
77
77
|
FROM products AS p1
|
|
78
78
|
WHERE EXISTS
|
|
79
79
|
(SELECT 1 FROM order_items AS o
|
|
80
|
-
WHERE (
|
|
80
|
+
WHERE (o.product_id = $1) AND (p1.product_id = o.product_id)) AND
|
|
81
81
|
NOT (EXISTS (SELECT 1 FROM order_items AS o1
|
|
82
82
|
INNER JOIN orders AS o2 ON o1.order_id = o2.order_id
|
|
83
|
-
WHERE (
|
|
83
|
+
WHERE (o2.order_date > $2) AND (p1.product_id = o1.product_id)))
|
|
84
84
|
`;
|
|
85
85
|
|
|
86
86
|
export default function() {
|
|
@@ -6,6 +6,7 @@ import Index from "../../decorators/Index.js";
|
|
|
6
6
|
import DateTime from "../../types/DateTime.js";
|
|
7
7
|
import { UserFile } from "./UseFile.js";
|
|
8
8
|
import Sql from "../../sql/Sql.js";
|
|
9
|
+
import MultiForeignKeys from "../../decorators/ForeignKey.js";
|
|
9
10
|
|
|
10
11
|
export const statusPublished = "published";
|
|
11
12
|
|
|
@@ -34,6 +35,8 @@ export class ShoppingContext extends EntityContext {
|
|
|
34
35
|
public userFiles = this.model.register(UserFile);
|
|
35
36
|
|
|
36
37
|
public emailAddresses = this.model.register(EmailAddress);
|
|
38
|
+
|
|
39
|
+
public userCategoryTags = this.model.register(UserCategoryTag);
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
@Table("Users")
|
|
@@ -191,6 +194,35 @@ export class UserCategory {
|
|
|
191
194
|
public user: User;
|
|
192
195
|
|
|
193
196
|
public category: Category;
|
|
197
|
+
|
|
198
|
+
public tags: UserCategoryTag[];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@Table("UserCategoryTags")
|
|
202
|
+
export class UserCategoryTag {
|
|
203
|
+
|
|
204
|
+
@Column({ key: true, dataType: "BigInt", generated: "identity"})
|
|
205
|
+
tagID: number;
|
|
206
|
+
|
|
207
|
+
@Column({ dataType: "Char", length: 200 })
|
|
208
|
+
tag: string;
|
|
209
|
+
|
|
210
|
+
@Column({ dataType: "BigInt"})
|
|
211
|
+
public userID: number;
|
|
212
|
+
|
|
213
|
+
@Column({ dataType: "Char", length: 200 })
|
|
214
|
+
public categoryID: string;
|
|
215
|
+
|
|
216
|
+
@MultiForeignKeys(UserCategory, {
|
|
217
|
+
inverseProperty: (x) => x.tags,
|
|
218
|
+
foreignKeys: [
|
|
219
|
+
{ foreignKey: (x) => x.userID, key: (x) => x.userID },
|
|
220
|
+
{ foreignKey: (x) => x.categoryID, key: (x) => x.categoryID}
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
)
|
|
224
|
+
public userCategory: UserCategory;
|
|
225
|
+
|
|
194
226
|
}
|
|
195
227
|
|
|
196
228
|
@Table("Products")
|