|
|
@@ -26,10 +26,10 @@ var Expression = module.exports = function Expression() {
|
|
|
});
|
|
|
|
|
|
|
|
|
-
|
|
|
-function fn(){
|
|
|
- return;
|
|
|
-}
|
|
|
+//NOTE: DEVIATION FROM MONGO: What is this?
|
|
|
+//function fn(){
|
|
|
+// return;
|
|
|
+//}
|
|
|
|
|
|
|
|
|
// NESTED CLASSES
|
|
|
@@ -74,18 +74,38 @@ var ObjectCtx = Expression.ObjectCtx = (function() {
|
|
|
return klass;
|
|
|
})();
|
|
|
|
|
|
-proto.removeFieldPrefix = function removeFieldPrefix(prefixedField) {
|
|
|
- if (prefixedField.indexOf("\0") !== -1) {
|
|
|
- // field path must not contain embedded null characters - 16419
|
|
|
- }
|
|
|
- if (prefixedField[0] !== '$') {
|
|
|
- // "field path references must be prefixed with a '$'"
|
|
|
- }
|
|
|
- return prefixedField.slice(1);
|
|
|
-};
|
|
|
+//NOTE: DEVIATION FROM MONGO: This probably isn't a deviation. #removeFieldPrefix has been duplicated possibly accidently.
|
|
|
+// there is only one use of removeFieldPrefix in all of mungedb-aggregate. This is in UnwindDocumentSource and it refers to the klass version.
|
|
|
+//
|
|
|
+//proto.removeFieldPrefix = function removeFieldPrefix(prefixedField) {
|
|
|
+// if (prefixedField.indexOf("\0") !== -1) {
|
|
|
+// throw new Error("field path must not contain embedded null characters; code 16419");
|
|
|
+// }
|
|
|
+// if (prefixedField[0] !== '$') {
|
|
|
+// throw new Error("field path references must be prefixed with a '$'; code 15982");
|
|
|
+// }
|
|
|
+// return prefixedField.slice(1);
|
|
|
+//};
|
|
|
+
|
|
|
var KIND_UNKNOWN = 0,
|
|
|
KIND_NOTOPERATOR = 1,
|
|
|
KIND_OPERATOR = 2;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * Produce a field path string with the field prefix removed.
|
|
|
+ * Throws an error if the field prefix is not present.
|
|
|
+ *
|
|
|
+ * @static
|
|
|
+ * @param prefixedField the prefixed field
|
|
|
+ * @returns the field path with the prefix removed
|
|
|
+ **/
|
|
|
+klass.removeFieldPrefix = function removeFieldPrefix(prefixedField) {
|
|
|
+ if (prefixedField.indexOf("\0") != -1) throw new Error("field path must not contain embedded null characters; code 16419");
|
|
|
+ if (prefixedField[0] !== "$") throw new Error("field path references must be prefixed with a '$' ('" + prefixedField + "'); code 15982");
|
|
|
+ return prefixedField.substr(1);
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* Parse an Object. The object could represent a functional expression or a Document expression.
|
|
|
*
|
|
|
@@ -98,14 +118,15 @@ var KIND_UNKNOWN = 0,
|
|
|
* @method parseObject
|
|
|
* @param obj the element representing the object
|
|
|
* @param ctx a MiniCtx representing the options above
|
|
|
+ * @param vps Variables Parse State
|
|
|
* @returns the parsed Expression
|
|
|
**/
|
|
|
klass.parseObject = function parseObject(obj, ctx, vps) {
|
|
|
- if (!(ctx instanceof ObjectCtx)) throw new Error("ctx must be ObjectCtx");
|
|
|
+ if (!(ctx instanceof ObjectCtx)) throw new Error("ctx must be ObjectCtx"); //NOTE: DEVIATION FROM MONGO: This check is not in the c++ code
|
|
|
var kind = KIND_UNKNOWN,
|
|
|
pExpression, // the result
|
|
|
pExpressionObject; // the alt result
|
|
|
- if (obj === undefined || obj == {}) return new ObjectExpression();
|
|
|
+ if (obj === undefined || obj == {}) return new ObjectExpression(); //NOTE: DEVIATION FROM MONGO: is {} really empty in this context?
|
|
|
var fieldNames = Object.keys(obj);
|
|
|
if (fieldNames.length === 0) { //NOTE: Added this for mongo 2.5 port of document sources. Should reconsider when porting the expressions themselves
|
|
|
return new ObjectExpression();
|
|
|
@@ -115,12 +136,12 @@ klass.parseObject = function parseObject(obj, ctx, vps) {
|
|
|
|
|
|
if (pFieldName[0] === "$") {
|
|
|
if (fieldCount !== 0)
|
|
|
- throw new Error("the operator must be the only field in a pipeline object (at '" + pFieldName + "'.; code 16410");
|
|
|
+ throw new Error("the operator must be the only field in a pipeline object (at '" + pFieldName + "'.; code 15983");
|
|
|
|
|
|
if (ctx.isTopLevel)
|
|
|
throw new Error("$expressions are not allowed at the top-level of $project; code 16404");
|
|
|
kind = KIND_OPERATOR; //we've determined this "object" is an operator expression
|
|
|
- pExpression = Expression.parseExpression(pFieldName, obj[pFieldName], vps);
|
|
|
+ pExpression = Expression.parseExpression(pFieldName, obj[pFieldName], vps); //NOTE: DEVIATION FROM MONGO: c++ code uses 2 arguments. See #parseExpression
|
|
|
} else {
|
|
|
if (kind === KIND_OPERATOR)
|
|
|
throw new Error("this object is already an operator expression, and can't be used as a document expression (at '" + pFieldName + "'.; code 15990");
|
|
|
@@ -130,13 +151,16 @@ klass.parseObject = function parseObject(obj, ctx, vps) {
|
|
|
if (pExpression === undefined) { // if it's our first time, create the document expression
|
|
|
if (!ctx.isDocumentOk)
|
|
|
throw new Error("document not allowed in this context"); // CW TODO error: document not allowed in this context
|
|
|
- pExpression = pExpressionObject = new ObjectExpression(); //check for top level?
|
|
|
+ pExpression = pExpressionObject = new ObjectExpression(); //check for top level? //NOTE: DEVIATION FROM MONGO: the c++ calls createRoot() or create() here.
|
|
|
kind = KIND_NOTOPERATOR; //this "object" is not an operator expression
|
|
|
}
|
|
|
var fieldValue = obj[pFieldName];
|
|
|
switch (typeof(fieldValue)) {
|
|
|
case "object":
|
|
|
// it's a nested document
|
|
|
+ //NOTE: DEVIATION FROM MONGO: the behavior here differs from that of the c++ code. The c++ code will
|
|
|
+ // set document_ok or inclusion_ok. This code sets both values leaving ObjectCtx to figure out
|
|
|
+ // which to use.
|
|
|
var subCtx = new ObjectCtx({
|
|
|
isDocumentOk: ctx.isDocumentOk,
|
|
|
isInclusionOk: ctx.isInclusionOk
|
|
|
@@ -145,7 +169,7 @@ klass.parseObject = function parseObject(obj, ctx, vps) {
|
|
|
break;
|
|
|
case "string":
|
|
|
// it's a renamed field // CW TODO could also be a constant
|
|
|
- var pathExpr = new FieldPathExpression.parse(fieldValue);
|
|
|
+ var pathExpr = new FieldPathExpression.parse(fieldValue, vps);
|
|
|
pExpressionObject.addField(pFieldName, pathExpr);
|
|
|
break;
|
|
|
case "boolean":
|
|
|
@@ -156,13 +180,13 @@ klass.parseObject = function parseObject(obj, ctx, vps) {
|
|
|
throw new Error("field inclusion is not allowed inside of $expressions; code 16420");
|
|
|
pExpressionObject.includePath(pFieldName);
|
|
|
} else {
|
|
|
- if (!(ctx.isTopLevel && fn == Document.ID_PROPERTY_NAME))
|
|
|
+ if (!(ctx.isTopLevel && fn === Document.ID_PROPERTY_NAME))
|
|
|
throw new Error("The top-level " + Document.ID_PROPERTY_NAME + " field is the only field currently supported for exclusion; code 16406");
|
|
|
pExpressionObject.excludeId = true;
|
|
|
}
|
|
|
break;
|
|
|
default:
|
|
|
- throw new Error("disallowed field type " + (fieldValue ? fieldValue.constructor.name + ":" : "") + typeof(fieldValue) + " in object expression (at '" + pFieldName + "')");
|
|
|
+ throw new Error("disallowed field type " + (fieldValue ? fieldValue.constructor.name + ":" : "") + typeof(fieldValue) + " in object expression (at '" + pFieldName + "') code 15992");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -172,12 +196,17 @@ klass.parseObject = function parseObject(obj, ctx, vps) {
|
|
|
|
|
|
klass.expressionParserMap = {};
|
|
|
|
|
|
+/** Registers an ExpressionParser so it can be called from parseExpression and friends.
|
|
|
+ *
|
|
|
+ * As an example, if your expression looks like {"$foo": [1,2,3]} you would add this line:
|
|
|
+ * REGISTER_EXPRESSION("$foo", ExpressionFoo::parse);
|
|
|
+ */
|
|
|
klass.registerExpression = function registerExpression(key, parserFunc) {
|
|
|
if (key in klass.expressionParserMap) {
|
|
|
- throw new Error("Duplicate expression registrarion for " + key);
|
|
|
+ throw new Error("Duplicate expression registration for " + key + "; code 17064");
|
|
|
}
|
|
|
klass.expressionParserMap[key] = parserFunc;
|
|
|
- return 0; // Should
|
|
|
+ return 1; //NOTE: DEVIATION FROM MONGO: Mongo returns Status::OK()/
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -189,13 +218,18 @@ klass.registerExpression = function registerExpression(key, parserFunc) {
|
|
|
* @param obj the BSONElement to parse
|
|
|
* @returns the parsed Expression
|
|
|
**/
|
|
|
+//NOTE: DEVIATION FROM MONGO: the c++ version has 2 arguments, not 3.
|
|
|
klass.parseExpression = function parseExpression(exprKey, exprValue, vps) {
|
|
|
if (!(exprKey in Expression.expressionParserMap)) {
|
|
|
- throw new Error("Invalid operator : " + exprKey);
|
|
|
+ throw new Error("Invalid operator : " + exprKey + "; code 15999");
|
|
|
}
|
|
|
return Expression.expressionParserMap[exprKey](exprValue, vps);
|
|
|
};
|
|
|
|
|
|
+//NOTE: DEVIATION FROM MONGO: The c++ code includes a method here called ExpressionNary::parseArguments(...)
|
|
|
+// This is missing from the code you're currently looking at. I am not sure it is a bad thing as it seems
|
|
|
+// like it should be in the NaryExpression class.
|
|
|
+
|
|
|
/**
|
|
|
* Parse a BSONElement which is an operand in an Expression.
|
|
|
*
|
|
|
@@ -207,29 +241,13 @@ klass.parseOperand = function parseOperand(exprElement, vps) {
|
|
|
var t = typeof(exprElement);
|
|
|
if (t === "string" && exprElement[0] == "$") { //if we got here, this is a field path expression
|
|
|
return new FieldPathExpression.parse(exprElement, vps);
|
|
|
- } else
|
|
|
- if (t === "object" && exprElement && exprElement.constructor === Object)
|
|
|
+ } else if (t === "object" && exprElement && exprElement.constructor === Object)
|
|
|
return Expression.parseObject(exprElement, new ObjectCtx({
|
|
|
isDocumentOk: true
|
|
|
}), vps);
|
|
|
else return ConstantExpression.parse(exprElement, vps);
|
|
|
};
|
|
|
|
|
|
-/**
|
|
|
- * Produce a field path string with the field prefix removed.
|
|
|
- * Throws an error if the field prefix is not present.
|
|
|
- *
|
|
|
- * @static
|
|
|
- * @param prefixedField the prefixed field
|
|
|
- * @returns the field path with the prefix removed
|
|
|
- **/
|
|
|
-klass.removeFieldPrefix = function removeFieldPrefix(prefixedField) {
|
|
|
- if (prefixedField.indexOf("\0") != -1) throw new Error("field path must not contain embedded null characters; code 16419");
|
|
|
- if (prefixedField[0] !== "$") throw new Error("field path references must be prefixed with a '$' ('" + prefixedField + "'); code 15982");
|
|
|
- return prefixedField.substr(1);
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
// PROTOTYPE MEMBERS
|
|
|
/**
|
|
|
* Evaluate the Expression using the given document as input.
|