From a7da54cf27f072cf1eaf2af14f05580f246515d9 Mon Sep 17 00:00:00 2001 From: Hieuzest Date: Sun, 31 Dec 2023 00:51:39 +0800 Subject: [PATCH] revert(core): back to original join model --- packages/core/src/selection.ts | 10 ++++++++-- packages/memory/src/index.ts | 21 ++++++--------------- packages/mongo/src/utils.ts | 23 +++++++++++++++-------- packages/sql-utils/src/index.ts | 1 + packages/tests/src/selection.ts | 9 +++++++++ 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/packages/core/src/selection.ts b/packages/core/src/selection.ts index c70bf015..2c5a3711 100644 --- a/packages/core/src/selection.ts +++ b/packages/core/src/selection.ts @@ -55,9 +55,15 @@ class Executable { constructor(driver: Driver, payload: Executable.Payload) { Object.assign(this, payload) - defineProperty(this, 'driver', driver) defineProperty(this, 'model', driver.model(this.table)) - defineProperty(this, 'row', createRow(this.ref, {}, '', this.model)) + const expr = { $model: this.model } + if (typeof payload.table !== 'string' && !(payload.table instanceof Selection)) { + for (const key in payload.table) { + expr[key] = createRow(key, {}, '', this.model) + } + } + defineProperty(this, 'driver', driver) + defineProperty(this, 'row', createRow(this.ref, expr, '', this.model)) } protected resolveQuery(query?: Query): Query.Expr diff --git a/packages/memory/src/index.ts b/packages/memory/src/index.ts index 4d0a3112..85ef1fdb 100644 --- a/packages/memory/src/index.ts +++ b/packages/memory/src/index.ts @@ -28,34 +28,25 @@ export class MemoryDriver extends Driver { // await this.#loader?.stop(this.#store) } - table(sel: string | Selection.Immutable | Dict, env: any = {}): any[] { + table(sel: string | Selection.Immutable | Dict, env: any = {}, expr?: any): any[] { if (typeof sel === 'string') { return this.#store[sel] ||= [] } if (!(sel instanceof Selection)) { - throw new Error('Should not reach here') - } - - const { ref, query, table, args, model } = sel - const { fields, group, having } = sel.args[0] - - let data: any[] - - if (typeof table === 'object' && !(table instanceof Selection)) { - const entries = Object.entries(table).map(([name, sel]) => [name, this.table(sel, env)] as const) + const entries = Object.entries(sel).map(([name, sel]) => [name, this.table(sel, env)] as const) const catesian = (entries: (readonly [string, any[]])[]): any[] => { if (!entries.length) return [] const [[name, rows], ...tail] = entries if (!tail.length) return rows.map(row => ({ [name]: row })) return rows.flatMap(row => catesian(tail).map(tail => ({ ...tail, [name]: row }))) } - data = catesian(entries).map(x => ({ ...env, [ref]: x })).filter(data => executeEval(data, having)).map(x => x[ref]) - } else { - data = this.table(table, env).filter(row => executeQuery(row, query, ref, env)) + return catesian(entries).filter(data => executeEval({ ...env, ...data }, expr)) } - env[ref] = data + const { ref, query, table, args, model } = sel + const { fields, group, having } = sel.args[0] + const data = this.table(table, env, having).filter(row => executeQuery(row, query, ref, env)) const branches: { index: Dict; table: any[] }[] = [] const groupFields = group ? pick(fields!, group) : fields diff --git a/packages/mongo/src/utils.ts b/packages/mongo/src/utils.ts index b528c558..35892321 100644 --- a/packages/mongo/src/utils.ts +++ b/packages/mongo/src/utils.ts @@ -108,7 +108,7 @@ export class Transformer { } else if (this.refTables.includes(arg[0])) { return `$$${arg[0]}.` + this.getActualKey(arg[1], arg[0]) } else { - throw new Error(`$ not transformed: ${JSON.stringify(arg)}`) + return this.recursivePrefix + this.getActualKey(arg[1]) } }, $if: (arg, group) => ({ $cond: arg.map(val => this.eval(val, group)) }), @@ -382,7 +382,8 @@ export class Transformer { this.table = predecessor.table this.pipeline.push(...predecessor.flushLookups(), ...predecessor.pipeline) } else { - for (const [name, subtable] of Object.entries(table)) { + const refs: string[] = [] + Object.entries(table).forEach(([name, subtable], i) => { const predecessor = this.createSubquery(subtable) if (!predecessor) return if (!this.table) { @@ -390,22 +391,28 @@ export class Transformer { this.pipeline.push(...predecessor.flushLookups(), ...predecessor.pipeline, { $replaceRoot: { newRoot: { [name]: '$$ROOT' } }, }) - continue + refs.push(name) + return + } + if (sel.args[0].having['$and'].length && i === Object.keys(table).length - 1) { + const thisRefTables = this.refTables + this.refTables = [...this.refTables, ...refs] + const $expr = this.eval(sel.args[0].having['$and'][0]) + predecessor.pipeline.push(...this.flushLookups(), { $match: { $expr } }) + this.refTables = thisRefTables } const $lookup = { from: predecessor.table, as: name, + let: Object.fromEntries(refs.map(name => [name, `$$ROOT.${name}`])), pipeline: predecessor.pipeline, } const $unwind = { path: `$${name}`, } this.pipeline.push({ $lookup }, { $unwind }) - } - if (sel.args[0].having['$and'].length) { - const $expr = this.eval(sel.args[0].having) - this.pipeline.push(...this.flushLookups(), { $match: { $expr } }) - } + refs.push(name) + }) } // where diff --git a/packages/sql-utils/src/index.ts b/packages/sql-utils/src/index.ts index 2be671a9..f4c9a279 100644 --- a/packages/sql-utils/src/index.ts +++ b/packages/sql-utils/src/index.ts @@ -480,6 +480,7 @@ export class Builder { } else { const sqlTypes: Dict = {} const joins: string[] = Object.entries(table).map(([key, table]) => { + this.state.tables![key] = this.state.tables![table.ref] const restore = this.saveState({ tables: table.tables }) const t = `${this.get(table, true, false, false)} AS ${this.escapeId(key)}` for (const [fieldKey, fieldType] of Object.entries(this.state.sqlTypes!)) { diff --git a/packages/tests/src/selection.ts b/packages/tests/src/selection.ts index 53412481..d620497e 100644 --- a/packages/tests/src/selection.ts +++ b/packages/tests/src/selection.ts @@ -1,6 +1,7 @@ import { $, Database } from '@minatojs/core' import { expect } from 'chai' import { setup } from './utils' +import MongoDriver from '@minatojs/driver-mongo' interface Foo { id: number @@ -442,6 +443,14 @@ namespace SelectionTests { ).to.eventually.have.length(6) }) + it('join inside subquery', async () => { + const sel = database.select('foo').project({ + x: row => database.join(['foo', 'bar'] as const, (foo, bar) => $.eq(foo.id, row.id)) + .evaluate(r => $.count(r.foo.id)), + }) + await expect(database.select(sel).execute(row => $.sum(row.x))).to.eventually.equal(3) + }) + it('join selection', async () => { await expect(database .select(