Skip to content

Commit

Permalink
add range queries to /get* endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
garyp committed Nov 9, 2011
1 parent 2a01f48 commit d882bc3
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 37 deletions.
2 changes: 1 addition & 1 deletion Apps/DevDocs/static/resources.json
Expand Up @@ -477,7 +477,7 @@
},
{
"name": "terms",
"description": "The actual query to be performed.<h3>Syntax</h3>The syntax uses a JSON like notation that always begins as an array. The fields can be checked by writing them in a json notation of field:value. The field name can use dot notation to access objects that are deeper than the top level. The value can be a quoted string, a number, or 'true'/'false' (without quotes). Strings may be quoted with either &quot; or &apos;. Multiple terms can be checked by separating them with a comma just like a JSON array: <code>[field:value, field2:value2]</code>. Multiple terms are treated as an AND statement, in order to preform an OR search the terms that will be ORed are put in parenthesis and separated by OR statements. For example: <code>[(field:value OR field2:value)]</code>. AND is also available when necessary, following the same rules as the OR operator.The value can also contain comparison operators. Currently supported operators are:<ul><li><span class='operator'>+</span> greater than</li><li><span class='operator'>+.</span> greater than or equal</li><li><span class='operator'>-</span> less than</li><li><span class='operator'>-.</span> less than or equal</li><li><span class='operator'>!=</span> not equal</li></ul>For example: <code>[field:10+.]</code> This searches for fields with a value of 10 or greater.",
"description": "The actual query to be performed.<h3>Syntax</h3>The syntax uses a JSON like notation that always begins as an array. The fields can be checked by writing them in a json notation of field:value. The field name can use dot notation to access objects that are deeper than the top level. The value can be a quoted string, a number, or 'true'/'false' (without quotes). Strings may be quoted with either &quot; or &apos;. Multiple terms can be checked by separating them with a comma just like a JSON array: <code>[field:value, field2:value2]</code>. Multiple terms are treated as an AND statement, in order to preform an OR search the terms that will be ORed are put in parenthesis and separated by OR statements. For example: <code>[(field:value OR field2:value)]</code>. AND is also available when necessary, following the same rules as the OR operator. The value can also contain comparison operators. Currently supported operators are:<ul><li><span class='operator'>+</span> greater than</li><li><span class='operator'>+.</span> greater than or equal</li><li><span class='operator'>-</span> less than</li><li><span class='operator'>-.</span> less than or equal</li><li><span class='operator'>!=</span> not equal</li></ul>For example: <code>[field:10+.]</code> This searches for fields with a value of 10 or greater. The value can also compare against a range using one of the range operators:<ul><li><span class='operator'>-</span> between two values, not including endpoints</li><li><span class='operator'>-.</span> between two values, including the end value</li><li><span class='operator'>.-</span> between two values, including the beginning value</li><li><span class='operator'>.-.</span> between two values, including both endpoints</li></ul>For example: <code>[field:10.-15]</code> This searches for fields with a value 10 or greater and less than 15.",
"dataType": "string",
"paramType": "query"
},
Expand Down
98 changes: 66 additions & 32 deletions Common/node/lpquery.js
Expand Up @@ -11,9 +11,9 @@
var lpquery = (function(){
var parser = {trace: function trace() { },
yy: {},
symbols_: {"error":2,"queryBase":3,"prefix":4,"argList":5,"EOF":6,"literal_op":7,"GREATER":8,"GREATER_EQUAL":9,"LESSER":10,"LESSER_EQUAL":11,"NOT_EQUAL":12,"literal":13,"NUMBER":14,"STRING":15,"BOOLEAN":16,"member":17,"KEY":18,".":19,"expression":20,"colon":21,"OR":22,"AND":23,"expressionSubset":24,"(":25,"expressionList":26,")":27,",":28,"array":29,"[":30,"]":31,"arg":32,"=":33,"&":34,"$accept":0,"$end":1},
terminals_: {2:"error",4:"prefix",6:"EOF",8:"GREATER",9:"GREATER_EQUAL",10:"LESSER",11:"LESSER_EQUAL",12:"NOT_EQUAL",14:"NUMBER",15:"STRING",16:"BOOLEAN",18:"KEY",19:".",21:"colon",22:"OR",23:"AND",25:"(",27:")",28:",",30:"[",31:"]",33:"=",34:"&"},
productions_: [0,[3,3],[7,1],[7,1],[7,1],[7,1],[7,1],[13,1],[13,1],[13,1],[13,2],[17,1],[17,3],[20,1],[20,3],[20,3],[20,3],[20,1],[24,3],[26,1],[26,3],[29,3],[32,3],[32,3],[5,1],[5,3]],
symbols_: {"error":2,"queryBase":3,"prefix":4,"argList":5,"EOF":6,"literal_op":7,"GREATER":8,"GREATER_EQUAL":9,"LESSER":10,"LESSER_EQUAL":11,"NOT_EQUAL":12,"literal":13,"NUMBER":14,"STRING":15,"BOOLEAN":16,"literal_expression":17,".":18,"member":19,"KEY":20,"expression":21,"colon":22,"OR":23,"AND":24,"expressionSubset":25,"(":26,"expressionList":27,")":28,",":29,"array":30,"[":31,"]":32,"arg":33,"=":34,"&":35,"$accept":0,"$end":1},
terminals_: {2:"error",4:"prefix",6:"EOF",8:"GREATER",9:"GREATER_EQUAL",10:"LESSER",11:"LESSER_EQUAL",12:"NOT_EQUAL",14:"NUMBER",15:"STRING",16:"BOOLEAN",18:".",20:"KEY",22:"colon",23:"OR",24:"AND",26:"(",28:")",29:",",31:"[",32:"]",34:"=",35:"&"},
productions_: [0,[3,3],[7,1],[7,1],[7,1],[7,1],[7,1],[13,1],[13,1],[13,1],[17,1],[17,2],[17,3],[17,3],[17,4],[17,4],[19,1],[19,3],[21,1],[21,3],[21,3],[21,3],[21,1],[25,3],[27,1],[27,3],[30,3],[33,3],[33,3],[5,1],[5,3]],
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {

var $0 = $$.length - 1;
Expand All @@ -36,41 +36,51 @@ case 8:this.$ = $$[$0];
break;
case 9:this.$ = (yytext == 'true');
break;
case 10:this.$ = [$$[$0], $$[$0-1]];
case 10:this.$ = $$[$0];
break;
case 11:this.$ = $$[$0];
case 11:this.$ = [$$[$0], $$[$0-1]];
break;
case 12:this.$ = $$[$0-2] + '.' + $$[$0];
case 12:this.$ = ['range', $$[$0-2], $$[$0]];
break;
case 13:this.$ = ['literal', $$[$0]]
case 13:this.$ = ['range_eq', $$[$0-2], $$[$0]];
break;
case 14:this.$ = ['keyValue', $$[$0-2], $$[$0]];
case 14:this.$ = ['eq_range', $$[$0-3], $$[$0]];
break;
case 15:this.$ = ['OR', [$$[$0-2], $$[$0]]]
case 15:this.$ = ['eq_range_eq', $$[$0-3], $$[$0]];
break;
case 16:this.$ = ['AND', [$$[$0-2], $$[$0]]]
case 16:this.$ = $$[$0];
break;
case 17:this.$ = $$[$0];
case 17:this.$ = $$[$0-2] + '.' + $$[$0];
break;
case 18:this.$ = ['subset', $$[$0-1]];
case 18:this.$ = ['literal', $$[$0]]
break;
case 19:this.$ = [$$[$0]];
case 19:this.$ = ['keyValue', $$[$0-2], $$[$0]];
break;
case 20:this.$ = $$[$0-2]; this.$.push($$[$0]);
case 20:this.$ = ['OR', [$$[$0-2], $$[$0]]]
break;
case 21:this.$ = $$[$0-1];
case 21:this.$ = ['AND', [$$[$0-2], $$[$0]]]
break;
case 22:this.$ = [$$[$0-2], $$[$0]];
case 22:this.$ = $$[$0];
break;
case 23:this.$ = [$$[$0-2], $$[$0]];
case 23:this.$ = ['subset', $$[$0-1]];
break;
case 24:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1];
case 24:this.$ = [$$[$0]];
break;
case 25:$$[$0-2][$$[$0][0]] = $$[$0][1];
case 25:this.$ = $$[$0-2]; this.$.push($$[$0]);
break;
case 26:this.$ = $$[$0-1];
break;
case 27:this.$ = [$$[$0-2], $$[$0]];
break;
case 28:this.$ = [$$[$0-2], $$[$0]];
break;
case 29:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1];
break;
case 30:$$[$0-2][$$[$0][0]] = $$[$0][1];
break;
}
},
table: [{3:1,4:[1,2]},{1:[3]},{5:3,18:[1,5],32:4},{6:[1,6],34:[1,7]},{6:[2,24],34:[2,24]},{33:[1,8]},{1:[2,1]},{18:[1,5],32:9},{13:11,14:[1,13],15:[1,14],16:[1,15],29:10,30:[1,12]},{6:[2,25],34:[2,25]},{6:[2,22],34:[2,22]},{6:[2,23],7:16,8:[1,17],9:[1,18],10:[1,19],11:[1,20],12:[1,21],34:[2,23]},{13:24,14:[1,13],15:[1,14],16:[1,15],17:25,18:[1,27],20:23,24:26,25:[1,28],26:22},{6:[2,7],8:[2,7],9:[2,7],10:[2,7],11:[2,7],12:[2,7],22:[2,7],23:[2,7],27:[2,7],28:[2,7],31:[2,7],34:[2,7]},{6:[2,8],8:[2,8],9:[2,8],10:[2,8],11:[2,8],12:[2,8],22:[2,8],23:[2,8],27:[2,8],28:[2,8],31:[2,8],34:[2,8]},{6:[2,9],8:[2,9],9:[2,9],10:[2,9],11:[2,9],12:[2,9],22:[2,9],23:[2,9],27:[2,9],28:[2,9],31:[2,9],34:[2,9]},{6:[2,10],8:[2,10],9:[2,10],10:[2,10],11:[2,10],12:[2,10],22:[2,10],23:[2,10],27:[2,10],28:[2,10],31:[2,10],34:[2,10]},{6:[2,2],8:[2,2],9:[2,2],10:[2,2],11:[2,2],12:[2,2],22:[2,2],23:[2,2],27:[2,2],28:[2,2],31:[2,2],34:[2,2]},{6:[2,3],8:[2,3],9:[2,3],10:[2,3],11:[2,3],12:[2,3],22:[2,3],23:[2,3],27:[2,3],28:[2,3],31:[2,3],34:[2,3]},{6:[2,4],8:[2,4],9:[2,4],10:[2,4],11:[2,4],12:[2,4],22:[2,4],23:[2,4],27:[2,4],28:[2,4],31:[2,4],34:[2,4]},{6:[2,5],8:[2,5],9:[2,5],10:[2,5],11:[2,5],12:[2,5],22:[2,5],23:[2,5],27:[2,5],28:[2,5],31:[2,5],34:[2,5]},{6:[2,6],8:[2,6],9:[2,6],10:[2,6],11:[2,6],12:[2,6],22:[2,6],23:[2,6],27:[2,6],28:[2,6],31:[2,6],34:[2,6]},{28:[1,30],31:[1,29]},{22:[1,31],23:[1,32],27:[2,19],28:[2,19],31:[2,19]},{7:16,8:[1,17],9:[1,18],10:[1,19],11:[1,20],12:[1,21],22:[2,13],23:[2,13],27:[2,13],28:[2,13],31:[2,13]},{19:[1,34],21:[1,33]},{22:[2,17],23:[2,17],27:[2,17],28:[2,17],31:[2,17]},{19:[2,11],21:[2,11]},{13:24,14:[1,13],15:[1,14],16:[1,15],17:25,18:[1,27],20:23,24:26,25:[1,28],26:35},{6:[2,21],34:[2,21]},{13:24,14:[1,13],15:[1,14],16:[1,15],17:25,18:[1,27],20:36,24:26,25:[1,28]},{13:24,14:[1,13],15:[1,14],16:[1,15],17:25,18:[1,27],20:37,24:26,25:[1,28]},{13:24,14:[1,13],15:[1,14],16:[1,15],17:25,18:[1,27],20:38,24:26,25:[1,28]},{13:39,14:[1,13],15:[1,14],16:[1,15]},{18:[1,40]},{27:[1,41],28:[1,30]},{22:[1,31],23:[1,32],27:[2,20],28:[2,20],31:[2,20]},{22:[2,15],23:[2,15],27:[2,15],28:[2,15],31:[2,15]},{22:[2,16],23:[2,16],27:[2,16],28:[2,16],31:[2,16]},{7:16,8:[1,17],9:[1,18],10:[1,19],11:[1,20],12:[1,21],22:[2,14],23:[2,14],27:[2,14],28:[2,14],31:[2,14]},{19:[2,12],21:[2,12]},{22:[2,18],23:[2,18],27:[2,18],28:[2,18],31:[2,18]}],
table: [{3:1,4:[1,2]},{1:[3]},{5:3,20:[1,5],33:4},{6:[1,6],35:[1,7]},{6:[2,29],35:[2,29]},{34:[1,8]},{1:[2,1]},{20:[1,5],33:9},{13:11,14:[1,13],15:[1,14],16:[1,15],30:10,31:[1,12]},{6:[2,30],35:[2,30]},{6:[2,27],35:[2,27]},{6:[2,28],35:[2,28]},{13:18,14:[1,13],15:[1,14],16:[1,15],19:19,20:[1,21],21:17,25:20,26:[1,22],27:16},{6:[2,7],8:[2,7],9:[2,7],10:[2,7],11:[2,7],12:[2,7],18:[2,7],23:[2,7],24:[2,7],28:[2,7],29:[2,7],32:[2,7],35:[2,7]},{6:[2,8],8:[2,8],9:[2,8],10:[2,8],11:[2,8],12:[2,8],18:[2,8],23:[2,8],24:[2,8],28:[2,8],29:[2,8],32:[2,8],35:[2,8]},{6:[2,9],8:[2,9],9:[2,9],10:[2,9],11:[2,9],12:[2,9],18:[2,9],23:[2,9],24:[2,9],28:[2,9],29:[2,9],32:[2,9],35:[2,9]},{29:[1,24],32:[1,23]},{23:[1,25],24:[1,26],28:[2,24],29:[2,24],32:[2,24]},{23:[2,18],24:[2,18],28:[2,18],29:[2,18],32:[2,18]},{18:[1,28],22:[1,27]},{23:[2,22],24:[2,22],28:[2,22],29:[2,22],32:[2,22]},{18:[2,16],22:[2,16]},{13:18,14:[1,13],15:[1,14],16:[1,15],19:19,20:[1,21],21:17,25:20,26:[1,22],27:29},{6:[2,26],35:[2,26]},{13:18,14:[1,13],15:[1,14],16:[1,15],19:19,20:[1,21],21:30,25:20,26:[1,22]},{13:18,14:[1,13],15:[1,14],16:[1,15],19:19,20:[1,21],21:31,25:20,26:[1,22]},{13:18,14:[1,13],15:[1,14],16:[1,15],19:19,20:[1,21],21:32,25:20,26:[1,22]},{13:34,14:[1,13],15:[1,14],16:[1,15],17:33},{20:[1,35]},{28:[1,36],29:[1,24]},{23:[1,25],24:[1,26],28:[2,25],29:[2,25],32:[2,25]},{23:[2,20],24:[2,20],28:[2,20],29:[2,20],32:[2,20]},{23:[2,21],24:[2,21],28:[2,21],29:[2,21],32:[2,21]},{23:[2,19],24:[2,19],28:[2,19],29:[2,19],32:[2,19]},{7:37,8:[1,41],9:[1,42],10:[1,38],11:[1,39],12:[1,43],18:[1,40],23:[2,10],24:[2,10],28:[2,10],29:[2,10],32:[2,10]},{18:[2,17],22:[2,17]},{23:[2,23],24:[2,23],28:[2,23],29:[2,23],32:[2,23]},{23:[2,11],24:[2,11],28:[2,11],29:[2,11],32:[2,11]},{13:44,14:[1,13],15:[1,14],16:[1,15],23:[2,4],24:[2,4],28:[2,4],29:[2,4],32:[2,4]},{13:45,14:[1,13],15:[1,14],16:[1,15],23:[2,5],24:[2,5],28:[2,5],29:[2,5],32:[2,5]},{10:[1,46],11:[1,47]},{23:[2,2],24:[2,2],28:[2,2],29:[2,2],32:[2,2]},{23:[2,3],24:[2,3],28:[2,3],29:[2,3],32:[2,3]},{23:[2,6],24:[2,6],28:[2,6],29:[2,6],32:[2,6]},{23:[2,12],24:[2,12],28:[2,12],29:[2,12],32:[2,12]},{23:[2,13],24:[2,13],28:[2,13],29:[2,13],32:[2,13]},{13:48,14:[1,13],15:[1,14],16:[1,15]},{13:49,14:[1,13],15:[1,14],16:[1,15]},{23:[2,14],24:[2,14],28:[2,14],29:[2,14],32:[2,14]},{23:[2,15],24:[2,15],28:[2,15],29:[2,15],32:[2,15]}],
defaultActions: {6:[2,1]},
parseError: function parseError(str, hash) {
throw new Error(str);
Expand Down Expand Up @@ -378,39 +388,39 @@ var YYSTATE=YY_START
switch($avoiding_name_collisions) {
case 0:/* skip */
break;
case 1:return 22;
case 1:return 23;
break;
case 2:return 23;
case 2:return 24;
break;
case 3:return 34;
case 3:return 35;
break;
case 4:return 19;
case 4:return 18;
break;
case 5:{yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 15;}
break;
case 6:{yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 15;}
break;
case 7:return 14;
break;
case 8:return 30
case 8:return 31
break;
case 9:return 31
case 9:return 32
break;
case 10:return 18;
case 10:return 20;
break;
case 11:return 16;
break;
case 12:return '?';
break;
case 13:return 33;
case 13:return 34;
break;
case 14:return 28;
case 14:return 29;
break;
case 15:return 25;
case 15:return 26;
break;
case 16:return 27;
case 16:return 28;
break;
case 17:return 21;
case 17:return 22;
break;
case 18:return 9;
break;
Expand Down Expand Up @@ -497,6 +507,30 @@ exports.buildMongoQuery = function(parseTree) {
},
"!=":function(node) {
return {$ne:treeTranslation.translateNode(node[1])};
},
"range":function(node) {
return {
$gt:treeTranslation.translateNode(node[1]),
$lt:treeTranslation.translateNode(node[2])
};
},
"range_eq":function(node) {
return {
$gt:treeTranslation.translateNode(node[1]),
$lte:treeTranslation.translateNode(node[2])
};
},
"eq_range":function(node) {
return {
$gte:treeTranslation.translateNode(node[1]),
$lt:treeTranslation.translateNode(node[2])
};
},
"eq_range_eq":function(node) {
return {
$gte:treeTranslation.translateNode(node[1]),
$lte:treeTranslation.translateNode(node[2])
};
}
};
var queryResult = {
Expand Down
13 changes: 10 additions & 3 deletions Docs/query.jison
Expand Up @@ -53,16 +53,23 @@ var grammar = {
"literal" : [
["NUMBER", "$$ = Number(yytext);"],
["STRING", "$$ = $1;"],
["BOOLEAN", "$$ = (yytext == 'true');"],
["literal literal_op", "$$ = [$2, $1];"]
["BOOLEAN", "$$ = (yytext == 'true');"]
],
"literal_expression" : [
["literal", "$$ = $1;"],
["literal literal_op", "$$ = [$2, $1];"],
["literal LESSER literal", "$$ = ['range', $1, $3];"],
["literal LESSER_EQUAL literal", "$$ = ['range_eq', $1, $3];"],
["literal . LESSER literal", "$$ = ['eq_range', $1, $4];"],
["literal . LESSER_EQUAL literal", "$$ = ['eq_range_eq', $1, $4];"]
],
"member" : [
["KEY", "$$ = $1;"],
["member . KEY", "$$ = $1 + '.' + $3;"]
],
"expression": [
["literal", "$$ = ['literal', $1]"],
["member colon literal", "$$ = ['keyValue', $1, $3];"],
["member colon literal_expression", "$$ = ['keyValue', $1, $3];"],
["expression OR expression", "$$ = ['OR', [$1, $3]]"],
["expression AND expression", "$$ = ['AND', [$1, $3]]"],
["expressionSubset", "$$ = $1;"],
Expand Down
10 changes: 9 additions & 1 deletion tests/lpquery-test-local.js
Expand Up @@ -79,14 +79,18 @@ vows.describe("Query System").addBatch({
}
},
"Advanced query operators" : {
topic:lpquery.parse("/getPhotos?terms=[gt:21+, gte:22+., lt:20-, lte:19-., (key:1 OR key:2)]&limit=10"),
topic:lpquery.parse("/getPhotos?terms=[gt:21+, gte:22+., lt:20-, lte:19-., range:2-4, range_eq:5-.7, eq_range:8.-11, eq_range_eq:12.-.17, (key:1 OR key:2)]&limit=10"),
"generate a valid parse tree":function(topic) {
assert.deepEqual(topic, [ 'Photos',
{ terms:
[ [ 'keyValue', 'gt', [ '+', 21 ] ],
[ 'keyValue', 'gte', [ '+.', 22 ] ],
[ 'keyValue', 'lt', [ '-', 20 ] ],
[ 'keyValue', 'lte', [ '-.', 19 ] ],
[ 'keyValue', 'range', [ 'range', 2, 4 ] ],
[ 'keyValue', 'range_eq', [ 'range_eq', 5, 7 ] ],
[ 'keyValue', 'eq_range', [ 'eq_range', 8, 11 ] ],
[ 'keyValue', 'eq_range_eq', [ 'eq_range_eq', 12, 17 ] ],
[ 'subset',
[ [ 'OR',
[ [ 'keyValue', 'key', 1 ], [ 'keyValue', 'key', 2 ] ] ] ] ] ],
Expand All @@ -100,6 +104,10 @@ vows.describe("Query System").addBatch({
gte: { $gte: 22 },
lt: { $lt: 20 },
lte: { $lte: 19 },
range: { $gt: 2, $lt: 4 },
range_eq: { $gt: 5, $lte: 7 },
eq_range: { $gte: 8, $lt: 11 },
eq_range_eq: { $gte: 12, $lte: 17 },
$or: [
{ key: 1 },
{ key: 2 }
Expand Down

0 comments on commit d882bc3

Please sign in to comment.