diff --git a/lib/model.js b/lib/model.js index e336c17..e6899b4 100644 --- a/lib/model.js +++ b/lib/model.js @@ -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; }