From 03e85bf53af97e2cb736201b7d66ad5eb3675913 Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Thu, 12 Sep 2024 09:20:05 +0200 Subject: [PATCH 1/2] feat(auto-unwind): adding tests and option to automatically unwind all joins --- lib/make/makeJoinForPipeline.js | 9 +++++---- test/bug-fix-tests/bug-fix.test.js | 5 ++++- test/joins/join-cases.json | 25 +++++++++++++++++++++++++ test/joins/joins.test.js | 14 ++++++++++++++ test/utils/query-tester/query-tester.js | 4 ++++ test/utils/query-tester/types.ts | 6 ++---- 6 files changed, 54 insertions(+), 9 deletions(-) diff --git a/lib/make/makeJoinForPipeline.js b/lib/make/makeJoinForPipeline.js index 1db4d606..612a0dbf 100644 --- a/lib/make/makeJoinForPipeline.js +++ b/lib/make/makeJoinForPipeline.js @@ -184,7 +184,8 @@ function makeJoinPart(join, previousJoin, aliases, pipeline, context, ast) { pipeline: lookupPipeline, }, }); - if (joinHints && joinHints.length > 0) { + + if ((joinHints && joinHints.length > 0) || context.unwindJoins) { if (joinHints.includes('first')) { pipeline.push({ $set: { @@ -197,7 +198,7 @@ function makeJoinPart(join, previousJoin, aliases, pipeline, context, ast) { [toAs || toTable]: {$last: `$${toAs || toTable}`}, }, }); - } else if (joinHints.includes('unwind')) { + } else if (joinHints.includes('unwind') || context.unwindJoins) { pipeline.push({ $unwind: { path: `$${toAs || toTable}`, @@ -331,7 +332,7 @@ function tableJoin( foreignField: foreignField, }, }); - if (joinHints && joinHints.length > 0) { + if ((joinHints && joinHints.length > 0) || context.unwindJoins) { if (joinHints.includes('first')) { pipeline.push({ $set: { @@ -344,7 +345,7 @@ function tableJoin( [toAs || toTable]: {$last: `$${toAs || toTable}`}, }, }); - } else if (joinHints.includes('unwind')) { + } else if (joinHints.includes('unwind') || context.unwindJoins) { pipeline.push({ $unwind: { path: `$${toAs || toTable}`, diff --git a/test/bug-fix-tests/bug-fix.test.js b/test/bug-fix-tests/bug-fix.test.js index 3dbc18aa..e56217bd 100644 --- a/test/bug-fix-tests/bug-fix.test.js +++ b/test/bug-fix-tests/bug-fix.test.js @@ -416,6 +416,7 @@ describe('bug-fixes', function () { await queryResultTester({ queryString: queryString, casePath: 'bugfix.to_objectid.case1', + unsetId: false, }); }); }); @@ -430,6 +431,7 @@ describe('bug-fixes', function () { await queryResultTester({ queryString: queryString, casePath: 'bugfix.object_to_array.case1', + unsetId: false, }); }); }); @@ -1019,8 +1021,9 @@ describe('bug-fixes', function () { await queryResultTester({ queryString: queryString, casePath: 'post-optimization.case1', - mode: 'test', + mode, outputPipeline: false, + unsetId: false, }); }); }); diff --git a/test/joins/join-cases.json b/test/joins/join-cases.json index 6979b76f..9e21d276 100644 --- a/test/joins/join-cases.json +++ b/test/joins/join-cases.json @@ -1850,5 +1850,30 @@ } ] } + }, + "auto-unwind": { + "left": { + "case1": { + "expectedResults": [ + { + "o": { + "id": 1, + "item": "almonds", + "price": 12, + "quantity": 2, + "customerId": 1, + "specialChars": "^^^" + }, + "i": { + "id": 1, + "sku": "almonds", + "description": "product 1", + "instock": 120, + "specialChars": "^^^" + } + } + ] + } + } } } \ No newline at end of file diff --git a/test/joins/joins.test.js b/test/joins/joins.test.js index e880563f..c1f94cb2 100644 --- a/test/joins/joins.test.js +++ b/test/joins/joins.test.js @@ -35,6 +35,7 @@ describe('joins', function () { after(function (done) { disconnect().then(done).catch(done); }); + describe('regression tests', () => { it('should work for case 1', async () => { await queryResultTester({ @@ -674,6 +675,7 @@ describe('joins', function () { queryString, casePath: 'optimize.explicit', mode: 'write', + unsetId: false, }); assert.deepStrictEqual(pipeline, expectedPipeline); @@ -747,4 +749,16 @@ describe('joins', function () { }); }); }); + + describe('Automatic Unwinding of joins', () => { + it('should be able to automatically unwind a left join', async () => { + await queryResultTester({ + queryString: + 'select *, unset(_id,o._id,o.orderDate,i._id) from orders as o left join `inventory` as i on o.item=i.sku limit 1', + casePath: 'auto-unwind.left.case1', + mode, + unwindJoins: true, + }); + }); + }); }); diff --git a/test/utils/query-tester/query-tester.js b/test/utils/query-tester/query-tester.js index 2a8413fe..55019dc7 100644 --- a/test/utils/query-tester/query-tester.js +++ b/test/utils/query-tester/query-tester.js @@ -46,12 +46,16 @@ async function queryResultTester(options) { ignoreDateValues = false, outputPipeline = false, schemas, + unwindJoins = false, + unsetId = true, } = options; if (!fileName.endsWith('.json')) { fileName = fileName + '.json'; } const {collections, pipeline} = SQLParser.makeMongoAggregate(queryString, { schemas, + unwindJoins, + unsetId, }); const filePath = $path.resolve(dirName, fileName); /** @type {import("mongodb").Document[]} */ diff --git a/test/utils/query-tester/types.ts b/test/utils/query-tester/types.ts index b2756052..1521b174 100644 --- a/test/utils/query-tester/types.ts +++ b/test/utils/query-tester/types.ts @@ -1,5 +1,5 @@ import {MongoClient, Document} from 'mongodb'; -import {PipelineFn, Schemas} from '../../../lib/types'; +import {PipelineFn, Schemas, ParserOptions} from '../../../lib/types'; /** Options to use when running the function to test/generate test outputs */ export interface BuildQueryResultOptions { @@ -13,7 +13,7 @@ export interface BuildQueryResultOptions { mode?: 'write' | 'test'; } /** Options to use when running the function to test/generate test outputs */ -export interface QueryResultOptions { +export interface QueryResultOptions extends ParserOptions { /** The query string to run against the db */ queryString: string; /** The JSON path in the target file at which to store the results */ @@ -26,8 +26,6 @@ export interface QueryResultOptions { ignoreDateValues?: boolean; /** Specifies if the pipeline should be written to the file, useful for debugging */ outputPipeline?: boolean; - /** Specifies a map of schemas the library should use that to produce better queries */ - schemas?: Schemas; } export type AllQueryResultOptions = BuildQueryResultOptions & From 8088ed4dab6bf00ff2c34e20a04576221686855d Mon Sep 17 00:00:00 2001 From: Ryan Kotzen Date: Thu, 12 Sep 2024 09:20:12 +0200 Subject: [PATCH 2/2] 4.1.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0745a623..4edadd9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@synatic/noql", - "version": "4.1.7", + "version": "4.1.8", "description": "Convert SQL statements to mongo queries or aggregates", "main": "index.js", "files": [