@entity-access/entity-access 1.0.316 → 1.0.318
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/compiler/postgres/PostgreSqlMethodTransformer.js +1 -1
- package/dist/compiler/postgres/PostgreSqlMethodTransformer.js.map +1 -1
- package/dist/drivers/sql-server/ExpressionToSqlServer.d.ts.map +1 -1
- package/dist/drivers/sql-server/ExpressionToSqlServer.js +5 -1
- package/dist/drivers/sql-server/ExpressionToSqlServer.js.map +1 -1
- package/dist/model/EntityContext.d.ts.map +1 -1
- package/dist/model/EntityContext.js +4 -0
- package/dist/model/EntityContext.js.map +1 -1
- package/dist/model/EntityQuery.d.ts +3 -1
- package/dist/model/EntityQuery.d.ts.map +1 -1
- package/dist/model/EntityQuery.js +125 -56
- package/dist/model/EntityQuery.js.map +1 -1
- package/dist/model/IFilterWithParameter.d.ts +5 -0
- package/dist/model/IFilterWithParameter.d.ts.map +1 -1
- package/dist/query/ast/ExpressionToSql.d.ts.map +1 -1
- package/dist/query/ast/ExpressionToSql.js +4 -1
- package/dist/query/ast/ExpressionToSql.js.map +1 -1
- package/dist/query/ast/Expressions.d.ts +1 -0
- package/dist/query/ast/Expressions.d.ts.map +1 -1
- package/dist/query/ast/Expressions.js.map +1 -1
- package/dist/tests/db-tests/tests/select-items-sum.d.ts.map +1 -1
- package/dist/tests/db-tests/tests/select-items-sum.js +17 -8
- package/dist/tests/db-tests/tests/select-items-sum.js.map +1 -1
- package/dist/tests/db-tests/tests/update-select.d.ts +3 -0
- package/dist/tests/db-tests/tests/update-select.d.ts.map +1 -0
- package/dist/tests/db-tests/tests/update-select.js +19 -0
- package/dist/tests/db-tests/tests/update-select.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/compiler/postgres/PostgreSqlMethodTransformer.ts +1 -1
- package/src/drivers/sql-server/ExpressionToSqlServer.ts +6 -1
- package/src/model/EntityContext.ts +4 -0
- package/src/model/EntityQuery.ts +99 -28
- package/src/model/IFilterWithParameter.ts +4 -0
- package/src/query/ast/ExpressionToSql.ts +5 -1
- package/src/query/ast/Expressions.ts +2 -0
- package/src/tests/db-tests/tests/select-items-sum.ts +19 -9
- package/src/tests/db-tests/tests/update-select.ts +28 -0
package/src/model/EntityQuery.ts
CHANGED
|
@@ -176,7 +176,7 @@ export default class EntityQuery<T = any>
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
async delete(p, f): Promise<number> {
|
|
179
|
-
if (
|
|
179
|
+
if (f) {
|
|
180
180
|
return this.where(p, f).delete(void 0, void 0);
|
|
181
181
|
}
|
|
182
182
|
|
|
@@ -218,9 +218,60 @@ export default class EntityQuery<T = any>
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
async updateSelect(p?, f?): Promise<T[]> {
|
|
222
|
+
const updateStatement = this.getUpdateStatement(p, f, true);
|
|
223
|
+
|
|
224
|
+
await using scope = new AsyncDisposableScope();
|
|
225
|
+
const session = this.context.logger?.newSession() ?? Logger.nullLogger;
|
|
226
|
+
let query: { text: string, values: any[]};
|
|
227
|
+
try {
|
|
228
|
+
scope.register(session);
|
|
229
|
+
const type = this.type;
|
|
230
|
+
const signal = this.signal;
|
|
231
|
+
|
|
232
|
+
const relationMapper = new RelationMapper(this.context.changeSet);
|
|
233
|
+
|
|
234
|
+
signal?.throwIfAborted();
|
|
235
|
+
|
|
236
|
+
query = this.context.driver.compiler.compileExpression(this, updateStatement);
|
|
237
|
+
this.traceQuery?.(query.text);
|
|
238
|
+
const reader = await this.context.connection.executeReader(query, signal);
|
|
239
|
+
scope.register(reader);
|
|
240
|
+
const results = [] as T[];
|
|
241
|
+
for await (const iterator of reader.next(10, signal)) {
|
|
242
|
+
const item = type.map(iterator) as any;
|
|
243
|
+
// set identity...
|
|
244
|
+
const entry = this.context.changeSet.getEntry(item, item);
|
|
245
|
+
relationMapper.fix(entry);
|
|
246
|
+
results.push(entry.entity);
|
|
247
|
+
}
|
|
248
|
+
return results;
|
|
249
|
+
} catch(error) {
|
|
250
|
+
session.error(`Failed executing ${query?.text}\n${error.stack ?? error}`);
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
221
255
|
async update(p?, f?): Promise<number> {
|
|
222
256
|
|
|
223
|
-
|
|
257
|
+
const updateStatement = this.getUpdateStatement(p, f);
|
|
258
|
+
|
|
259
|
+
const session = this.context.logger?.newSession() ?? Logger.nullLogger;
|
|
260
|
+
let query;
|
|
261
|
+
try {
|
|
262
|
+
query = this.context.driver.compiler.compileExpression(this, updateStatement);
|
|
263
|
+
this.traceQuery?.(query.text);
|
|
264
|
+
const r = await this.context.connection.executeQuery(query);
|
|
265
|
+
return r.updated;
|
|
266
|
+
} catch (error) {
|
|
267
|
+
session.error(`Failed executing ${query?.text}\r\n${error.stack ?? error}`);
|
|
268
|
+
throw error;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
getUpdateStatement(p?, f?, returnEntity = false) {
|
|
273
|
+
|
|
274
|
+
if (f) {
|
|
224
275
|
return this.extend(p, f, (select, body) => {
|
|
225
276
|
const fields = [] as Expression[];
|
|
226
277
|
switch(body.type) {
|
|
@@ -238,7 +289,7 @@ export default class EntityQuery<T = any>
|
|
|
238
289
|
break;
|
|
239
290
|
}
|
|
240
291
|
return { ... select, fields };
|
|
241
|
-
}).
|
|
292
|
+
}).getUpdateStatement(void 0, void 0, returnEntity);
|
|
242
293
|
}
|
|
243
294
|
|
|
244
295
|
const as = Expression.parameter("s1", this.type);
|
|
@@ -253,13 +304,13 @@ export default class EntityQuery<T = any>
|
|
|
253
304
|
const fieldMap = new Set();
|
|
254
305
|
|
|
255
306
|
for (const iterator of this.selectStatement.fields) {
|
|
256
|
-
if(iterator.type !== "ExpressionAs") {
|
|
307
|
+
if (iterator.type !== "ExpressionAs") {
|
|
257
308
|
throw new Error(`Invalid expression ${iterator.type}`);
|
|
258
309
|
}
|
|
259
310
|
const eAs = iterator as ExpressionAs;
|
|
260
311
|
const { field } = this.type.getProperty(eAs.alias.value);
|
|
261
312
|
fieldMap.add(field.columnName);
|
|
262
|
-
set.push(Expression.assign(
|
|
313
|
+
set.push(Expression.assign(Expression.quotedIdentifier(field.columnName), Expression.member(as, Expression.quotedIdentifier(eAs.alias.value))));
|
|
263
314
|
}
|
|
264
315
|
|
|
265
316
|
let where = null as Expression;
|
|
@@ -272,7 +323,18 @@ export default class EntityQuery<T = any>
|
|
|
272
323
|
if (fieldMap.has(iterator.columnName)) {
|
|
273
324
|
continue;
|
|
274
325
|
}
|
|
275
|
-
this.selectStatement.fields.push(
|
|
326
|
+
this.selectStatement.fields.push(Expression.member(this.selectStatement.sourceParameter, Expression.quotedIdentifier(iterator.columnName)));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
let returnUpdated = null as ExpressionAs[];
|
|
330
|
+
if(returnEntity) {
|
|
331
|
+
returnUpdated = [];
|
|
332
|
+
for (const iterator of this.type.columns) {
|
|
333
|
+
returnUpdated.push(Expression.as(
|
|
334
|
+
Expression.identifier(iterator.columnName),
|
|
335
|
+
Expression.quotedIdentifier(iterator.name)
|
|
336
|
+
));
|
|
337
|
+
}
|
|
276
338
|
}
|
|
277
339
|
|
|
278
340
|
const updateStatement = UpdateStatement.create({
|
|
@@ -281,20 +343,10 @@ export default class EntityQuery<T = any>
|
|
|
281
343
|
table: this.type.fullyQualifiedName,
|
|
282
344
|
model: this.type,
|
|
283
345
|
where,
|
|
284
|
-
join
|
|
346
|
+
join,
|
|
347
|
+
returnUpdated
|
|
285
348
|
});
|
|
286
|
-
|
|
287
|
-
const session = this.context.logger?.newSession() ?? Logger.nullLogger;
|
|
288
|
-
let query;
|
|
289
|
-
try {
|
|
290
|
-
query = this.context.driver.compiler.compileExpression(this, updateStatement);
|
|
291
|
-
this.traceQuery?.(query.text);
|
|
292
|
-
const r = await this.context.connection.executeQuery(query);
|
|
293
|
-
return r.updated;
|
|
294
|
-
} catch (error) {
|
|
295
|
-
session.error(`Failed executing ${query?.text}\r\n${error.stack ?? error}`);
|
|
296
|
-
throw error;
|
|
297
|
-
}
|
|
349
|
+
return updateStatement;
|
|
298
350
|
}
|
|
299
351
|
|
|
300
352
|
async toArray(): Promise<T[]> {
|
|
@@ -453,20 +505,35 @@ export default class EntityQuery<T = any>
|
|
|
453
505
|
return new EntityQuery({ ... this, selectStatement: { ... this.selectStatement, offset: n} });
|
|
454
506
|
}
|
|
455
507
|
|
|
456
|
-
async sum(parameters?:any, fx?: any): Promise<
|
|
457
|
-
if (
|
|
508
|
+
async sum(parameters?:any, fx?: any): Promise<any> {
|
|
509
|
+
if (fx !== void 0) {
|
|
458
510
|
return this.map(parameters, fx).sum();
|
|
459
511
|
}
|
|
460
|
-
|
|
461
|
-
const
|
|
462
|
-
|
|
512
|
+
|
|
513
|
+
const fields = [];
|
|
514
|
+
|
|
515
|
+
let fieldName;
|
|
516
|
+
|
|
517
|
+
for (const field of this.selectStatement.fields) {
|
|
518
|
+
let expression = field;
|
|
519
|
+
if (field.type === "ExpressionAs") {
|
|
520
|
+
const fe = field as ExpressionAs;
|
|
521
|
+
expression = fe.expression;
|
|
522
|
+
fieldName = fe.alias.value;
|
|
523
|
+
} else {
|
|
524
|
+
fieldName = "c1";
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
fields.push(ExpressionAs.create({
|
|
463
528
|
expression: Expression.callExpression(
|
|
464
529
|
"COALESCE",
|
|
465
|
-
Expression.callExpression("SUM",
|
|
530
|
+
Expression.callExpression("SUM", expression),
|
|
466
531
|
NumberLiteral.zero),
|
|
467
|
-
alias: Expression.
|
|
468
|
-
})
|
|
469
|
-
|
|
532
|
+
alias: Expression.quotedIdentifier(fieldName)
|
|
533
|
+
}));
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const select = { ... this.selectStatement, fields,
|
|
470
537
|
orderBy: void 0
|
|
471
538
|
};
|
|
472
539
|
|
|
@@ -477,9 +544,13 @@ export default class EntityQuery<T = any>
|
|
|
477
544
|
let query;
|
|
478
545
|
try {
|
|
479
546
|
query = this.context.driver.compiler.compileExpression(nq, select);
|
|
547
|
+
this.traceQuery?.(query.text);
|
|
480
548
|
const reader = await this.context.connection.executeReader(query);
|
|
481
549
|
scope.register(reader);
|
|
482
550
|
for await (const iterator of reader.next()) {
|
|
551
|
+
if (fields.length > 1) {
|
|
552
|
+
return iterator as any;
|
|
553
|
+
}
|
|
483
554
|
return iterator.c1 as number;
|
|
484
555
|
}
|
|
485
556
|
// this is special case when database does not return any count
|
|
@@ -7,6 +7,8 @@ export type ILambdaExpression<P = any, T = any, TR = any> = [input: P, x: (p: P)
|
|
|
7
7
|
|
|
8
8
|
export type IFilterExpression<P = any, T = any> = [input: P, x: (p: P) => (s: T) => boolean];
|
|
9
9
|
|
|
10
|
+
export type IFieldsAsNumbers<T> = { [P in keyof T]: number };
|
|
11
|
+
|
|
10
12
|
export interface IBaseQuery<T> {
|
|
11
13
|
enumerate(): AsyncGenerator<T>;
|
|
12
14
|
|
|
@@ -32,6 +34,7 @@ export interface IBaseQuery<T> {
|
|
|
32
34
|
|
|
33
35
|
sum(): Promise<number>;
|
|
34
36
|
sum<P>(parameters: P, fx: (p: P) => (x: T) => number): Promise<number>;
|
|
37
|
+
sum<P, TR>(parameters: P, fx: (p: P) => (x: T) => TR): Promise<IFieldsAsNumbers<TR>>;
|
|
35
38
|
|
|
36
39
|
|
|
37
40
|
withSignal<DT>(this:DT, signal: AbortSignal): DT;
|
|
@@ -39,6 +42,7 @@ export interface IBaseQuery<T> {
|
|
|
39
42
|
include<TR>(fx: (x: T) => TR | TR[]): IBaseQuery<T>;
|
|
40
43
|
|
|
41
44
|
update<P>(parameters: P, fx: (p: P) => (x:T) => Partial<T>): Promise<number>;
|
|
45
|
+
updateSelect<P>(parameters: P, fx: (p: P) => (x:T) => Partial<T>): Promise<T[]>;
|
|
42
46
|
|
|
43
47
|
/**
|
|
44
48
|
* Warning !! Be careful, this will delete rows from the database and neither soft delete nor any other events will be invoked.
|
|
@@ -587,7 +587,11 @@ export default class ExpressionToSql extends Visitor<ITextQuery> {
|
|
|
587
587
|
const joinName = this.scope.nameOf(as);
|
|
588
588
|
const asName = this.scope.nameOf(e.sourceParameter);
|
|
589
589
|
const set = this.visitArray(e.set, ",");
|
|
590
|
-
|
|
590
|
+
const returning = e.returnUpdated ? [ ` RETURNING `, ... e.returnUpdated.map((r, i) => i
|
|
591
|
+
? [ `, ${asName}.`, this.visit(r.expression), ` as ${this.visit(r.alias)}`]
|
|
592
|
+
: [ `${asName}.`, this.visit(r.expression), ` as ${this.visit(r.alias)}`]
|
|
593
|
+
) ] : [];
|
|
594
|
+
return prepare `WITH ${joinName} as (${join}) UPDATE ${table} ${asName} SET ${set} FROM ${joinName} WHERE ${where} ${returning}`;
|
|
591
595
|
|
|
592
596
|
}
|
|
593
597
|
|
|
@@ -20,16 +20,16 @@ export default async function(this: TestConfig) {
|
|
|
20
20
|
|
|
21
21
|
// assert.notEqual(null, user);
|
|
22
22
|
|
|
23
|
-
report = await context.users.all()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
// report = await context.users.all()
|
|
24
|
+
// // .where({}, (p) => (x) => x.orders.some((oi) => oi.customerID > 0))
|
|
25
|
+
// .map({}, (p) => (x) => ({
|
|
26
|
+
// total: 5 * Sql.coll.sum(x.orders.map((o) => Sql.coll.sum(o.orderItems.map((oi) => oi.amount))))
|
|
27
|
+
// })
|
|
28
|
+
// )
|
|
29
|
+
// .trace(console.log)
|
|
30
|
+
// .first();
|
|
31
31
|
|
|
32
|
-
assert.notEqual(null, report);
|
|
32
|
+
// assert.notEqual(null, report);
|
|
33
33
|
|
|
34
34
|
// await context.orders.asQuery()
|
|
35
35
|
// .update(void 0, (p) => (x) => ({
|
|
@@ -47,4 +47,14 @@ export default async function(this: TestConfig) {
|
|
|
47
47
|
.first();
|
|
48
48
|
|
|
49
49
|
assert.notEqual(null, report);
|
|
50
|
+
|
|
51
|
+
// const r = await context.users.all()
|
|
52
|
+
// .trace(console.log)
|
|
53
|
+
// .sum(void 0, (p) => (x) => ({
|
|
54
|
+
// paid: x.orders.some((o) => o.status === "pending") ? 1 : 0,
|
|
55
|
+
// total: 1
|
|
56
|
+
// }));
|
|
57
|
+
|
|
58
|
+
// assert.notEqual(r.paid, undefined);
|
|
59
|
+
// assert.notEqual(r.total, undefined);
|
|
50
60
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { Sql } from "../../../index.js";
|
|
3
|
+
import { TestConfig } from "../../TestConfig.js";
|
|
4
|
+
import { createContext } from "../../model/createContext.js";
|
|
5
|
+
|
|
6
|
+
export default async function(this: TestConfig) {
|
|
7
|
+
|
|
8
|
+
if (!this.db) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const context = await createContext(this.driver);
|
|
13
|
+
|
|
14
|
+
const first = await context.products.all().first();
|
|
15
|
+
|
|
16
|
+
first.name = "First product";
|
|
17
|
+
|
|
18
|
+
await context.saveChanges();
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const results = await context.products
|
|
22
|
+
.where(void 0, (p) => (x) => x.productID > 1)
|
|
23
|
+
.trace(console.log)
|
|
24
|
+
.updateSelect(void 0, (p) => (x) => ({
|
|
25
|
+
productDescription: "updated"
|
|
26
|
+
}));
|
|
27
|
+
assert.equal(results[0].productDescription, "updated");
|
|
28
|
+
}
|