Skip to content

Commit 1d85274

Browse files
authored
Added Optimizer, fixed sort and in SubQuery (#215)
2 parents e74f16d + 92748c7 commit 1d85274

16 files changed

+3635
-179
lines changed

docs/docs/overrides/home.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ <h2>
344344
<div class="md-footer-meta__inner md-grid">
345345
<div class="md-copyright">
346346
<div class="md-copyright__highlight">
347-
Copyright © 2023
347+
Copyright © 2025
348348
<a href="https://synatic.com">Synatic Inc.</a>
349349
</div>
350350

lib/SQLParser.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const {parseSQLtoAST} = require('./parseSQLtoAST');
22
const {canQuery} = require('./canQuery');
33
const {makeMongoAggregate, makeMongoQuery} = require('./make');
44
const {getResultSchemaForStatement} = require('./metadata');
5+
const optimizer = require('./optimizer');
56

67
/**
78
* @typedef {import('./types').ParserOptions} ParserOptions
@@ -128,6 +129,16 @@ class SQLParser {
128129
options
129130
);
130131
}
132+
133+
/**
134+
* Optimizes a Mongo Aggregate
135+
*
136+
* @param {Object[]} mongoAggregate - the mongo aggregate to optimize
137+
* @param {Object} options - the optimization options
138+
*/
139+
static optimizeMongoAggregate(mongoAggregate, options = {}) {
140+
return optimizer.optimizeMongoAggregate(mongoAggregate, options);
141+
}
131142
}
132143

133144
module.exports = SQLParser;

lib/arraySequenceIndexOf.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Finds the starting index of the first occurrence of a sequence within an array, using a custom comparison function.
3+
* @param {Array} sequenceArr - The sequence of elements to search for.
4+
* @param {Array} inArray - The array in which to search for the sequence.
5+
* @param {number} startIndex - the index to start searching from
6+
* @param {Function} [compareFn] - Optional custom comparison function that takes two arguments and returns a boolean indicating whether they are equal.
7+
* @returns {number} The starting index of the first occurrence of the sequence in the array or -1 if the sequence is not found.
8+
*/
9+
function arraySequenceIndexOf(sequenceArr, inArray, startIndex = 0, compareFn) {
10+
compareFn =
11+
compareFn ||
12+
function (a, b) {
13+
return a === b;
14+
};
15+
if (sequenceArr.length === 0) return 0;
16+
if (sequenceArr.length === 1) {
17+
return inArray.findIndex((a) => compareFn(a, sequenceArr[0]));
18+
}
19+
if (sequenceArr.length > inArray.length - startIndex) return -1;
20+
21+
let i = startIndex;
22+
while (i < inArray.length) {
23+
if (!compareFn(sequenceArr[0], inArray[i])) {
24+
i++;
25+
continue;
26+
}
27+
if (inArray.length < i + sequenceArr.length) {
28+
return -1;
29+
}
30+
let j = 0;
31+
let seqTrue = true;
32+
while (seqTrue && j < sequenceArr.length) {
33+
seqTrue = seqTrue && compareFn(sequenceArr[j], inArray[i + j]);
34+
j++;
35+
}
36+
if (seqTrue) {
37+
return i;
38+
}
39+
40+
i++;
41+
}
42+
43+
return -1;
44+
}
45+
46+
module.exports = arraySequenceIndexOf;

lib/canQuery.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,28 @@ function checkWhereContainsOtherTable(expr) {
180180
return false;
181181
}
182182

183+
function checkWhereExpressionIsSubQuery(expr) {
184+
if (!expr) {
185+
return false;
186+
}
187+
if (expr.type === 'binary_expr') {
188+
if (!['IN', 'NOT IN', '='].includes(expr.operator)) {
189+
return false;
190+
}
191+
return (
192+
checkWhereExpressionIsSubQuery(expr.left) ||
193+
checkWhereExpressionIsSubQuery(expr.right)
194+
);
195+
}
196+
if (expr.type === 'expr_list') {
197+
return expr.value.reduce((a, v) => a || (v && v.ast), false);
198+
}
199+
200+
return false;
201+
}
202+
183203
module.exports = {
184204
canQuery,
185205
whereContainsOtherTable: checkWhereContainsOtherTable,
206+
checkWhereExpressionIsSubQuery: checkWhereExpressionIsSubQuery,
186207
};

lib/make/filter-queries.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ function getWhereAstQueries(where, context) {
99
if (where.right.value) {
1010
return [
1111
{
12-
column: where.left.column,
12+
column: where.left.table?(where.left.table + '.' + where.left.column) : where.left.column,
1313
ast: where.right.value.filter(notAStandardValue),
1414
},
1515
];

lib/make/index.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const {createResultObject} = require('./createResultObject');
44
const makeQueryPartModule = require('./makeQueryPart');
55
const projectColumnParserModule = require('./projectColumnParser');
66
const makeAggregatePipelineModule = require('./makeAggregatePipeline');
7+
const arraySequenceIndexOf = require('../arraySequenceIndexOf');
8+
const lodash = require('lodash');
79
/**
810
* Parses a sql statement into a mongo aggregate pipeline
911
*
@@ -22,13 +24,34 @@ function makeMongoAggregate(sqlOrAST, options = {unwindJoins: false}, context) {
2224
context = newContext;
2325
}
2426
// todo fix sub select table return
27+
28+
const pipeline = makeAggregatePipelineModule.makeAggregatePipeline(
29+
ast,
30+
context
31+
);
32+
const tables = context.tables.filter((x, i, a) => a.indexOf(x) === i);
33+
if (
34+
context &&
35+
context._reorderedTables &&
36+
context._reorderedTables.length > 0
37+
) {
38+
const tablesIndexOf = arraySequenceIndexOf(
39+
lodash.reverse(context._reorderedTables),
40+
tables
41+
);
42+
if (tablesIndexOf > -1) {
43+
tables.splice(
44+
tablesIndexOf,
45+
context._reorderedTables.length,
46+
...lodash.reverse(context._reorderedTables)
47+
);
48+
}
49+
}
50+
2551
return {
26-
pipeline: makeAggregatePipelineModule.makeAggregatePipeline(
27-
ast,
28-
context
29-
),
52+
pipeline: pipeline,
3053
// subselect for arrays need to remove the collections since theyre actually arrays
31-
collections: context.tables.filter((x, i, a) => a.indexOf(x) === i),
54+
collections: tables,
3255
type: 'aggregate',
3356
};
3457
}

0 commit comments

Comments
 (0)