-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
100 lines (85 loc) · 2.93 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/*
Added a JSON.query() function that given an object or an array of objects
and a "lucene-like" expression, returns an array of the objects that match
the expression.
* Supported features:
* - conjunction operators (AND, OR, ||, &&, NOT)
* - prefix operators (+, -) (on the field, like Kibana)
* - quoted values ("foo bar")
* - named fields (foo:bar)
* - range expressions (foo:[bar TO baz], foo:{bar TO baz})
* - parentheses grouping ( (foo OR bar) AND baz )
* - field groups ( foo:(bar OR baz) )
* - fields that refer to arrays ( foo.bar[0]:baz )
* - test for missing and existing fields:
* foo.bar:_missing_
* foo.bar:_exists_
*/
var _ = require( 'lodash' );
var parser = require('./parser');
JSON.query = function( arr, expression ) {
function handleTerm( o, term ) {
if ( term.field && term.field != '<implicit>' )
var compare = _.get( o, term.field );
else
var compare = JSON.stringify( o );
if ( term.field && term.term == '_missing_' ) return ( compare == undefined );
if ( term.field && term.term == '_exists_' ) return ( compare != undefined );
if ( ! compare ) return false;
if ( term.field && term.term_min && term.term_max ) {
// range check
if ( compare.toString().match(/^\d+$/) ) {
if ( Number( compare ) >= Number( term.term_min ) &&
Number( compare ) <= Number( term.term_max ) ) return true;
else return false;
}
else {
if ( compare >= term.term_min && compare <= term.term_max ) return true;
else return false;
}
}
if ( term.field && term.term.match( /^([\>\<]{1}[=]*)(\d+)$/ ) ) {
var m = term.term.match( /^([\>\<]{1}[=]*)(\d+)$/ );
var T = Number( compare );
var M = Number( m[2] );
switch( m[1] ) {
case '<': return ( T < M ); break;
case '<=': return ( T <= M ); break;
case '>': return ( T > M ); break;
case '>=': return ( T >= M ); break;
}
}
var regex = new RegExp( term.term );
var match = ( compare.toString().match( regex ) ? true : false );
if ( term.prefix && term.prefix == '-' )
match = ! match;
return match;
}
function handleExpr( o, exp ) {
if ( _.has( exp, 'left.operator' ) )
var left = handleExpr( o, exp.left );
else if ( _.has( exp, 'left.term' ) || _.has( exp, 'left.term_min' ) )
var left = handleTerm( o, exp.left );
else
var left = false;
if ( _.has( exp, 'right.operator' ) )
var right = handleExpr( o, exp.right );
else if ( _.has( exp, 'right.term' ) || _.has( exp, 'right.term_min' ) )
var right = handleTerm( o, exp.right );
else
var right = true;
if ( exp.operator == 'AND' ) return ( left && right );
else if ( exp.operator == 'OR' ) return ( left || right );
else return left;
}
var exp = parser.parse( expression );
if ( ! ( arr instanceof Array ) ) {
var a = [];
a.push( arr );
arr = a;
}
var matches = _.filter( arr, function( o ) {
return handleExpr( o, exp );
});
return matches;
}