13 Commits
Author SHA1 Message Date
harish2704 eee8f1c975 * Added documentation
* Release V 1.0.0
2020-02-23 23:07:34 +05:30
harish2704 53058d58bf Support '$or' & '$and' based grouping for queries 2020-02-23 23:01:53 +05:30
harish2704andGitHub ae0663d314 Merge pull request #2 from joselcvarela/master
Add raw statement for special handling like postgres operators
2019-03-16 00:53:10 +05:30
Jose Varelaandjoselcvarela 4422e9e424 Handle conditional arrays 2018-11-30 09:35:29 +00:00
joselcvarela d96011dc19 Add or_raw statement 2018-11-28 08:45:23 +00:00
Jose Varelaandjoselcvarela 0f93490366 Add raw statement for special handling like postgres operators 2018-11-16 10:24:30 +00:00
harish2704andGitHub 91301c839a Merge pull request #1 from joselcvarela/not-in-statement
Add 'where not in' statement
2018-11-15 09:25:50 +05:30
Jose Varela e44ebb33f2 Add 'where not in' statement 2018-11-11 17:17:01 +00:00
harish2704 26a9b57e90 Fix test runner 2017-03-29 19:35:11 +05:30
harish2704 13427005fe V0.0.5
Added support for mongo like query. Fully backward compatible
2017-03-29 13:03:15 +05:30
harish2704 68bdbf750c Added support for isnull and notnull query 2017-02-16 18:00:04 +05:30
harish2704 498ac917d4 Version 2017-02-09 16:13:39 +05:30
harish2704 e850edcdda Fix. Do not mutate the input raw Query 2017-02-09 16:11:32 +05:30
5 changed files with 293 additions and 27 deletions
+1
View File
@@ -35,3 +35,4 @@ jspm_packages
# Optional REPL history # Optional REPL history
.node_repl_history .node_repl_history
.vscode/*
+145
View File
@@ -1,3 +1,148 @@
[![Build Status](https://travis-ci.org/harish2704/knex-json-query.svg?branch=master)](https://travis-ci.org/harish2704/knex-json-query) [![Build Status](https://travis-ci.org/harish2704/knex-json-query.svg?branch=master)](https://travis-ci.org/harish2704/knex-json-query)
# knex-json-query # knex-json-query
A high-level utility which will will generate Knex query from a single JSON object. A high-level utility which will will generate Knex query from a single JSON object.
it is a simple function with just ~80 lines of code.
## Usage
```javascript
var knex = require('knex');
//var KnexJsonQuery = require('knex-json-query');
var KnexJsonQuery = require('./');
var jsonInput = {f1:{ $like: 'test%' }, $and: [{f2:22, f3: 33}] };
var queryBuilderFunction = KnexJsonQuery( jsonInput );
var sql = knex.where( queryBuilderFunction ).toString()
console.log( sql );
```
**Output**
```
select * where ("f1" LIKE 'test%' and (("f2" = 22 and "f3" = 33)))
```
## Examples
For more details, see the [test.js](./test.js)
**Examples from Unit test file:**
1. simple and conditions
- *input JSON*: `{"f1":10,"f2":20,"f3":30}`
- *output SQL*: `select * where ("f1" = 10 and "f2" = 20 and "f3" = 30)`
2. simple and conditions
- *input JSON*: `{"firstName":[["LIKE","H%"],["OR","hemanth"]]}`
- *output SQL*: `select * where (("firstName" LIKE 'H%' or "firstName" = 'hemanth'))`
3. regex query: MongoQuery
- *input JSON*: `{"firstName":{"$regex":"H*"}}`
- *output SQL*: `select * where ("firstName" REGEXP 'H*')`
4. regex query and should omit $options: MongoQuery
- *input JSON*: `{"firstName":{"$regex":"H*","$options":"i"}}`
- *output SQL*: `select * where ("firstName" REGEXP 'H*')`
5. simple and conditions
- *input JSON*: `{"f1":[["A"],["LIKE","B"]],"f2":20,"f3":30}`
- *output SQL*: `select * where (("f1" = 'A' and "f1" LIKE 'B') and "f2" = 20 and "f3" = 30)`
6. basic operators :: greater-than less-than
- *input JSON*: `{"f1":[">",10],"f2":["<",20],"f3":30}`
- *output SQL*: `select * where ("f1" > 10 and "f2" < 20 and "f3" = 30)`
7. basic operators :: in
- *input JSON*: `{"f1":["IN",[10,50]],"f2":["<",20],"f3":30}`
- *output SQL*: `select * where ("f1" in (10, 50) and "f2" < 20 and "f3" = 30)`
8. basic operators :: notin
- *input JSON*: `{"f1":["NOTIN",[10,50]],"f2":["<",20],"f3":30}`
- *output SQL*: `select * where ("f1" not in (10, 50) and "f2" < 20 and "f3" = 30)`
9. multiple conditions in one field
- *input JSON*: `{"f1":[["LIKE",20],21,["AND_BETWEEN",[40,45]]],"f2":20,"f3":30}`
- *output SQL*: `select * where (("f1" LIKE 20 and "f1" = 21 and "f1" between 40 and 45) and "f2" = 20 and "f3" = 30)`
10. simple and condition with like statement
- *input JSON*: `{"f1":["LIKE",20],"f2":20,"f3":30}`
- *output SQL*: `select * where ("f1" LIKE 20 and "f2" = 20 and "f3" = 30)`
11. simple and condition with between statement
- *input JSON*: `{"f1":["BETWEEN",[50,60]],"f2":20,"f3":30}`
- *output SQL*: `select * where ("f1" between 50 and 60 and "f2" = 20 and "f3" = 30)`
12. simple and condition with in statement
- *input JSON*: `{"f1":["IN",[50,60]],"f2":20,"f3":30}`
- *output SQL*: `select * where ("f1" in (50, 60) and "f2" = 20 and "f3" = 30)`
13. simple and condition with not in statement
- *input JSON*: `{"f1":["NOTIN",[50,60]],"f2":20,"f3":30}`
- *output SQL*: `select * where ("f1" not in (50, 60) and "f2" = 20 and "f3" = 30)`
14. simple and condition with raw statement
- *input JSON*: `{"f1":{"$raw":"@> ANY(ARRAY(1,2,3))"},"f2":20}`
- *output SQL*: `select * where ("f1" @> ANY(ARRAY(1,2,3)) and "f2" = 20)`
15. $and grouping
- *input JSON*: `{"f1":10,"f2":20,"f3":30,"$and":[{"f4":55},{"f5":66}]}`
- *output SQL*: `select * where ("f1" = 10 and "f2" = 20 and "f3" = 30 and (("f4" = 55) or ("f5" = 66)))`
16. simple or condition
- *input JSON*: `[{"f1":10},{"f2":20},{"f3":30}]`
- *output SQL*: `select * where (("f1" = 10) or ("f2" = 20) or ("f3" = 30))`
17. multiple conditions in one field
- *input JSON*: `{"f1":[["LIKE",20],["OR",21],["OR_BETWEEN",[40,45]]],"f2":20,"f3":30}`
- *output SQL*: `select * where (("f1" LIKE 20 or "f1" = 21 or "f1" between 40 and 45) and "f2" = 20 and "f3" = 30)`
18. multiple conditions in one field: MongoQuery
- *input JSON*: `{"f1":{"$like":20,"$or":21,"$or_between":[40,45]},"f2":20,"f3":30}`
- *output SQL*: `select * where (("f1" LIKE 20 or "f1" = 21 or "f1" between 40 and 45) and "f2" = 20 and "f3" = 30)`
19. simple or condition with like statement
- *input JSON*: `[{"f1":["LIKE",10]},{"f2":20},{"f3":30}]`
- *output SQL*: `select * where (("f1" LIKE 10) or ("f2" = 20) or ("f3" = 30))`
20. simple or condition with like statement: MongoQuery
- *input JSON*: `[{"f1":{"$like":10}},{"f2":20},{"f3":30}]`
- *output SQL*: `select * where (("f1" LIKE 10) or ("f2" = 20) or ("f3" = 30))`
21. simple or condition with between statement
- *input JSON*: `[{"f1":["BETWEEN",[50,60]]},{"f2":20},{"f3":30}]`
- *output SQL*: `select * where (("f1" between 50 and 60) or ("f2" = 20) or ("f3" = 30))`
22. simple or condition with between statement :MongoQuery
- *input JSON*: `[{"f1":{"$between":[50,60]}},{"f2":20},{"f3":30}]`
- *output SQL*: `select * where (("f1" between 50 and 60) or ("f2" = 20) or ("f3" = 30))`
23. simple or condition with in statement
- *input JSON*: `[{"f1":["IN",[50,60]]},{"f2":20},{"f3":30}]`
- *output SQL*: `select * where (("f1" in (50, 60)) or ("f2" = 20) or ("f3" = 30))`
24. simple or condition with not in statement
- *input JSON*: `[{"f1":["NOTIN",[50,60]]},{"f2":20},{"f3":30}]`
- *output SQL*: `select * where (("f1" not in (50, 60)) or ("f2" = 20) or ("f3" = 30))`
25. simple or condition with in statement :MongoQuery
- *input JSON*: `[{"f1":{"$in":[50,60]}},{"f2":20},{"f3":30}]`
- *output SQL*: `select * where (("f1" in (50, 60)) or ("f2" = 20) or ("f3" = 30))`
26. simple or condition with nin statement :MongoQuery
- *input JSON*: `[{"f1":{"$nin":[50,60]}},{"f2":20},{"f3":30}]`
- *output SQL*: `select * where (("f1" not in (50, 60)) or ("f2" = 20) or ("f3" = 30))`
27. simple or condition with raw statement
- *input JSON*: `[{"f1":{"$raw":"@> ANY(ARRAY(1,2,3))"}},{"f2":20}]`
- *output SQL*: `select * where (("f1" @> ANY(ARRAY(1,2,3))) or ("f2" = 20))`
28. simple or condition with conditional array
- *input JSON*: `{"f1":[["ILIKE","awesome"],["OR_ILIKE","%super%"]]}`
- *output SQL*: `select * where (("f1" ILIKE 'awesome' or "f1" ILIKE '%super%'))`
29. $or grouping
- *input JSON*: `{"f1":10,"f2":20,"f3":30,"$or":[{"f4":55},{"f5":66}]}`
- *output SQL*: `select * where ("f1" = 10 and "f2" = 20 and "f3" = 30 or (("f4" = 55) or ("f5" = 66)))`
+58 -24
View File
@@ -1,51 +1,85 @@
"use strict";
var functionOperators = [
'BETWEEN',
'IN',
/* ---- */
'OR',
'AND',
/* ---- */
'OR_BETWEEN',
'OR_IN',
/* ---- */
'AND_BETWEEN',
'AND_IN'
];
var functionOperatorMap = { var functionOperatorMap = {
BETWEEN: 'whereBetween', BETWEEN: 'whereBetween',
IN: 'whereIn', IN: 'whereIn',
NIN: 'whereNotIn',
NOTIN: 'whereNotIn',
ISNULL: 'whereNull',
NOTNULL: 'whereNotNull',
/* ---- */ /* ---- */
OR: 'orWhere', OR: 'orWhere',
AND: 'where', AND: 'where',
EQ: 'where',
'=': 'where',
/* ---- */ /* ---- */
OR_BETWEEN: 'orWhereBetween', OR_BETWEEN: 'orWhereBetween',
OR_IN: 'orWhereIn', OR_IN: 'orWhereIn',
OR_NOTIN: 'orWhereNotIn',
OR_ISNULL: 'orWhereNull',
IR_NOTNULL: 'orWhereNotNull',
/* ---- */ /* ---- */
AND_BETWEEN: 'andWhereBetween', AND_BETWEEN: 'andWhereBetween',
AND_IN: 'andWhereIn' AND_IN: 'andWhereIn',
AND_NOTIN: 'andWhereNotIn',
AND_ISNULL: 'andWhereNull',
AND_NOTNULL: 'andWhereNotNull',
/* ---- */
RAW: 'whereRaw',
OR_RAW: 'orWhereRaw',
AND_RAW: 'whereRaw',
};
var aliases = {
REGEX: 'REGEXP'
}; };
function addCondition (q, field, val) { function addCondition (q, field, val) {
if( field === '$or' ){
return q.orWhere( getWhereCondition( val ))
}
if( field === '$and' ){
return q.where( getWhereCondition( val ))
}
if( val.constructor.name === 'Object' ){
delete val.$options;
val = Object.keys(val).map( function(key){
return [ key.slice(1).toUpperCase(), val[key] ];
});
if( val.length === 1){
val = val[0];
}
}
if (Array.isArray(val[0])) { if (Array.isArray(val[0])) {
return q.where(function () { return q.where(function () {
return val.forEach(addCondition.bind(null, this, field)) return val.forEach(addCondition.bind(null, this, field));
}) });
} }
if (!Array.isArray(val)) { if (!Array.isArray(val)) {
val = ['AND', field, val ] // Simple string or number value
} else if (functionOperators.indexOf(val[0]) !== -1) { val = ['AND', field, val ];
val.splice(1, 0, field)
} else { } else {
val = [ 'AND', field ].concat(val) val[0] = aliases[ val[0] ] || val[0];
if (functionOperatorMap.hasOwnProperty( val[0] ) ) {
// SQL operator
val = [ val[0], field ].concat(val.slice(1));
} else {
// Cases when we have something like 'OR_ILIKE' or 'AND_@>'
var operators = /(\w+)_(\w+)/.exec(val[0])
var operatorsExist = operators && operators.constructor === Array && operators.length >= 3
if (operatorsExist) {
val = [operators[1], field].concat([operators[2]], val.slice(1));
} else {
// other cases like ( '>', '10' ) Greater than 10
val = [ 'AND', field ].concat(val);
}
}
} }
var args = val[0].includes('RAW') ? [ '"'+val[1]+'" ' + val[2] ] : val.slice(1)
return q[functionOperatorMap[val[0]]].apply(q, val.slice(1)) return q[functionOperatorMap[val[0]]].apply(q, args);
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "knex-json-query", "name": "knex-json-query",
"version": "0.0.2", "version": "1.0.0",
"description": "A high-level utility which will will generate Knex query from a single JSON object.", "description": "A high-level utility which will will generate Knex query from a single JSON object.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
+88 -2
View File
@@ -1,17 +1,37 @@
/* global describe, it */ /* global describe, it */
var assert = require('assert'); var assert = require('assert');
var knex = require('knex')({}); var knex = require('knex')({client: 'sqlite' });
var knexJsonQuery = require('./'); var knexJsonQuery = require('./');
require('simple-mocha'); require('simple-mocha');
var conditions= [ var conditions= [
/* -----------Tests for and conditoins---------- */ /* -----------Tests for and conditions---------- */
{ {
name: 'handle simple and conditions', name: 'handle simple and conditions',
input:{ f1: 10, f2: 20, f3: 30 }, input:{ f1: 10, f2: 20, f3: 30 },
output: 'select * where ("f1" = 10 and "f2" = 20 and "f3" = 30)' output: 'select * where ("f1" = 10 and "f2" = 20 and "f3" = 30)'
}, },
{
name: 'handle simple and conditions',
input:{"firstName":[["LIKE","H%"],["OR","hemanth"]]},
output: 'select * where (("firstName" LIKE \'H%\' or "firstName" = \'hemanth\'))'
},
{
name: 'handle regex query: MongoQuery',
input:{"firstName":{ $regex: "H*" }},
output: 'select * where ("firstName" REGEXP \'H*\')'
},
{
name: 'handle regex query and should omit $options: MongoQuery',
input:{"firstName":{ $regex: "H*", $options: 'i' }},
output: 'select * where ("firstName" REGEXP \'H*\')'
},
{
name: 'handle simple and conditions',
input:{ f1: [[ 'A' ],['LIKE', 'B' ]], f2: 20, f3: 30 },
output: 'select * where (("f1" = \'A\' and "f1" LIKE \'B\') and "f2" = 20 and "f3" = 30)'
},
{ {
name: 'handle basic operators :: greater-than less-than', name: 'handle basic operators :: greater-than less-than',
input:{ f1: ['>', 10], f2: [ '<', 20 ], f3: 30 }, input:{ f1: ['>', 10], f2: [ '<', 20 ], f3: 30 },
@@ -22,6 +42,11 @@ var conditions= [
input:{ f1: ['IN', [10,50]], f2: [ '<', 20 ], f3: 30 }, input:{ f1: ['IN', [10,50]], f2: [ '<', 20 ], f3: 30 },
output: 'select * where ("f1" in (10, 50) and "f2" < 20 and "f3" = 30)' output: 'select * where ("f1" in (10, 50) and "f2" < 20 and "f3" = 30)'
}, },
{
name: 'handle basic operators :: notin',
input:{ f1: ['NOTIN', [10,50]], f2: [ '<', 20 ], f3: 30 },
output: 'select * where ("f1" not in (10, 50) and "f2" < 20 and "f3" = 30)'
},
{ {
name: 'handle multiple conditions in one field', name: 'handle multiple conditions in one field',
input:{ f1: [ [ 'LIKE', 20 ], 21, [ 'AND_BETWEEN', [ 40, 45 ] ] ], f2: 20, f3: 30 }, input:{ f1: [ [ 'LIKE', 20 ], 21, [ 'AND_BETWEEN', [ 40, 45 ] ] ], f2: 20, f3: 30 },
@@ -42,6 +67,22 @@ var conditions= [
input:{ f1: ['IN', [ 50, 60 ] ], f2: 20, f3: 30 }, input:{ f1: ['IN', [ 50, 60 ] ], f2: 20, f3: 30 },
output: 'select * where ("f1" in (50, 60) and "f2" = 20 and "f3" = 30)' output: 'select * where ("f1" in (50, 60) and "f2" = 20 and "f3" = 30)'
}, },
{
name: 'handle simple and condition with not in statement',
input:{ f1: ['NOTIN', [ 50, 60 ] ], f2: 20, f3: 30 },
output: 'select * where ("f1" not in (50, 60) and "f2" = 20 and "f3" = 30)'
},
{
name: 'handle simple and condition with raw statement',
input: { f1: { $raw: '@> ANY(ARRAY(1,2,3))' }, f2: 20 },
output: 'select * where ("f1" @> ANY(ARRAY(1,2,3)) and "f2" = 20)'
},
/* -----------Tests for or conditions---------- */
{
name: 'handle $and grouping',
input:{ f1: 10, f2: 20, f3: 30, $and: [ { f4: 55 }, { f5: 66} ] },
output: 'select * where ("f1" = 10 and "f2" = 20 and "f3" = 30 and (("f4" = 55) or ("f5" = 66)))'
},
/* -----------Tests for or conditoins---------- */ /* -----------Tests for or conditoins---------- */
{ {
name: 'handle simple or condition', name: 'handle simple or condition',
@@ -53,21 +94,66 @@ var conditions= [
input:{ f1: [ [ 'LIKE', 20 ], [ 'OR', 21 ], [ 'OR_BETWEEN', [ 40, 45 ] ] ], f2: 20, f3: 30 }, input:{ f1: [ [ 'LIKE', 20 ], [ 'OR', 21 ], [ 'OR_BETWEEN', [ 40, 45 ] ] ], f2: 20, f3: 30 },
output: 'select * where (("f1" LIKE 20 or "f1" = 21 or "f1" between 40 and 45) and "f2" = 20 and "f3" = 30)' output: 'select * where (("f1" LIKE 20 or "f1" = 21 or "f1" between 40 and 45) and "f2" = 20 and "f3" = 30)'
}, },
{
name: 'handle multiple conditions in one field: MongoQuery',
input:{ f1: { $like: 20, $or: 21, $or_between: [ 40, 45 ] }, f2: 20, f3: 30 },
output: 'select * where (("f1" LIKE 20 or "f1" = 21 or "f1" between 40 and 45) and "f2" = 20 and "f3" = 30)'
},
{ {
name: 'handle simple or condition with like statement', name: 'handle simple or condition with like statement',
input:[ { f1: ['LIKE',10] }, { f2: 20 }, { f3: 30 } ], input:[ { f1: ['LIKE',10] }, { f2: 20 }, { f3: 30 } ],
output: 'select * where (("f1" LIKE 10) or ("f2" = 20) or ("f3" = 30))' output: 'select * where (("f1" LIKE 10) or ("f2" = 20) or ("f3" = 30))'
}, },
{
name: 'handle simple or condition with like statement: MongoQuery',
input:[ { f1: { $like: 10 } }, { f2: 20 }, { f3: 30 } ],
output: 'select * where (("f1" LIKE 10) or ("f2" = 20) or ("f3" = 30))'
},
{ {
name: 'handle simple or condition with between statement', name: 'handle simple or condition with between statement',
input:[ { f1: ['BETWEEN',[50, 60] ] }, { f2: 20 }, { f3: 30 } ], input:[ { f1: ['BETWEEN',[50, 60] ] }, { f2: 20 }, { f3: 30 } ],
output: 'select * where (("f1" between 50 and 60) or ("f2" = 20) or ("f3" = 30))' output: 'select * where (("f1" between 50 and 60) or ("f2" = 20) or ("f3" = 30))'
}, },
{
name: 'handle simple or condition with between statement :MongoQuery',
input:[ { f1: { $between:[50, 60] } }, { f2: 20 }, { f3: 30 } ],
output: 'select * where (("f1" between 50 and 60) or ("f2" = 20) or ("f3" = 30))'
},
{ {
name: 'handle simple or condition with in statement', name: 'handle simple or condition with in statement',
input:[ { f1: ['IN',[ 50, 60 ]] }, { f2: 20 }, { f3: 30 } ], input:[ { f1: ['IN',[ 50, 60 ]] }, { f2: 20 }, { f3: 30 } ],
output: 'select * where (("f1" in (50, 60)) or ("f2" = 20) or ("f3" = 30))' output: 'select * where (("f1" in (50, 60)) or ("f2" = 20) or ("f3" = 30))'
}, },
{
name: 'handle simple or condition with not in statement',
input:[ { f1: ['NOTIN',[ 50, 60 ]] }, { f2: 20 }, { f3: 30 } ],
output: 'select * where (("f1" not in (50, 60)) or ("f2" = 20) or ("f3" = 30))'
},
{
name: 'handle simple or condition with in statement :MongoQuery',
input:[ { f1: {$in:[ 50, 60 ]} }, { f2: 20 }, { f3: 30 } ],
output: 'select * where (("f1" in (50, 60)) or ("f2" = 20) or ("f3" = 30))'
},
{
name: 'handle simple or condition with nin statement :MongoQuery',
input:[ { f1: { $nin: [ 50, 60 ] } }, { f2: 20 }, { f3: 30 } ],
output: 'select * where (("f1" not in (50, 60)) or ("f2" = 20) or ("f3" = 30))'
},
{
name: 'handle simple or condition with raw statement',
input: [ { f1: { $raw: '@> ANY(ARRAY(1,2,3))' } }, { f2: 20 } ],
output: 'select * where (("f1" @> ANY(ARRAY(1,2,3))) or ("f2" = 20))'
},
{
name: 'handle simple or condition with conditional array',
input: { f1: [['ILIKE', 'awesome'], ['OR_ILIKE', '%super%'] ] },
output: 'select * where (("f1" ILIKE \'awesome\' or "f1" ILIKE \'%super%\'))'
},
{
name: 'handle $or grouping',
input:{ f1: 10, f2: 20, f3: 30, $or: [ { f4: 55 }, { f5: 66} ] },
output: 'select * where ("f1" = 10 and "f2" = 20 and "f3" = 30 or (("f4" = 55) or ("f5" = 66)))'
},
]; ];