jsonsql
Advanced tools
Comparing version 0.0.4 to 0.1.0
448
index.js
var AND = '&&' | ||
, OR = '||' | ||
, AND_STR = 'and' | ||
, OR_STR = 'or' | ||
, NOT = '!' | ||
, EQUAL = '=' | ||
, LIKE = '~' | ||
, NOTEQUAL = NOT + EQUAL | ||
, NOTLIKE = NOT + LIKE | ||
, WILDCARD = '*' | ||
, COMMA = ',' | ||
, DELIMITER = '.' | ||
, LEFT = '(' | ||
, RIGHT = ')' | ||
, WHERE = 'where' | ||
, synopsis = { | ||
pathway: [], | ||
groups: {} | ||
} | ||
, AST = {} | ||
, options = {}; | ||
, OR = '||' | ||
, AND_STR = 'and' | ||
, OR_STR = 'or' | ||
, NOT = '!' | ||
, EQUAL = '=' | ||
, LIKE = '~' | ||
, NOTEQUAL = NOT + EQUAL | ||
, NOTLIKE = NOT + LIKE | ||
, GT = '>' | ||
, GE = '>=' | ||
, LT = '<' | ||
, LE = '<=' | ||
, WILDCARD = '*' | ||
, COMMA = ',' | ||
, DELIMITER = '.' | ||
, LEFT = '(' | ||
, RIGHT = ')' | ||
, WHERE = 'where' | ||
, synopsis = { | ||
pathway: [], | ||
groups: {} | ||
} | ||
, AST = {} | ||
, options = {}; | ||
var print = console.log; | ||
function getObjectType(obj) { | ||
return Object.prototype.toString.call(obj); | ||
} | ||
function isDate(obj) { | ||
return getObjectType(obj) === '[object Date]'; | ||
} | ||
// ------------------ splitter -------------------- // | ||
function Tokenize(query) { | ||
var parts = __splitTrim(query, WHERE); | ||
var pathway = parts[0]; | ||
var where = parts[1]; | ||
var parts = __splitTrim(query, WHERE); | ||
var pathway = parts[0]; | ||
var where = parts[1]; | ||
synopsis.pathway = __splitTrim(pathway, COMMA); | ||
for (var i = 0, len = synopsis.pathway.length; i < len; i++) { | ||
synopsis.pathway[i] = __splitTrim(synopsis.pathway[i], DELIMITER); | ||
if (synopsis.pathway[i][0] == WILDCARD) | ||
synopsis.pathway[i].shift(); | ||
if(synopsis.pathway[i].length === 0) | ||
synopsis.pathway.splice(i, 1); | ||
} | ||
synopsis.pathway = __splitTrim(pathway, COMMA); | ||
for (var i = 0, len = synopsis.pathway.length; i < len; i++) { | ||
synopsis.pathway[i] = __splitTrim(synopsis.pathway[i], DELIMITER); | ||
if (synopsis.pathway[i][0] == WILDCARD) | ||
synopsis.pathway[i].shift(); | ||
if (synopsis.pathway[i].length === 0) | ||
synopsis.pathway.splice(i, 1); | ||
} | ||
var lastLeft = -1, | ||
lastRight = -1, | ||
current = 0; | ||
while (current < where.length) { | ||
if (where[current] === LEFT) { | ||
lastLeft = current; | ||
} else if (where[current] === RIGHT) { | ||
lastRight = current; | ||
if (lastRight > lastLeft && lastLeft !== -1) { | ||
var k = 'gr' + '_' + new Date().getTime(); | ||
synopsis.groups[k] = where.substring(lastLeft + 1, lastRight); | ||
where = where.replace(LEFT + synopsis.groups[k] + RIGHT, k); | ||
current = -1; | ||
} | ||
} | ||
current += 1; | ||
var lastLeft = -1, | ||
lastRight = -1, | ||
current = 0; | ||
while (current < where.length) { | ||
if (where[current] === LEFT) { | ||
lastLeft = current; | ||
} else if (where[current] === RIGHT) { | ||
lastRight = current; | ||
if (lastRight > lastLeft && lastLeft !== -1) { | ||
var k = 'gr' + '_' + new Date().getTime(); | ||
synopsis.groups[k] = where.substring(lastLeft + 1, lastRight); | ||
where = where.replace(LEFT + synopsis.groups[k] + RIGHT, k); | ||
current = -1; | ||
} | ||
} | ||
LogicalGrouping(AST, where); | ||
current += 1; | ||
} | ||
LogicalGrouping(AST, where); | ||
} | ||
function LogicalGrouping(current, where) { | ||
var lastAnd = __findIndex(where, AND), | ||
lastOr = __findIndex(where, OR); | ||
var lastAnd = __findIndex(where, AND), | ||
lastOr = __findIndex(where, OR); | ||
if (lastAnd !== Number.MAX_VALUE || lastOr !== Number.MAX_VALUE) { | ||
if (lastAnd < lastOr) { | ||
current.and = current.and || []; | ||
var parts = __splitTrim(where, AND); | ||
current.and.push(parts[0]); | ||
LogicalGrouping(current.and, parts[1]); | ||
} else { | ||
current.or = current.or || []; | ||
var parts = __splitTrim(where, OR); | ||
current.or.push(parts[0]); | ||
LogicalGrouping(current.or, parts[1]); | ||
} | ||
if (lastAnd !== Number.MAX_VALUE || lastOr !== Number.MAX_VALUE) { | ||
if (lastAnd < lastOr) { | ||
current.and = current.and || []; | ||
var parts = __splitTrim(where, AND); | ||
current.and.push(parts[0]); | ||
LogicalGrouping(current.and, parts[1]); | ||
} else { | ||
if (synopsis.groups[where]) { | ||
where = synopsis.groups[where]; | ||
LogicalGrouping(current, where); | ||
} else { | ||
if (Array.isArray(current)) | ||
current.push(where); | ||
else | ||
current.or = [where]; | ||
ExtractExpression(AST.or ? AST.or : AST.and) | ||
} | ||
current.or = current.or || []; | ||
var parts = __splitTrim(where, OR); | ||
current.or.push(parts[0]); | ||
LogicalGrouping(current.or, parts[1]); | ||
} | ||
} else { | ||
if (synopsis.groups[where]) { | ||
where = synopsis.groups[where]; | ||
LogicalGrouping(current, where); | ||
} else { | ||
if (Array.isArray(current)) | ||
current.push(where); | ||
else | ||
current.or = [where]; | ||
ExtractExpression(AST.or ? AST.or : AST.and) | ||
} | ||
} | ||
} | ||
function ExtractExpression(logicalGroup) { | ||
for (var k in logicalGroup) { | ||
if (logicalGroup.hasOwnProperty(k)) { | ||
if (Array.isArray(logicalGroup[k])) { | ||
ExtractExpression(logicalGroup[k]); | ||
} | ||
else if (typeof logicalGroup[k] === 'string') { | ||
if (__contains(logicalGroup[k], NOTEQUAL)) { | ||
var parts = __splitTrim(logicalGroup[k], NOTEQUAL); | ||
logicalGroup[k] = { | ||
ne: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} else if (__contains(logicalGroup[k], NOTLIKE)) { | ||
var parts = __splitTrim(logicalGroup[k], NOTLIKE); | ||
logicalGroup[k] = { | ||
nreq: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} else if (__contains(logicalGroup[k], EQUAL)) { | ||
var parts = __splitTrim(logicalGroup[k], EQUAL); | ||
logicalGroup[k] = { | ||
eq: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} else if (__contains(logicalGroup[k], LIKE)) { | ||
var parts = __splitTrim(logicalGroup[k], LIKE); | ||
logicalGroup[k] = { // rough eq | ||
req: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} | ||
} | ||
for (var k in logicalGroup) { | ||
if (logicalGroup.hasOwnProperty(k)) { | ||
if (Array.isArray(logicalGroup[k])) { | ||
ExtractExpression(logicalGroup[k]); | ||
} | ||
else if (typeof logicalGroup[k] === 'string') { | ||
if (__contains(logicalGroup[k], NOTEQUAL)) { | ||
var parts = __splitTrim(logicalGroup[k], NOTEQUAL); | ||
logicalGroup[k] = { | ||
ne: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} else if (__contains(logicalGroup[k], NOTLIKE)) { | ||
var parts = __splitTrim(logicalGroup[k], NOTLIKE); | ||
logicalGroup[k] = { | ||
nreq: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} else if (__contains(logicalGroup[k], LIKE)) { | ||
var parts = __splitTrim(logicalGroup[k], LIKE); | ||
logicalGroup[k] = { // rough eq | ||
req: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} else if (__contains(logicalGroup[k], GE)) { | ||
var parts = __splitTrim(logicalGroup[k], GE); | ||
logicalGroup[k] = { // greater than or equal | ||
ge: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} else if (__contains(logicalGroup[k], GT)) { | ||
var parts = __splitTrim(logicalGroup[k], GT); | ||
logicalGroup[k] = { // greater than | ||
gt: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} else if (__contains(logicalGroup[k], LE)) { | ||
var parts = __splitTrim(logicalGroup[k], LE); | ||
logicalGroup[k] = { // less than or equal | ||
le: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} else if (__contains(logicalGroup[k], LT)) { | ||
var parts = __splitTrim(logicalGroup[k], LT); | ||
logicalGroup[k] = { // less than | ||
lt: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} else if (__contains(logicalGroup[k], EQUAL)) { | ||
var parts = __splitTrim(logicalGroup[k], EQUAL); | ||
logicalGroup[k] = { | ||
eq: [ | ||
parts[0], | ||
parts[1] | ||
] | ||
}; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
function __findIndex(str, token) { | ||
var index = str.indexOf(token); | ||
return index === -1 ? Number.MAX_VALUE : index; | ||
var index = str.indexOf(token); | ||
return index === -1 ? Number.MAX_VALUE : index; | ||
} | ||
function __splitTrim(str, token) { | ||
return str.split(token).map(function (p) { | ||
return p.trim(); | ||
}); | ||
return str.split(token).map(function (p) { | ||
return p.trim(); | ||
}); | ||
} | ||
function __contains(a, b) { | ||
return a.indexOf(b) > -1; | ||
return a.indexOf(b) > -1; | ||
} | ||
function __hierarchize(obj, dottedPath) { | ||
var parts = __splitTrim(dottedPath, DELIMITER); | ||
var res = obj; | ||
for (var p in parts) | ||
if (res.hasOwnProperty(parts[p])) | ||
res = res[parts[p]]; | ||
else | ||
return ''; | ||
return res.toString(); | ||
var parts = __splitTrim(dottedPath, DELIMITER); | ||
var res = obj; | ||
for (var p in parts) { | ||
if (res.hasOwnProperty(parts[p])) | ||
res = res[parts[p]]; | ||
else | ||
return ''; | ||
} | ||
// support comparison for Date | ||
if(isDate(res)) res = res.valueOf() | ||
else res.toString(); | ||
return res | ||
} | ||
function FilterOR(ASTNode, row) { | ||
var res = false; | ||
for (var k in ASTNode) { | ||
var filterFunc = (k === AND_STR ? FilterAND : (k === OR_STR ? FilterOR : Filter)); | ||
res = res || filterFunc(ASTNode[k], row); | ||
if (options.trace) | ||
console.log(synopsis.step, '======((( or', ASTNode[k], res); | ||
if (res) return res; | ||
} | ||
return res; | ||
var res = false; | ||
for (var k in ASTNode) { | ||
var filterFunc = (k === AND_STR ? FilterAND : (k === OR_STR ? FilterOR : Filter)); | ||
res = res || filterFunc(ASTNode[k], row); | ||
if (options.trace) | ||
print(synopsis.step, '======((( or', ASTNode[k], res); | ||
if (res) return res; | ||
} | ||
return res; | ||
} | ||
function FilterAND(ASTNode, row) { | ||
var res = true; | ||
for (var k in ASTNode) { | ||
var filterFunc = (k === AND_STR ? FilterAND : (k === OR_STR ? FilterOR : Filter)); | ||
res = res && filterFunc(ASTNode[k], row); | ||
if (options.trace) | ||
console.log(synopsis.step, '======((( and', ASTNode[k], res); | ||
if (!res) return res; | ||
} | ||
return res; | ||
var res = true; | ||
for (var k in ASTNode) { | ||
var filterFunc = (k === AND_STR ? FilterAND : (k === OR_STR ? FilterOR : Filter)); | ||
res = res && filterFunc(ASTNode[k], row); | ||
if (options.trace) | ||
print(synopsis.step, '======((( and', ASTNode[k], res); | ||
if (!res) return res; | ||
} | ||
return res; | ||
} | ||
function Filter(ASTNode, row) { | ||
synopsis.step += 1; | ||
if (ASTNode.or) { | ||
var res = FilterOR(ASTNode.or, row); | ||
if (options.trace) | ||
console.log(synopsis.step, 'OR', ASTNode, res); | ||
return res; | ||
} else if (ASTNode.and) { | ||
var res = FilterAND(ASTNode.and, row); | ||
if (options.trace) | ||
console.log(synopsis.step, 'AND', ASTNode, res); | ||
return res; | ||
} else if (typeof ASTNode === 'object') { | ||
if (ASTNode.eq) { // = | ||
return __hierarchize(row, ASTNode.eq[0]) === ASTNode.eq[1]; | ||
} else if (ASTNode.ne) { // != | ||
return __hierarchize(row, ASTNode.ne[0]) !== ASTNode.ne[1]; | ||
} else if (ASTNode.req) { // ~ | ||
return __contains(__hierarchize(row, ASTNode.req[0]), ASTNode.req[1]); | ||
} else if (ASTNode.nreq) { // ~ | ||
return !__contains(__hierarchize(row, ASTNode.nreq[0]), ASTNode.nreq[1]); | ||
} else { | ||
return Filter(ASTNode, row); | ||
} | ||
synopsis.step += 1; | ||
if (ASTNode.or) { | ||
var res = FilterOR(ASTNode.or, row); | ||
if (options.trace) | ||
print(synopsis.step, 'OR', ASTNode, res); | ||
return res; | ||
} else if (ASTNode.and) { | ||
var res = FilterAND(ASTNode.and, row); | ||
if (options.trace) | ||
print(synopsis.step, 'AND', ASTNode, res); | ||
return res; | ||
} else if (typeof ASTNode === 'object') { | ||
if (ASTNode.eq) { // = | ||
return __hierarchize(row, ASTNode.eq[0]) === ASTNode.eq[1]; | ||
} else if (ASTNode.ne) { // != | ||
return __hierarchize(row, ASTNode.ne[0]) !== ASTNode.ne[1]; | ||
} else if (ASTNode.req) { // ~ | ||
return __contains(__hierarchize(row, ASTNode.req[0]), ASTNode.req[1]); | ||
} else if (ASTNode.nreq) { // ~ | ||
return !__contains(__hierarchize(row, ASTNode.nreq[0]), ASTNode.nreq[1]); | ||
} else if (ASTNode.gt) { // > | ||
return __hierarchize(row, ASTNode.gt[0]) > ASTNode.gt[1]; | ||
} else if (ASTNode.ge) { // >= | ||
return __hierarchize(row, ASTNode.ge[0]) >= ASTNode.ge[1]; | ||
} else if (ASTNode.lt) { // < | ||
return __hierarchize(row, ASTNode.lt[0]) < ASTNode.lt[1]; | ||
} else if (ASTNode.le) { // <= | ||
return __hierarchize(row, ASTNode.le[0]) <= ASTNode.le[1]; | ||
} else { | ||
return Filter(ASTNode, row); | ||
} | ||
} | ||
} | ||
function Parse(dataSource) { | ||
var result = []; | ||
for (var k in dataSource) | ||
if (Filter(AST, dataSource[k])) | ||
result.push(dataSource[k]); | ||
return result; | ||
var result = []; | ||
for (var k in dataSource) | ||
if (Filter(AST, dataSource[k])) | ||
result.push(dataSource[k]); | ||
return result; | ||
} | ||
function Fields(result) { | ||
if (result && synopsis.pathway.length > 0) { | ||
//console.log(synopsis.pathway); | ||
return result.map(function (ele) { | ||
var res = {}; | ||
for(var i = 0, len = synopsis.pathway.length; i< len; i++){ | ||
var key = synopsis.pathway[i].join(DELIMITER); | ||
res[key] = __hierarchize(ele, key); | ||
} | ||
return res; | ||
}); | ||
} | ||
return result; | ||
if (result && synopsis.pathway.length > 0) { | ||
//print(synopsis.pathway); | ||
return result.map(function (ele) { | ||
var res = {}; | ||
for (var i = 0, len = synopsis.pathway.length; i < len; i++) { | ||
var key = synopsis.pathway[i].join(DELIMITER); | ||
res[key] = __hierarchize(ele, key); | ||
} | ||
return res; | ||
}); | ||
} | ||
return result; | ||
} | ||
function Query(dataSource, query, opts) { | ||
synopsis = { | ||
pathway: [], | ||
groups: {}, | ||
step: 0 | ||
synopsis = { | ||
pathway: [], | ||
groups: {}, | ||
step: 0 | ||
}; | ||
AST = {}; | ||
opts = opts || { | ||
trace: false | ||
}; | ||
AST = {}; | ||
opts = opts || { | ||
trace: false | ||
}; | ||
options = opts; | ||
Tokenize(query); | ||
return Fields(Parse(dataSource)); | ||
options = opts; | ||
Tokenize(query); | ||
return Fields(Parse(dataSource)); | ||
} | ||
@@ -236,0 +294,0 @@ |
{ | ||
"name": "jsonsql", | ||
"version": "0.0.4", | ||
"version": "0.1.0", | ||
"description" : "A SQL-like query language for JSON objects.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
# jsonSQL | ||
[![npm version](https://badge.fury.io/js/jsonsql.svg?branche=master)](http://badge.fury.io/js/jsonsql) | ||
A SQL-like query language for JSON objects. | ||
## Install | ||
`npm install jsonsql` | ||
``` | ||
npm install jsonsql | ||
``` | ||
@@ -20,5 +25,5 @@ ## Semantics | ||
## Example | ||
## Generic query | ||
#### Data Source: | ||
##### Data Source: | ||
```javascript | ||
@@ -70,3 +75,5 @@ var dataSource = { | ||
``` | ||
#### Query by jsonSQL: | ||
- - - | ||
##### Query with condition: | ||
```javascript | ||
@@ -77,3 +84,3 @@ var Query = require('jsonSQL'); | ||
``` | ||
#### RESULT OUTPUT: | ||
##### RESULT OUTPUT: | ||
@@ -102,3 +109,4 @@ ```bash | ||
#### Multi-Fields Query: | ||
- - - | ||
##### Multi-Fields Query: | ||
```javascript | ||
@@ -108,3 +116,3 @@ var res = Query(dataSource, '*.id, *.holy.addr where holy.name=CRAP'); | ||
``` | ||
#### RESULT OUTPUT: | ||
##### RESULT OUTPUT: | ||
```bash | ||
@@ -114,2 +122,55 @@ ======RESULT 2 | ||
'holy.addr': '7f:e8:ee:32:cd:15' } ] | ||
``` | ||
``` | ||
## Comparsion | ||
##### Data Source: | ||
```javascript | ||
var dataSource = { | ||
'4866102f06de4f38bc30592e001cf423': { | ||
name: 'Tom', | ||
age: 33, | ||
birth: new Date(2016, 1, 1) | ||
}, | ||
'd07872f7d2e8447bbe874bbfd3fb0296': { | ||
name: 'Jack', | ||
age: 18, | ||
birth: new Date(2015, 1, 1) | ||
}, | ||
'd935869b80f542a9bf3f6a59d4f635f1': { | ||
name: 'Peter', | ||
age: 26, | ||
birth: new Date(2014, 1, 1) | ||
}, | ||
}; | ||
``` | ||
- - - | ||
##### Number comparison: | ||
```javascript | ||
var Query = require('../index'); | ||
var res = Query(dataSource, '* where age<=30'); | ||
console.log('======RESULT 1\n',res); | ||
``` | ||
##### RESULT OUTPUT: | ||
```bash | ||
======RESULT 1 | ||
[ { name: 'Jack', | ||
age: 18, | ||
birth: Sun Feb 01 2015 00:00:00 GMT+0800 (CST) }, | ||
{ name: 'Peter', | ||
age: 26, | ||
birth: Sat Feb 01 2014 00:00:00 GMT+0800 (CST) } ] | ||
``` | ||
##### Date comparison: | ||
```javascript | ||
var res = Query(dataSource, '* where birth>' + new Date(2015,1,1).valueOf()); | ||
console.log('======RESULT 2\n',res); | ||
``` | ||
##### RESULT OUTPUT: | ||
```bash | ||
======RESULT 2 | ||
[ { name: 'Tom', | ||
age: 33, | ||
birth: Mon Feb 01 2016 00:00:00 GMT+0800 (CST) } ] | ||
``` |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
20202
7
522
171
1