|
|
|
@ -12,6 +12,7 @@ var dateToJSON = function () { return { $$date: this.getTime() }; } |
|
|
|
|
, lastStepModifierFunctions = {} |
|
|
|
|
, comparisonFunctions = {} |
|
|
|
|
, logicalOperators = {} |
|
|
|
|
, queryOperators = {} |
|
|
|
|
; |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -439,6 +440,12 @@ function getDotValue (obj, field) { |
|
|
|
|
|
|
|
|
|
if (fieldParts.length === 1) { |
|
|
|
|
return obj[fieldParts[0]]; |
|
|
|
|
} else if (util.isArray(obj[fieldParts[0]])) { |
|
|
|
|
var objs = new Array(); |
|
|
|
|
for (var i = 0; i < obj[fieldParts[0]].length; i += 1) { |
|
|
|
|
objs = objs.concat(getDotValue(obj[fieldParts[0]][i], fieldParts.slice(1).join('.'))); |
|
|
|
|
} |
|
|
|
|
return objs; |
|
|
|
|
} else { |
|
|
|
|
return getDotValue(obj[fieldParts[0]], fieldParts.slice(1)); |
|
|
|
|
} |
|
|
|
@ -566,7 +573,6 @@ comparisonFunctions.$exists = function (value, exists) { |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Match any of the subqueries |
|
|
|
|
* @param {Model} obj |
|
|
|
@ -613,6 +619,17 @@ logicalOperators.$not = function (obj, query) { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* |
|
|
|
|
* @param obj |
|
|
|
|
* @param value |
|
|
|
|
* @returns {*|boolean} |
|
|
|
|
*/ |
|
|
|
|
queryOperators.$size = function (obj, value) { |
|
|
|
|
return (util.isArray(obj) && obj.length == value); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Tell if a given document matches a query
|
|
|
|
|
* @param {Object} obj Document to check |
|
|
|
@ -650,48 +667,56 @@ function match (obj, query) { |
|
|
|
|
* Match an object against a specific { key: value } part of a query |
|
|
|
|
*/ |
|
|
|
|
function matchQueryPart (obj, queryKey, queryValue) { |
|
|
|
|
var objValue = getDotValue(obj, queryKey) |
|
|
|
|
, i, keys, firstChars, dollarFirstChars; |
|
|
|
|
|
|
|
|
|
// Check if the object value is an array treat it as an array of { obj, query }
|
|
|
|
|
// Where there needs to be at least one match
|
|
|
|
|
if (util.isArray(objValue)) { |
|
|
|
|
for (i = 0; i < objValue.length; i += 1) { |
|
|
|
|
if (matchQueryPart({ k: objValue[i] }, 'k', queryValue)) { return true; } // k here could be any string
|
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
var objValue = getDotValue(obj, queryKey) |
|
|
|
|
, i, keys, firstChars, dollarFirstChars; |
|
|
|
|
|
|
|
|
|
// queryValue is an actual object. Determine whether it contains comparison operators
|
|
|
|
|
// or only normal fields. Mixed objects are not allowed
|
|
|
|
|
if (queryValue !== null && typeof queryValue === 'object' && !util.isRegExp(queryValue)) { |
|
|
|
|
keys = Object.keys(queryValue); |
|
|
|
|
firstChars = _.map(keys, function (item) { return item[0]; }); |
|
|
|
|
dollarFirstChars = _.filter(firstChars, function (c) { return c === '$'; }); |
|
|
|
|
|
|
|
|
|
if (dollarFirstChars.length !== 0 && dollarFirstChars.length !== firstChars.length) { |
|
|
|
|
throw "You cannot mix operators and normal fields"; |
|
|
|
|
// Check if the object value is an array treat it as an array of { obj, query }
|
|
|
|
|
// Where there needs to be at least one match
|
|
|
|
|
if (util.isArray(objValue)) { |
|
|
|
|
if (dollarFirstChars.length > 0) { |
|
|
|
|
if (!queryOperators[keys[0]]) { throw "Unknown query operator " + keys[i]; } |
|
|
|
|
|
|
|
|
|
if (!queryOperators[keys[0]](objValue, queryValue[keys[0]])) { return false; } |
|
|
|
|
return true; |
|
|
|
|
} else { |
|
|
|
|
for (i = 0; i < objValue.length; i += 1) { |
|
|
|
|
if (matchQueryPart({ k: objValue[i] }, 'k', queryValue)) { return true; } // k here could be any string
|
|
|
|
|
} |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// queryValue is an object of this form: { $comparisonOperator1: value1, ... }
|
|
|
|
|
if (dollarFirstChars.length > 0) { |
|
|
|
|
for (i = 0; i < keys.length; i += 1) { |
|
|
|
|
if (!comparisonFunctions[keys[i]]) { throw "Unknown comparison function " + keys[i]; } |
|
|
|
|
// queryValue is an actual object. Determine whether it contains comparison operators
|
|
|
|
|
// or only normal fields. Mixed objects are not allowed
|
|
|
|
|
if (queryValue !== null && typeof queryValue === 'object' && !util.isRegExp(queryValue)) { |
|
|
|
|
|
|
|
|
|
if (!comparisonFunctions[keys[i]](objValue, queryValue[keys[i]])) { return false; } |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
if (dollarFirstChars.length !== 0 && dollarFirstChars.length !== firstChars.length) { |
|
|
|
|
throw "You cannot mix operators and normal fields"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// queryValue is an object of this form: { $comparisonOperator1: value1, ... }
|
|
|
|
|
if (dollarFirstChars.length > 0) { |
|
|
|
|
for (i = 0; i < keys.length; i += 1) { |
|
|
|
|
if (!comparisonFunctions[keys[i]]) { throw "Unknown comparison function " + keys[i]; } |
|
|
|
|
|
|
|
|
|
if (!comparisonFunctions[keys[i]](objValue, queryValue[keys[i]])) { return false; } |
|
|
|
|
} |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Using regular expressions with basic querying
|
|
|
|
|
if (util.isRegExp(queryValue)) { return comparisonFunctions.$regex(objValue, queryValue); } |
|
|
|
|
// Using regular expressions with basic querying
|
|
|
|
|
if (util.isRegExp(queryValue)) { return comparisonFunctions.$regex(objValue, queryValue); } |
|
|
|
|
|
|
|
|
|
// queryValue is either a native value or a normal object
|
|
|
|
|
// Basic matching is possible
|
|
|
|
|
if (!areThingsEqual(objValue, queryValue)) { return false; } |
|
|
|
|
// queryValue is either a native value or a normal object
|
|
|
|
|
// Basic matching is possible
|
|
|
|
|
if (!areThingsEqual(objValue, queryValue)) { return false; } |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|