From 6b8ff19a9e723cb0a2d3467634c7a39037b0f618 Mon Sep 17 00:00:00 2001 From: Martin Naude Date: Mon, 30 Jan 2023 14:44:44 +0200 Subject: [PATCH 1/2] Fixes: - STRPOS 1 based index as per postgres Enhancements - NOT query support New Functions - LOCATE MySQL function --- lib/MongoFunctions.js | 17 +++- lib/make/makeQueryPart.js | 32 ++++++++ test/aggregateTests/aggregateTests.json | 30 ++++--- test/queryTests/comparisonOperators.json | 28 +++++++ test/queryTests/stringOperators.json | 99 ++++++++++++++++++++++-- 5 files changed, 189 insertions(+), 17 deletions(-) diff --git a/lib/MongoFunctions.js b/lib/MongoFunctions.js index 934ccea6..f97cea6b 100644 --- a/lib/MongoFunctions.js +++ b/lib/MongoFunctions.js @@ -603,7 +603,22 @@ class AllowableFunctions { } return { - $indexOfCP: parameters, + $add: [{$indexOfCP: parameters}, 1], + }; + }, + }, + { + name: 'locate', + allowQuery: true, + parse: (parameters) => { + if (!$check.array(parameters)) + throw new Error('Invalid parameters for substring'); + if (parameters.length !== 2) { + throw new Error('Invalid parameters starts_with'); + } + + return { + $add: [{$indexOfCP: [parameters[1], parameters[0]]}, 1], }; }, }, diff --git a/lib/make/makeQueryPart.js b/lib/make/makeQueryPart.js index d362c0c3..10797edd 100644 --- a/lib/make/makeQueryPart.js +++ b/lib/make/makeQueryPart.js @@ -160,12 +160,25 @@ function makeQueryPart( throw new Error(`Unsupported operator:${queryPart.operator}`); } + if (queryPart.type === 'function' && queryPart.name === 'NOT') { + return { + $nor: makeQueryPart( + queryPart.args, + ignorePrefix, + allowedTypes, + includeThis, + tableAlias + ), + }; + } + if (queryPart.type === 'function' || queryPart.type === 'select') return makeProjectionExpressionPartModule.makeProjectionExpressionPart( queryPart, 0, true ); + if (queryPart.type === 'expr_list') { return queryPart.value.map((v) => makeQueryPart(v)); } @@ -189,6 +202,25 @@ function makeQueryPart( }; } + // NOT Expression + if ( + queryPart.type === 'unary_expr' && + queryPart.operator === 'NOT' && + queryPart.expr + ) { + const exprQuery = makeQueryPart( + queryPart.expr, + ignorePrefix, + allowedTypes, + includeThis, + tableAlias + ); + + return { + $nor: $check.array(exprQuery) ? exprQuery : [exprQuery], + }; + } + // todo add not if (queryPart.type === 'aggr_func') { diff --git a/test/aggregateTests/aggregateTests.json b/test/aggregateTests/aggregateTests.json index 38548de0..3a6e1494 100644 --- a/test/aggregateTests/aggregateTests.json +++ b/test/aggregateTests/aggregateTests.json @@ -1730,7 +1730,7 @@ }, { "name": "Aggregate:strpos", - "query": " select strpos(Title,'B') as x,* from films where strpos(Title,'B') > 0", + "query": " select strpos(Title,'B') as x,* from films where strpos(Title,'B') > 1", "type": "aggregate", "output": { "pipeline": [ @@ -1739,14 +1739,19 @@ "$expr": { "$gt": [ { - "$indexOfCP": [ - "$Title", + "$add": [ { - "$literal": "B" - } + "$indexOfCP": [ + "$Title", + { + "$literal": "B" + } + ] + }, + 1 ] }, - 0 + 1 ] } } @@ -1758,11 +1763,16 @@ "$$ROOT", { "x": { - "$indexOfCP": [ - "$Title", + "$add": [ { - "$literal": "B" - } + "$indexOfCP": [ + "$Title", + { + "$literal": "B" + } + ] + }, + 1 ] } } diff --git a/test/queryTests/comparisonOperators.json b/test/queryTests/comparisonOperators.json index 05639ef6..74d40315 100644 --- a/test/queryTests/comparisonOperators.json +++ b/test/queryTests/comparisonOperators.json @@ -585,6 +585,34 @@ } } + } + }, + { + "name": "Comparison:NOT OR", + "query": "select * from films where NOT (Title = 'Test' or Title = 'test2')", + "output": { + "collection": "films", + "limit": 100, + "query": { + "$nor": [ + { + "$or": [ + { + "Title": { + "$eq": "Test" + } + }, + { + "Title": { + "$eq": "test2" + } + } + ] + } + ] + + } + } } ] diff --git a/test/queryTests/stringOperators.json b/test/queryTests/stringOperators.json index dca76027..d335958d 100644 --- a/test/queryTests/stringOperators.json +++ b/test/queryTests/stringOperators.json @@ -199,17 +199,22 @@ }, { "name": "String:STRPOS", - "query": "select strpos(Title,'B') as x,filmId from films where strpos(Title,'B') > -1", + "query": "select strpos(Title,'B') as x,filmId from films where strpos(Title,'B') > 0", "output": { "collection": "films", "limit": 100, "projection": { "x": { - "$indexOfCP": [ - "$Title", + "$add": [ { - "$literal": "B" - } + "$indexOfCP": [ + "$Title", + { + "$literal": "B" + } + ] + }, + 1 ] }, "filmId": "$filmId" @@ -217,6 +222,34 @@ "query": { "$expr": { "$gt": [ + { + "$add": [ + { + "$indexOfCP": [ + "$Title", + { + "$literal": "B" + } + ] + }, + 1 + ] + }, + 0 + ] + } + } + } + }, + { + "name": "String:LOCATE", + "query": "select locate('B',Title) as x,filmId from films where locate('B',Title) > 0", + "output": { + "collection": "films", + "limit": 100, + "projection": { + "x": { + "$add": [ { "$indexOfCP": [ "$Title", @@ -225,12 +258,66 @@ } ] }, - -1 + 1 + ] + }, + "filmId": "$filmId" + }, + "query": { + "$expr": { + "$gt": [ + { + "$add": [ + { + "$indexOfCP": [ + "$Title", + { + "$literal": "B" + } + ] + }, + 1 + ] + }, + 0 ] } } } }, + { + "name": "String:NOT STRPOS", + "query": "select * from films where NOT strpos(Title,'B')>0", + "output": { + "collection": "films", + "limit": 100, + "query": { + "$nor": [ + { + "$expr": { + "$gt": [ + { + "$add": [ + { + "$indexOfCP": [ + "$Title", + { + "$literal": "B" + } + ] + }, + 1 + ] + }, + 0 + ] + } + } + ] + } + } + }, + { "name": "String:STARTS_WITH", "query": "select starts_with(Title,'B') as x,filmId from films where starts_with(Title,'B') is true", From 38b7754866f58803dd77edcb90ce6b9f2d0bb268 Mon Sep 17 00:00:00 2001 From: Martin Naude Date: Mon, 30 Jan 2023 14:45:10 +0200 Subject: [PATCH 2/2] 1.1.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8daa9505..70fb8591 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@synatic/sql-to-mongo", - "version": "1.1.7", + "version": "1.1.8", "description": "Convert SQL to mongo queries or aggregates", "main": "index.js", "files": [