|
|
@@ -0,0 +1,735 @@
|
|
|
+"use strict";
|
|
|
+
|
|
|
+// Autogenerated by cport.py on 2013-09-17 14:37
|
|
|
+var MatchExpressionParser = module.exports = function (){
|
|
|
+
|
|
|
+}, klass = MatchExpressionParser, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
|
|
|
+
|
|
|
+// DEPENDENCIES
|
|
|
+var errors = require("../../Errors.js"),
|
|
|
+ ErrorCodes = errors.ErrorCodes,
|
|
|
+ AndMatchExpression = require("./AndMatchExpression.js"),
|
|
|
+ MatchExpression = require("./MatchExpression.js"),
|
|
|
+ OrMatchExpression = require("./OrMatchExpression.js"),
|
|
|
+ ModMatchExpression = require("./ModMatchExpression.js"),
|
|
|
+ NorMatchExpression = require("./NorMatchExpression.js"),
|
|
|
+ NotMatchExpression = require("./NotMatchExpression.js"),
|
|
|
+ LTMatchExpression = require("./LTMatchExpression.js"),
|
|
|
+ LTEMatchExpression = require("./LTEMatchExpression.js"),
|
|
|
+ GTMatchExpression = require("./GTMatchExpression.js"),
|
|
|
+ GTEMatchExpression = require("./GTEMatchExpression.js"),
|
|
|
+ InMatchExpression = require("./InMatchExpression.js"),
|
|
|
+ SizeMatchExpression = require("./SizeMatchExpression.js"),
|
|
|
+ TypeMatchExpression = require("./TypeMatchExpression.js"),
|
|
|
+ ExistsMatchExpression = require("./ExistsMatchExpression.js"),
|
|
|
+ EqualityMatchExpression = require("./EqualityMatchExpression.js"),
|
|
|
+ ArrayMatchingMatchExpression = require("./ArrayMatchingMatchExpression.js"),
|
|
|
+ RegexMatchExpression = require("./RegexMatchExpression.js"),
|
|
|
+ FalseMatchExpression = require("./FalseMatchExpression.js"),
|
|
|
+ ComparisonMatchExpression = require("./ComparisonMatchExpression.js"),
|
|
|
+ ElemMatchValueMatchExpression = require("./ElemMatchValueMatchExpression.js"),
|
|
|
+ ElemMatchObjectMatchExpression = require("./ElemMatchObjectMatchExpression.js"),
|
|
|
+ AllElemMatchOp = require("./AllElemMatchOp.js"),
|
|
|
+ AtomicMatchExpression = require("./AtomicMatchExpression.js");
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Check if the input element is an expression
|
|
|
+ * @method _isExpressionDocument
|
|
|
+ * @param element
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._isExpressionDocument = function _isExpressionDocument(element){
|
|
|
+ // File: expression_parser.cpp lines: 340-355
|
|
|
+ if (!(element instanceof Object))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (Object.keys(element).length === 0)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ var name = Object.keys(element)[0];
|
|
|
+ if (name[0] != '$')
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if ("$ref" == name)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse the input object into individual elements
|
|
|
+ * @method _parse
|
|
|
+ * @param obj
|
|
|
+ * @param topLevel
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parse = function _parse(obj, topLevel){
|
|
|
+ // File: expression_parser.cpp lines: 217-319
|
|
|
+ var rest, temp, status, element, eq, real;
|
|
|
+ var root = new AndMatchExpression();
|
|
|
+ var objkeys = Object.keys(obj);
|
|
|
+ var currname, currval;
|
|
|
+ for (var i = 0; i < objkeys.length; i++) {
|
|
|
+ currname = objkeys[i];
|
|
|
+ currval = obj[currname];
|
|
|
+ if (currname[0] == '$' ) {
|
|
|
+ rest = currname.substr(1);
|
|
|
+
|
|
|
+ // TODO: optimize if block?
|
|
|
+ if ("or" == rest) {
|
|
|
+ if (!(currval instanceof Array))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$or needs an array"};
|
|
|
+ temp = new OrMatchExpression();
|
|
|
+ status = this._parseTreeList(currval, temp);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ root.add(temp);
|
|
|
+ }
|
|
|
+ else if ("and" == rest) {
|
|
|
+ if (!(currval instanceof Array))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"and needs an array"};
|
|
|
+ temp = new AndMatchExpression();
|
|
|
+ status = this._parseTreeList(currval, temp);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ root.add(temp);
|
|
|
+ }
|
|
|
+ else if ("nor" == rest) {
|
|
|
+ if (!(currval instanceof Array))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"and needs an array"};
|
|
|
+ temp = new NorMatchExpression();
|
|
|
+ status = this._parseTreeList(currval, temp);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ root.add(temp);
|
|
|
+ }
|
|
|
+ else if (("atomic" == rest) || ("isolated" == rest)) {
|
|
|
+ if (!topLevel)
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$atomic/$isolated has to be at the top level"};
|
|
|
+ if (element)
|
|
|
+ root.add(new AtomicMatchExpression());
|
|
|
+ }
|
|
|
+ else if ("where" == rest) {
|
|
|
+ /*
|
|
|
+ if ( !topLevel )
|
|
|
+ return StatusWithMatchExpression( ErrorCodes::BAD_VALUE, "$where has to be at the top level" );
|
|
|
+ */
|
|
|
+
|
|
|
+ return {'code':'FAILED_TO_PARSE', 'desc':'Where unimplimented.'};
|
|
|
+ /*
|
|
|
+ status = this.expressionParserWhereCallback(element);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ root.add(status.result);*/
|
|
|
+ }
|
|
|
+ else if ("comment" == rest) {
|
|
|
+ 1+1;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"unknown top level operator: " + currname};
|
|
|
+ }
|
|
|
+
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this._isExpressionDocument(currval)) {
|
|
|
+ status = this._parseSub(currname, currval, root);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (currval instanceof RegExp) {
|
|
|
+ status = this._parseRegexElement(currname, currval);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ root.add(status.result);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ eq = new EqualityMatchExpression();
|
|
|
+ status = eq.init(currname, currval);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ root.add(eq);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (root.numChildren() == 1) {
|
|
|
+ return {code:ErrorCodes.OK, result:root.getChild(0)};
|
|
|
+ }
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:root};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse the $all element
|
|
|
+ * @method _parseAll
|
|
|
+ * @param name
|
|
|
+ * @param element
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseAll = function _parseAll(name, element){
|
|
|
+ // File: expression_parser.cpp lines: 512-583
|
|
|
+ var status, i;
|
|
|
+
|
|
|
+ if (!(element instanceof Array))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$all needs an array"};
|
|
|
+
|
|
|
+ var arr = element;
|
|
|
+ if ((arr[0] instanceof Object) && ("$elemMatch" == Object.keys(arr[0])[0])) {
|
|
|
+ // $all : [ { $elemMatch : {} } ... ]
|
|
|
+
|
|
|
+ var temp = new AllElemMatchOp();
|
|
|
+ status = temp.init(name);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ for (i = 0; i < arr.length; i++) {
|
|
|
+ var hopefullyElemMatchElement = arr[i];
|
|
|
+
|
|
|
+ if (!(hopefullyElemMatchElement instanceof Object)) {
|
|
|
+ // $all : [ { $elemMatch : ... }, 5 ]
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$all/$elemMatch has to be consistent"};
|
|
|
+ }
|
|
|
+
|
|
|
+ if ("$elemMatch" != Object.keys(hopefullyElemMatchElement)[0]) {
|
|
|
+ // $all : [ { $elemMatch : ... }, { x : 5 } ]
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$all/$elemMatch has to be consistent"};
|
|
|
+ }
|
|
|
+
|
|
|
+ status = this._parseElemMatch("", hopefullyElemMatchElement.$elemMatch ); // TODO: wrong way to do this?
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ temp.add(status.result);
|
|
|
+ }
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+ }
|
|
|
+
|
|
|
+ var myAnd = new AndMatchExpression();
|
|
|
+ for (i = 0; i < arr.length; i++) {
|
|
|
+ var e = arr[i];
|
|
|
+
|
|
|
+ if (e instanceof RegExp) {
|
|
|
+ var r = new RegexMatchExpression();
|
|
|
+ status = r.init(name, e);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ myAnd.add(r);
|
|
|
+ }
|
|
|
+ else if ((e instanceof Object) && (typeof(Object.keys(e)[0] == 'string' && Object.keys(e)[0][0] == '$' ))) {
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"no $ expressions in $all"};
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ var x = new EqualityMatchExpression();
|
|
|
+ status = x.init(name, e);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ myAnd.add(x);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (myAnd.numChildren() === 0) {
|
|
|
+ return {code:ErrorCodes.OK, result:new FalseMatchExpression()};
|
|
|
+ }
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:myAnd};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse the input array and add new RegexMatchExpressions to entries
|
|
|
+ * @method _parseArrayFilterEntries
|
|
|
+ * @param entries
|
|
|
+ * @param theArray
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseArrayFilterEntries = function _parseArrayFilterEntries(entries, theArray){
|
|
|
+ // File: expression_parser.cpp lines: 445-468
|
|
|
+ var status, e, r;
|
|
|
+ for (var i = 0; i < theArray.length; i++) {
|
|
|
+ e = theArray[i];
|
|
|
+
|
|
|
+ if (e instanceof RegExp ) {
|
|
|
+ r = new RegexMatchExpression();
|
|
|
+ status = r.init("", e);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ status = entries.addRegex(r);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ status = entries.addEquality(e);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {code:ErrorCodes.OK};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse the input ComparisonMatchExpression
|
|
|
+ * @method _parseComparison
|
|
|
+ * @param name
|
|
|
+ * @param cmp
|
|
|
+ * @param element
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseComparison = function _parseComparison(name, cmp, element){
|
|
|
+ // File: expression_parser.cpp lines: 34-43
|
|
|
+ var temp = new ComparisonMatchExpression(cmp);
|
|
|
+
|
|
|
+ var status = temp.init(name, element);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse an element match into the appropriate expression
|
|
|
+ * @method _parseElemMatch
|
|
|
+ * @param name
|
|
|
+ * @param element
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseElemMatch = function _parseElemMatch(name, element){
|
|
|
+ // File: expression_parser.cpp lines: 471-509
|
|
|
+ var temp, status;
|
|
|
+ if (!(element instanceof Object))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$elemMatch needs an Object"};
|
|
|
+
|
|
|
+ if (this._isExpressionDocument(element)) {
|
|
|
+ // value case
|
|
|
+
|
|
|
+ var theAnd = new AndMatchExpression();
|
|
|
+ status = this._parseSub("", element, theAnd);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ temp = new ElemMatchValueMatchExpression();
|
|
|
+ status = temp.init(name);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ for (var i = 0; i < theAnd.numChildren(); i++ ) {
|
|
|
+ temp.add(theAnd.getChild(i));
|
|
|
+ }
|
|
|
+ theAnd.clearAndRelease();
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+ }
|
|
|
+
|
|
|
+ // object case
|
|
|
+
|
|
|
+ status = this._parse(element, false);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ temp = new ElemMatchObjectMatchExpression();
|
|
|
+ status = temp.init(name, status.result);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse a ModMatchExpression
|
|
|
+ * @method _parseMOD
|
|
|
+ * @param name
|
|
|
+ * @param element
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseMOD = function _parseMOD(name, element){
|
|
|
+ // File: expression_parser.cpp lines: 360-387
|
|
|
+ var d,r;
|
|
|
+ if (!(element instanceof Array))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, result:"malformed mod, needs to be an array"};
|
|
|
+ if (element.length < 2)
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, result:"malformed mod, not enough elements"};
|
|
|
+ if (element.length > 2)
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, result:"malformed mod, too many elements"};
|
|
|
+ if ((typeof(element[0]) != 'number')) {
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, result:"malformed mod, divisor not a number"};
|
|
|
+ } else {
|
|
|
+ d = element[0];
|
|
|
+ }
|
|
|
+ if (( typeof(element[1]) != 'number') ) {
|
|
|
+ r = 0;
|
|
|
+ } else {
|
|
|
+ r = element[1];
|
|
|
+ }
|
|
|
+
|
|
|
+ var temp = new ModMatchExpression();
|
|
|
+ var status = temp.init( name, d, r);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse a NotMatchExpression
|
|
|
+ * @method _parseNot
|
|
|
+ * @param name
|
|
|
+ * @param element
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseNot = function _parseNot(name, element){
|
|
|
+ // File: expression_parser_tree.cpp lines: 55-91
|
|
|
+ var status;
|
|
|
+ if (element instanceof RegExp) {
|
|
|
+ status = this._parseRegexElement(name, element);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ var n = new NotMatchExpression();
|
|
|
+ status = n.init(status.result);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ return {code:ErrorCodes.OK, result:n};
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(element instanceof Object))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, result:"$not needs a regex or a document"};
|
|
|
+
|
|
|
+ if (element == {})
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, result:"$not cannot be empty"};
|
|
|
+
|
|
|
+ var theAnd = new AndMatchExpression();
|
|
|
+ status = this._parseSub(name, element, theAnd);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ // TODO: this seems arbitrary?
|
|
|
+ // tested in jstests/not2.js
|
|
|
+ for (var i = 0; i < theAnd.numChildren(); i++) {
|
|
|
+ if (theAnd.getChild(i).matchType == MatchExpression.REGEX) {
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, result:"$not cannot have a regex"};
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var theNot = new NotMatchExpression();
|
|
|
+ status = theNot.init(theAnd);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:theNot};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse a RegexMatchExpression
|
|
|
+ * @method _parseRegexDocument
|
|
|
+ * @param name
|
|
|
+ * @param doc
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseRegexDocument = function _parseRegexDocument(name, doc){
|
|
|
+ // File: expression_parser.cpp lines: 402-442
|
|
|
+ var regex = '', regexOptions = '', e;
|
|
|
+
|
|
|
+ if(doc.$regex) {
|
|
|
+ e = doc.$regex;
|
|
|
+ if(e instanceof RegExp) {
|
|
|
+ var str = e.toString(),
|
|
|
+ flagIndex = 0;
|
|
|
+ for (var c = str.length; c > 0; c--){
|
|
|
+ if (str[c] == '/') {
|
|
|
+ flagIndex = c;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ regex = (flagIndex? str : str.substr(1, flagIndex-1));
|
|
|
+ regexOptions = str.substr(flagIndex, str.length);
|
|
|
+ } else if (typeof(e) == 'string') {
|
|
|
+ regex = e;
|
|
|
+ } else {
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$regex has to be a string"};
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(doc.$options) {
|
|
|
+ e = doc.$options;
|
|
|
+ if(typeof(e) == 'string') {
|
|
|
+ regexOptions = e;
|
|
|
+ } else {
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$options has to be a string"};
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var temp = new RegexMatchExpression();
|
|
|
+ var status = temp.init(name, regex, regexOptions);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse an element into a RegexMatchExpression
|
|
|
+ * @method _parseRegexElement
|
|
|
+ * @param name
|
|
|
+ * @param element
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseRegexElement = function _parseRegexElement(name, element){
|
|
|
+ // File: expression_parser.cpp lines: 390-399
|
|
|
+ if (!(element instanceof RegExp))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"not a regex"};
|
|
|
+
|
|
|
+ var str = element.toString(),
|
|
|
+ flagIndex = 0;
|
|
|
+ for (var c = str.length; c > 0; c--){
|
|
|
+ if (str[c] == '/') {
|
|
|
+ flagIndex = c;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ var regex = str.substr(1, flagIndex-1),
|
|
|
+ regexOptions = str.substr(flagIndex+1, str.length),
|
|
|
+ temp = new RegexMatchExpression(),
|
|
|
+ status = temp.init(name, regex, regexOptions);
|
|
|
+
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse a sub expression
|
|
|
+ * @method _parseSub
|
|
|
+ * @param name
|
|
|
+ * @param sub
|
|
|
+ * @param root
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseSub = function _parseSub(name, sub, root){
|
|
|
+ // File: expression_parser.cpp lines: 322-337
|
|
|
+ var subkeys = Object.keys(sub),
|
|
|
+ currname, currval;
|
|
|
+
|
|
|
+ for (var i = 0; i < subkeys.length; i++) {
|
|
|
+ currname = subkeys[i];
|
|
|
+ currval = sub[currname];
|
|
|
+ var deep = {};
|
|
|
+ deep[currname] = currval;
|
|
|
+
|
|
|
+ var status = this._parseSubField(sub, root, name, deep);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ if (status.result)
|
|
|
+ root.add(status.result);
|
|
|
+ }
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:root};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse a sub expression field
|
|
|
+ * @method _parseSubField
|
|
|
+ * @param context
|
|
|
+ * @param andSoFar
|
|
|
+ * @param name
|
|
|
+ * @param element
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseSubField = function _parseSubField(context, andSoFar, name, element){
|
|
|
+ // File: expression_parser.cpp lines: 46-214
|
|
|
+ // TODO: these should move to getGtLtOp, or its replacement
|
|
|
+ var currname = Object.keys(element)[0];
|
|
|
+ var currval = element[currname];
|
|
|
+ if ("$eq" == currname)
|
|
|
+ return this._parseComparison(name, 'EQ', currval);
|
|
|
+
|
|
|
+ if ("$not" == currname) {
|
|
|
+ return this._parseNot(name, currval);
|
|
|
+ }
|
|
|
+
|
|
|
+ var status, temp, temp2;
|
|
|
+ switch (currname) {
|
|
|
+ case '$lt':
|
|
|
+ return this._parseComparison(name, 'LT', currval);
|
|
|
+ case '$lte':
|
|
|
+ return this._parseComparison(name, 'LTE', currval);
|
|
|
+ case '$gt':
|
|
|
+ return this._parseComparison(name, 'GT', currval);
|
|
|
+ case '$gte':
|
|
|
+ return this._parseComparison(name, 'GTE', currval);
|
|
|
+ case '$ne':
|
|
|
+ status = this._parseComparison(name, 'EQ', currval);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ var n = new NotMatchExpression();
|
|
|
+ status = n.init(status.result);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ return {code:ErrorCodes.OK, result:n};
|
|
|
+ case '$eq':
|
|
|
+ return this._parseComparison(name, 'EQ', currval);
|
|
|
+
|
|
|
+ case '$in':
|
|
|
+ if (!(currval instanceof Array))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$in needs an array"};
|
|
|
+ temp = new InMatchExpression();
|
|
|
+ status = temp.init(name);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ status = this._parseArrayFilterEntries(temp.getArrayFilterEntries(), currval);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+
|
|
|
+ case '$nin':
|
|
|
+ if (!(currval instanceof Array))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$nin needs an array"};
|
|
|
+ temp = new InMatchExpression();
|
|
|
+ status = temp.init(name);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ status = this._parseArrayFilterEntries(temp.getArrayFilterEntries(), currval);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ temp2 = new NotMatchExpression();
|
|
|
+ status = temp2.init(temp);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:temp2};
|
|
|
+
|
|
|
+ case '$size':
|
|
|
+ var size = 0;
|
|
|
+ if ( typeof(currval) === 'string')
|
|
|
+ // matching old odd semantics
|
|
|
+ size = 0;
|
|
|
+ else if (typeof(currval) === 'number')
|
|
|
+ size = currval;
|
|
|
+ else {
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$size needs a number"};
|
|
|
+ }
|
|
|
+
|
|
|
+ temp = new SizeMatchExpression();
|
|
|
+ status = temp.init(name, size);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+
|
|
|
+ case '$exists':
|
|
|
+ if (currval == {})
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$exists can't be eoo"};
|
|
|
+ temp = new ExistsMatchExpression();
|
|
|
+ status = temp.init(name);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ if (currval)
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+ temp2 = new NotMatchExpression();
|
|
|
+ status = temp2.init(temp);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ return {code:ErrorCodes.OK, result:temp2};
|
|
|
+
|
|
|
+ case '$type':
|
|
|
+ if (typeof(currval) != 'number')
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$type has to be a number"};
|
|
|
+ var type = currval;
|
|
|
+ temp = new TypeMatchExpression();
|
|
|
+ status = temp.init(name, type);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+ return {code:ErrorCodes.OK, result:temp};
|
|
|
+
|
|
|
+ case '$mod':
|
|
|
+ return this._parseMOD(name, currval);
|
|
|
+
|
|
|
+ case '$options':
|
|
|
+ // TODO: try to optimize this
|
|
|
+ // we have to do this since $options can be before or after a $regex
|
|
|
+ // but we validate here
|
|
|
+ for(var i = 0; i < Object.keys(context).length; i++) {
|
|
|
+ temp = Object.keys(context)[i];
|
|
|
+ if (temp == '$regex')
|
|
|
+ return {code:ErrorCodes.OK, result:null};
|
|
|
+ }
|
|
|
+
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$options needs a $regex"};
|
|
|
+
|
|
|
+ case '$regex':
|
|
|
+ return this._parseRegexDocument(name, context);
|
|
|
+
|
|
|
+ case '$elemMatch':
|
|
|
+ return this._parseElemMatch(name, currval);
|
|
|
+
|
|
|
+ case '$all':
|
|
|
+ return this._parseAll(name, currval);
|
|
|
+
|
|
|
+ case '$geoWithin':
|
|
|
+ case '$geoIntersects':
|
|
|
+ case '$near':
|
|
|
+ case '$nearSphere':
|
|
|
+ var x = 'Temporary value until Geo fns implimented.';
|
|
|
+ return this.expressionParserGeoCallback(name, x, context);
|
|
|
+ default:
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"not handled: " + element};
|
|
|
+ } // end switch
|
|
|
+
|
|
|
+
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"not handled: " + element};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Parse a list of parsable elements
|
|
|
+ * @method _parseTreeList
|
|
|
+ * @param arr
|
|
|
+ * @param out
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto._parseTreeList = function _parseTreeList(arr, out){
|
|
|
+ // File: expression_parser_tree.cpp lines: 33-52
|
|
|
+ if (arr.length === 0)
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$and/$or/$nor must be a nonempty array"};
|
|
|
+
|
|
|
+ var status, element;
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ element = arr[i];
|
|
|
+
|
|
|
+ if (!(element instanceof Object))
|
|
|
+ return {code:ErrorCodes.BAD_VALUE, description:"$or/$and/$nor entries need to be full objects"};
|
|
|
+
|
|
|
+ status = this._parse(element, false);
|
|
|
+ if (status.code != ErrorCodes.OK)
|
|
|
+ return status;
|
|
|
+
|
|
|
+ out.add(status.result);
|
|
|
+ }
|
|
|
+ return {code:ErrorCodes.OK};
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * Wrapper for _parse
|
|
|
+ * @method parse
|
|
|
+ * @param obj
|
|
|
+ *
|
|
|
+ */
|
|
|
+proto.parse = function parse(obj){
|
|
|
+ // File: expression_parser.h lines: 40-41
|
|
|
+ return this._parse(obj, true);
|
|
|
+};
|