From 28de1f5622dbf2e0d6c8f76f6f6324295794a835 Mon Sep 17 00:00:00 2001 From: Hieuzest Date: Tue, 19 May 2026 16:53:33 +0800 Subject: [PATCH 1/2] feat: support $.first aggr --- packages/core/src/eval.ts | 4 ++++ packages/mongo/src/builder.ts | 2 +- packages/sql-utils/src/index.ts | 1 + packages/tests/src/query.ts | 26 +++++++++++++------------- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/core/src/eval.ts b/packages/core/src/eval.ts index fa69e972..d88e69b7 100644 --- a/packages/core/src/eval.ts +++ b/packages/core/src/eval.ts @@ -138,6 +138,7 @@ export namespace Eval { number: Unary // aggregation / json + first: Aggr sum: Aggr avg: Aggr max: Aggr @@ -290,6 +291,9 @@ const unwrapAggr = (expr: any, def?: Type) => { } // aggregation +Eval.first = unary('first', (expr, table) => Array.isArray(table) + ? table.map(data => executeAggr(expr, data))[0] + : Array.from(executeEval(table, expr))[0], (expr) => unwrapAggr(expr)) Eval.sum = unary('sum', (expr, table) => Array.isArray(table) ? table.reduce((prev, curr) => prev + executeAggr(expr, curr), 0) : Array.from(executeEval(table, expr)).reduce((prev, curr) => prev + curr, 0), Type.Number) diff --git a/packages/mongo/src/builder.ts b/packages/mongo/src/builder.ts index 914b37e0..b35d1ed5 100644 --- a/packages/mongo/src/builder.ts +++ b/packages/mongo/src/builder.ts @@ -83,7 +83,7 @@ export type EvalOperators = { [K in keyof Eval.Static as `$${K}`]?: (expr: ExtractUnary>, group?: object) => any } & { $: (expr: any, group?: object) => any } -const aggrKeys = ['$sum', '$avg', '$min', '$max', '$count', '$length', '$array'] +const aggrKeys = ['$first', '$sum', '$avg', '$min', '$max', '$count', '$length', '$array'] export class Builder { private counter = 0 diff --git a/packages/sql-utils/src/index.ts b/packages/sql-utils/src/index.ts index d9f0e008..df79e58e 100644 --- a/packages/sql-utils/src/index.ts +++ b/packages/sql-utils/src/index.ts @@ -186,6 +186,7 @@ export class Builder { $literal: ([value, type]) => this.escape(value, type as any), // aggregation + $first: (expr) => this.parseEval(expr, false), $sum: (expr) => this.createAggr(expr, value => `ifnull(sum(${value}), 0)`), $avg: (expr) => this.createAggr(expr, value => `avg(${value})`), $min: (expr) => this.createAggr(expr, value => `min(${value})`), diff --git a/packages/tests/src/query.ts b/packages/tests/src/query.ts index 6b83e909..35d9fa6c 100644 --- a/packages/tests/src/query.ts +++ b/packages/tests/src/query.ts @@ -318,19 +318,19 @@ namespace QueryOperators { row => $.eq($.xor(0, row.value, 3), 7), )).eventually.to.have.shape([{ value: 4 }]) - await expect(database.eval('temp1', _ => $.max($.not(2 ** 30)))).eventually.to.deep.equal(-(2 ** 30) - 1) - await expect(database.eval('temp1', _ => $.max($.not(-(2 ** 30))))).eventually.to.deep.equal(2 ** 30 - 1) - await expect(database.eval('temp1', _ => $.max($.or(-(2 ** 30), 1)))).eventually.to.deep.equal(-(2 ** 30) + 1) - - await expect(database.eval('temp1', _ => $.max($.xor(2, 3, 6)))).eventually.to.deep.equal(7) - await expect(database.eval('temp1', _ => $.array($.xor(true, false)))).eventually.to.include.members([true]) - await expect(database.eval('temp1', _ => $.array($.xor(true, false, true)))).eventually.to.include.members([false]) - - await expect(database.eval('temp1', _ => $.max($.not(BigInt(2 ** 40))))).eventually.to.deep.equal(BigInt(-(2 ** 40) - 1)) - await expect(database.eval('temp1', _ => $.max($.and(9223372036854775701n, 9223372036854775702n)))).eventually.to.deep.equal(9223372036854775700n) - await expect(database.eval('temp1', _ => $.max($.or(9223372036854775701n, 1n)))).eventually.to.deep.equal(9223372036854775701n) - await expect(database.eval('temp1', _ => $.max($.xor(9223372036854775701n, 9223372036854775702n)))).eventually.to.deep.equal(3n) - await expect(database.eval('temp1', _ => $.max($.not(9223372036854775701n)))).eventually.to.deep.equal(-9223372036854775702n) + await expect(database.eval('temp1', _ => $.first($.not(2 ** 30)))).eventually.to.deep.equal(-(2 ** 30) - 1) + await expect(database.eval('temp1', _ => $.first($.not(-(2 ** 30))))).eventually.to.deep.equal(2 ** 30 - 1) + await expect(database.eval('temp1', _ => $.first($.or(-(2 ** 30), 1)))).eventually.to.deep.equal(-(2 ** 30) + 1) + + await expect(database.eval('temp1', _ => $.first($.xor(2, 3, 6)))).eventually.to.deep.equal(7) + await expect(database.eval('temp1', _ => $.first($.xor(true, false)))).eventually.to.deep.equal(true) + await expect(database.eval('temp1', _ => $.first($.xor(true, false, true)))).eventually.to.deep.equal(false) + + await expect(database.eval('temp1', _ => $.first($.not(BigInt(2 ** 40))))).eventually.to.deep.equal(BigInt(-(2 ** 40) - 1)) + await expect(database.eval('temp1', _ => $.first($.and(9223372036854775701n, 9223372036854775702n)))).eventually.to.deep.equal(9223372036854775700n) + await expect(database.eval('temp1', _ => $.first($.or(9223372036854775701n, 1n)))).eventually.to.deep.equal(9223372036854775701n) + await expect(database.eval('temp1', _ => $.first($.xor(9223372036854775701n, 9223372036854775702n)))).eventually.to.deep.equal(3n) + await expect(database.eval('temp1', _ => $.first($.not(9223372036854775701n)))).eventually.to.deep.equal(-9223372036854775702n) }) } From f31852491a46e5f5441dbcbdda724cdb90bc9bf3 Mon Sep 17 00:00:00 2001 From: Hieuzest Date: Tue, 19 May 2026 17:25:22 +0800 Subject: [PATCH 2/2] fix(mysql): incorrect transform of boolean '0' to true --- packages/mysql/src/builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mysql/src/builder.ts b/packages/mysql/src/builder.ts index b6e490ea..56fa8d65 100644 --- a/packages/mysql/src/builder.ts +++ b/packages/mysql/src/builder.ts @@ -78,7 +78,7 @@ export class MySQLBuilder extends Builder { this.transformers['boolean'] = { encode: value => `if(${value}=true, 1, 0)`, decode: value => `if(${value}=1, true, false)`, - load: value => isNullable(value) ? value : !!value, + load: value => isNullable(value) ? value : !!+value, dump: value => isNullable(value) ? value : value ? 1 : 0, }