Source: probe.js

(function() {
  /**
    @fileOverview Queries objects in memory using a mongo-like notation for reaching into objects and filtering for records

    @module ink/probe
    @author Terry Weiss
    @license MIT
    @mixing
*/;
  /**
These operators manage updates (this is not a real namespace, but a document artifact to organize the symbols)
@namespace updateOperators
@memberof module:ink/probe
**/;
  /**
Query operators  (this is not a real namespace, but a document artifact to organize the symbols)
@namespace queryOperators
@memberof module:ink/probe
**/;
  var bindables, donothing, execQuery, nestedOps, operations, parseQueryExpression, prefixOps, processExpressionObject, processNestedOperator, processPrefixOperator, pushin, reachin, splitPath, sys;

  sys = require("lodash");

  /**
    The list of operators that are nested within the expression object. These take the form <code>{path:{operator:operand}}</code>
    @private
**/;

  nestedOps = ["$eq", "$gt", "$gte", "$in", "$lt", "$lte", "$ne", "$nin", "$exists", "$mod", "$size", "$all"];

  /**
    The list of operators that prefix the expression object. These take the form <code>{operator:{operands}}</code> or <code>{operator: [operands]}</code>
    @private
**/;

  prefixOps = ["$and", "$or", "$nor", "$not"];

  /**
    Processes a nested operator by picking the operator out of the expression object. Returns a formatted object that can be used for querying
    @private
    @param {string} path The path to element to work with
    @param {object} operand The operands to use for the query
    @return {object} A formatted operation definition
**/;

  processNestedOperator = function(path, operand) {
    var opKeys;

    opKeys = Object.keys(operand);
    return {
      operation: opKeys[0],
      operands: [operand[opKeys[0]]],
      path: path
    };
  };

  /**
    Processes a prefixed operator and then passes control to the nested operator method to pick out the contained values
    @private
    @param {string} operation The operation prefix
    @param {object} operand The operands to use for the query
    @return {object} A formatted operation definition
**/;

  processPrefixOperator = function(operation, operand) {
    var component;

    component = {
      operation: operation,
      path: null,
      operands: []
    };
    if (sys.isArray(operand)) {
      sys.each(operand, function(obj) {
        return sys.each(obj, function(val, key) {
          return component.operands.push(processExpressionObject(val, key));
        });
      });
    } else {
      sys.each(operand, function(val, key) {
        return component.operands.push(processExpressionObject(val, key));
      });
    }
    return component;
  };

  /**
    Interogates a single query expression object and calls the appropriate handler for its contents
    @private
    @param {object} val The expression
    @param {object} key The prefix
    @returns {object} A formatted operation definition
**/;

  processExpressionObject = function(val, key) {
    var op, opKeys, operator;

    if (sys.isObject(val)) {
      opKeys = Object.keys(val);
      op = opKeys[0];
      if (sys.indexOf(nestedOps, op) > -1) {
        operator = processNestedOperator(key, val);
      } else if (sys.indexOf(prefixOps, key) > -1) {
        operator = processPrefixOperator(key, val);
      } else if (op === "$regex") {
        operator = processNestedOperator(key, val);
      } else if (op === "$elemMatch") {
        operator = {
          path: key,
          operation: op,
          operands: []
        };
        sys.each(val[op], function(entry) {
          return operator.operands = parseQueryExpression(entry);
        });
      } else {
        throw new Error("Unrecognized operator");
      }
    } else {
      operator = processNestedOperator(key, {
        $eq: val
      });
    }
    return operator;
  };

  /**
    Parses a query request and builds an object that can used to process a query target
    @private
    @param {object} obj The expression object
    @returns {object} All components of the expression in a kind of execution tree
**/;

  parseQueryExpression = function(obj) {
    var arr, payload;

    if (sys.size(obj) > 1) {
      arr = sys.map(obj, function(v, k) {
        var entry;

        entry = {};
        entry[k] = v;
        return entry;
      });
      obj = {
        $and: arr
      };
    }
    payload = [];
    sys.each(obj, function(val, key) {
      var exprObj;

      exprObj = processExpressionObject(val, key);
      if (exprObj.operation === "$regex") {
        exprObj.options = val["$options"];
      }
      return payload.push(exprObj);
    });
    return payload;
  };

  /**
    Does what it says
    @private
**/;

  donothing = function() {};

  /**
The delimiter to use when splitting an expression
@type {string}
@default '.'
**/;

  exports.delimiter = '.';

  /**
    Splits a path expression into its component parts
    @private
    @param {string} path The path to split
    @returns {array}
**/;

  splitPath = function(path) {
    return path.split(exports.delimiter);
  };

  /**
    Reaches into an object and allows you to get at a value deeply nested in an object
    @private
    @param {array} path The split path of the element to work with
    @param {object} record The record to reach into
    @return {*} Whatever was found in the record
**/;

  reachin = function(path, record) {
    var context, part, _i, _len;

    context = record;
    for (_i = 0, _len = path.length; _i < _len; _i++) {
      part = path[_i];
      context = context[part];
      if (sys.isNull(context) || sys.isUndefined(context)) {
        break;
      }
    }
    return context;
  };

  /**
  This will write the value into a record at the path, creating intervening objects if they don't exist
  @private
  @param {array} path The split path of the element to work with
  @param {object} record The record to reach into
  @param {object} newValue The value to write to the, or if the operator is $pull, the query of items to look for
*/;

  pushin = function(path, record, setter, newValue) {
    var context, keys, lastPart, parent, part, _i, _len;

    context = record;
    parent = record;
    lastPart = null;
    for (_i = 0, _len = path.length; _i < _len; _i++) {
      part = path[_i];
      lastPart = part;
      parent = context;
      context = context[part];
      if (sys.isNull(context) || sys.isUndefined(context)) {
        parent[part] = {};
        context = parent[part];
      }
    }
    if (sys.isEmpty(setter) || setter === '$set') {
      return parent[lastPart] = newValue;
    } else {
      switch (setter) {
        case '$inc':
          /**
          Increments a field by the amount you specify. It takes the form
              `{ $inc: { field1: amount } }`
          @name $inc
          @memberOf module:ink/probe.updateOperators
          @example
          var probe = require("ink-probe");
          probe.update( obj, {'name.last' : 'Owen', 'name.first' : 'LeRoy'},
            {$inc : {'password.changes' : 2}} );
          */

          if (!sys.isNumber(newValue)) {
            newValue = 1;
          }
          if (sys.isNumber(parent[lastPart])) {
            return parent[lastPart] = parent[lastPart] + newValue;
          }
          break;
        case '$dec':
          /**
          Decrements a field by the amount you specify. It takes the form
              `{ $dec: { field1: amount }`
          @name $dec
          @memberOf module:ink/probe.updateOperators
          @example
          var probe = require("ink-probe");
          probe.update( obj, {'name.last' : 'Owen', 'name.first' : 'LeRoy'},
            {$dec : {'password.changes' : 2}} );
          */

          if (!sys.isNumber(newValue)) {
            newValue = 1;
          }
          if (sys.isNumber(parent[lastPart])) {
            return parent[lastPart] = parent[lastPart] - newValue;
          }
          break;
        case '$unset':
          /**
          Removes the field from the object. It takes the form
              `{ $unset: { field1: "" } }`
          @name $unset
          @memberOf module:ink/probe.updateOperators
          @example
          var probe = require("ink-probe");
          probe.update( data, {'name.first' : 'Yogi'}, {$unset : {'name.first' : ''}} );
          */

          return delete parent[lastPart];
        case '$pop':
          /**
          The $pop operator removes the first or last element of an array. Pass $pop a value of 1 to remove the last element
          in an array and a value of -1 to remove the first element of an array. This will only work on arrays. Syntax:
            `{ $pop: { field: 1 } }` or `{ $pop: { field: -1 } }`
          @name $pop
          @memberOf module:ink/probe.updateOperators
          @example
          var probe = require("ink-probe");
          // attr is the name of the array field
          probe.update( data, {_id : '511d18827da2b88b09000133'}, {$pop : {attr : 1}} );
          */

          if (sys.isArray(parent[lastPart])) {
            if (!sys.isNumber(newValue)) {
              newValue = 1;
            }
            if (newValue === 1) {
              return parent[lastPart].pop();
            } else {
              return parent[lastPart].shift();
            }
          }
          break;
        case '$push':
          /**
          The $push operator appends a specified value to an array. It looks like this:
            `{ $push: { <field>: <value> } }`
          @name $push
          @memberOf module:ink/probe.updateOperators
          @example
          var probe = require("ink-probe");
          // attr is the name of the array field
          probe.update( data, {_id : '511d18827da2b88b09000133'},
              {$push : {attr : {"hand" : "new", "color" : "new"}}} );
          */

          if (sys.isArray(parent[lastPart])) {
            return parent[lastPart].push(newValue);
          }
          break;
        case '$pull':
          /**
          The $pull operator removes all instances of a value from an existing array. It looks like this:
          `{ $pull: { field: <query> } }`
          @name $pull
          @memberOf module:ink/probe.updateOperators
          @example
          var probe = require("ink-probe");
          // attr is the name of the array field
          probe.update( data, {'email' : 'EWallace.43@fauxprisons.com'},
            {$pull : {attr : {"color" : "green"}}} );
          */

          if (sys.isArray(parent[lastPart])) {
            keys = exports.findKeys(parent[lastPart], newValue);
            sys.each(keys, function(val, index) {
              return delete parent[lastPart][index];
            });
            return parent[lastPart] = sys.compact(parent[lastPart]);
          }
      }
    }
  };

  /**
    The query operations that evaluate directly from an operation
    @private
**/;

  operations = {
    /**
    `$eq` performs a `===` comparison by comparing the value directly if it is an atomic value.
    otherwise if it is an array, it checks to see if the value looked for is in the array.
    `{field: value}` or `{field: {$eq : value}}` or `{array: value}` or `{array: {$eq : value}}`
    @name $eq
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {categories : "cat1"} );
    // is the same as
    probe.find( data, {categories : {$eq: "cat1"}} );
    */

    $eq: function(qu, value) {
      if (sys.isArray(value)) {
        return sys.find(value, function(entry) {
          return JSON.stringify(qu.operands[0]) === JSON.stringify(entry);
        }) !== void 0;
      } else {
        return JSON.stringify(qu.operands[0]) === JSON.stringify(value);
      }
    },
    /**
    `$ne` performs a `!==` comparison by comparing the value directly if it is an atomic value. Otherwise, if it is an array
    this is performs a "not in array".
    '{field: {$ne : value}}` or '{array: {$ne : value}}`
    @name $ne
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"name.first" : {$ne : "Sheryl"}} );
    */

    $ne: function(qu, value) {
      if (sys.isArray(value)) {
        return sys.find(value, function(entry) {
          return JSON.stringify(qu.operands[0]) !== JSON.stringify(entry);
        }) !== void 0;
      } else {
        return JSON.stringify(qu.operands[0]) !== JSON.stringify(value);
      }
    },
    /**
    `$all` checks to see if all of the members of the query are included in an array
    `{array: {$all: [val1, val2, val3]}}`
    @name $all
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"categories" : {$all : ["cat4", "cat2", "cat1"]}} );
    */

    $all: function(qu, value) {
      var operands, result;

      result = false;
      if (sys.isArray(value)) {
        operands = sys.flatten(qu.operands);
        result = sys.intersection(operands, value).length === operands.length;
      }
      return result;
    },
    /**
    `$gt` Sees if a field is greater than the value
    `{field: {$gt: value}}`
    @name $gt
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"age" : {$gt : 24}} );
    */

    $gt: function(qu, value) {
      return qu.operands[0] < value;
    },
    /**
    `$gte` Sees if a field is greater than or equal to the value
    `{field: {$gte: value}}`
    @name $gte
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"age" : {$gte : 50}} );
    */

    $gte: function(qu, value) {
      return qu.operands[0] <= value;
    },
    /**
    `$lt` Sees if a field is less than the value
    `{field: {$lt: value}}`
    @name $lt
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"age" : {$lt : 24}} );
    */

    $lt: function(qu, value) {
      return qu.operands[0] > value;
    },
    /**
    `$lte` Sees if a field is less than or equal to the value
    `{field: {$lte: value}}`
    @name $lte
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"age" : {$lte : 50}} );
    */

    $lte: function(qu, value) {
      return qu.operands[0] >= value;
    },
    /**
    `$in` Sees if a field has one of the values in the query
    `{field: {$in: [test1, test2, test3,...]}}`
    @name $in
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"age" : {$in : [24, 28, 60]}} );
    */

    $in: function(qu, value) {
      var operands;

      operands = sys.flatten(qu.operands);
      return sys.indexOf(operands, value) > -1;
    },
    /**
    `$nin` Sees if a field has none of the values in the query
    `{field: {$nin: [test1, test2, test3,...]}}`
    @name $nin
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"age" : {$nin : [24, 28, 60]}} );
    */

    $nin: function(qu, value) {
      var operands;

      operands = sys.flatten(qu.operands);
      return sys.indexOf(operands, value) === -1;
    },
    /**
    `$exists` Sees if a field exists.
    `{field: {$exists: true|false}}`
    @name $exists
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"name.middle" : {$exists : true}} );
    */

    $exists: function(qu, value) {
      return !((sys.isNull(value) || sys.isUndefined(value)) === qu.operands[0]);
    },
    /**
    Checks equality to a modulus operation on a field
    `{field: {$mod: [divisor, remainder]}}`
    @name $mod
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"age" : {$mod : [2, 0]}} );
    */

    $mod: function(qu, value) {
      var mod, operands, rem;

      operands = sys.flatten(qu.operands);
      if (operands.length !== 2) {
        throw new Error("$mod requires two operands");
      }
      mod = operands[0];
      rem = operands[1];
      return value % mod === rem;
    },
    /**
    Compares the size of the field/array to the query. This can be used on arrays, strings and objects (where it will count keys)
    `{'field|array`: {$size: value}}`
    @name $size
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {attr : {$size : 3}} );
    */

    $size: function(qu, value) {
      return sys.size(value) === qu.operands[0];
    },
    /**
    Performs a regular expression test againts the field
    `{field: {$regex: re, $options: reOptions}}`
    @name $regex
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {"name.first" : {$regex : "m*", $options : "i"}} );
    */

    $regex: function(qu, value) {
      var r;

      r = new RegExp(qu.operands[0], qu.options);
      return r.test(value);
    },
    /**
    This is like $all except that it works with an array of objects or value. It checks to see the array matches all
    of the conditions of the query
    `{array: {$elemMatch: {path: value, path: {$operation: value2}}}`
    @name $elemMatch
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {attr : {$elemMatch : [
    		{color : "red", "hand" : "left"}
    	]}} );
    */

    $elemMatch: function(qu, value) {
      var expression, test, _i, _len, _ref;

      if (sys.isArray(value)) {
        _ref = qu.operands;
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          expression = _ref[_i];
          if (expression.path) {
            expression.splitPath = splitPath(expression.path);
          }
        }
        test = execQuery(value, qu.operands, null, true).arrayResults;
      }
      return test.length > 0;
    },
    /**
    Returns true if all of the conditions of the query are met
    `{$and: [query1, query2, query3]}`
    @name $and
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {$and : [
    		{"name.first" : "Mildred"},
    		{"name.last" : "Graves"}
    	]} );
    */

    $and: function(qu, value, record) {
      var expr, isAnd, test, _i, _len, _ref, _ref1;

      isAnd = false;
      _ref = qu.operands;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        expr = _ref[_i];
        if (expr.path) {
          if ((_ref1 = expr.splitPath) == null) {
            expr.splitPath = splitPath(expr.path);
          }
        }
        test = reachin(expr.splitPath, record, expr.operation);
        isAnd = operations[expr.operation](expr, test, record);
        if (!isAnd) {
          break;
        }
      }
      return isAnd;
    },
    /**
    Returns true if any of the conditions of the query are met
    `{$or: [query1, query2, query3]}`
    @name $or
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {$or : [
    		{"age" : {$in : [24, 28, 60]}},
    		{categories : "cat1"}
    	]} );
    */

    $or: function(qu, value, record) {
      var expr, isOr, test, _i, _len, _ref, _ref1;

      isOr = false;
      _ref = qu.operands;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        expr = _ref[_i];
        if (expr.path) {
          if ((_ref1 = expr.splitPath) == null) {
            expr.splitPath = splitPath(expr.path);
          }
        }
        test = reachin(expr.splitPath, record, expr.operation);
        isOr = operations[expr.operation](expr, test, record);
        if (isOr) {
          break;
        }
      }
      return isOr;
    },
    /**
    Returns true if none of the conditions of the query are met
    `{$nor: [query1, query2, query3]}`
    @name $nor
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {$nor : [
    		{"age" : {$in : [24, 28, 60]}},
    		{categories : "cat1"}
    	]} );
    */

    $nor: function(qu, value, record) {
      var expr, isOr, test, _i, _len, _ref, _ref1;

      isOr = false;
      _ref = qu.operands;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        expr = _ref[_i];
        if (expr.path) {
          if ((_ref1 = expr.splitPath) == null) {
            expr.splitPath = splitPath(expr.path);
          }
        }
        test = reachin(expr.splitPath, record, expr.operation);
        isOr = operations[expr.operation](expr, test, record);
        if (isOr) {
          break;
        }
      }
      return !isOr;
    },
    /**
    Logical NOT on the conditions of the query
    `{$not: [query1, query2, query3]}`
    @name $not
    @memberOf module:ink/probe.queryOperators
    @example
    var probe = require("ink-probe");
    probe.find( data, {$not : {"age" : {$lt : 24}}} );
    */

    $not: function(qu, value, record) {
      var expr, result, test, _i, _len, _ref, _ref1;

      result = false;
      _ref = qu.operands;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        expr = _ref[_i];
        if (expr.path) {
          if ((_ref1 = expr.splitPath) == null) {
            expr.splitPath = splitPath(expr.path);
          }
        }
        test = reachin(expr.splitPath, record, expr.operation);
        result = operations[expr.operation](expr, test, record);
        if (result) {
          break;
        }
      }
      return !result;
    }
  };

  /**
    Executes a query by traversing a document and evaluating each record
    @private
    @param {array|object} obj The object to query
    @param {object} qu The query to execute
    @param {boolean} shortCircuit When true, the condition that matches the query stops evaluation for that record, otherwise all conditions have to be met
    @param {boolean} stopOnFirst When true all evaluation stops after the first record is found to match the conditons
**/;

  execQuery = function(obj, qu, shortCircuit, stopOnFirst) {
    var arrayResults, keyResults;

    arrayResults = [];
    keyResults = [];
    sys.each(obj, function(record, key) {
      var expr, result, test, _i, _len;

      for (_i = 0, _len = qu.length; _i < _len; _i++) {
        expr = qu[_i];
        if (expr.splitPath) {
          test = reachin(expr.splitPath, record, expr.operation);
        }
        result = operations[expr.operation](expr, test, record);
        if (result) {
          arrayResults.push(record);
          keyResults.push(key);
        }
        if (!result && shortCircuit) {
          break;
        }
      }
      if (arrayResults.length > 0 && stopOnFirst) {
        return false;
      }
    });
    return {
      arrayResults: arrayResults,
      keyResults: keyResults
    };
  };

  /**
Updates all records in obj that match the query. See {@link module:ink/probe.updateOperators} for the operators that are supported.
@param {object|array} obj The object to update
@param {object} qu The query which will be used to identify the records to updated
@param {object} setDocument The update operator. See {@link module:ink/probe.updateOperators}
 */;

  exports.update = function(obj, qu, setDocument) {
    var records;

    records = exports.find(obj, qu);
    return sys.each(records, function(record) {
      return sys.each(setDocument, function(fields, operator) {
        return sys.each(fields, function(newValue, path) {
          return pushin(splitPath(path), record, operator, newValue);
        });
      });
    });
  };

  /**
    Find all records that match a query
    @param {array|object} obj The object to query
    @param {object} qu The query to execute. See {@link module:ink/probe.queryOperators} for the operators you can use.
    @returns {array} The results
**/;

  exports.find = function(obj, qu) {
    var expression, query, _i, _len;

    query = parseQueryExpression(qu);
    for (_i = 0, _len = query.length; _i < _len; _i++) {
      expression = query[_i];
      if (expression.path) {
        expression.splitPath = splitPath(expression.path);
      }
    }
    return execQuery(obj, query).arrayResults;
  };

  /**
      Find all records that match a query and returns the keys for those items. This is similar to {@link module:ink/probe.find} but instead of returning
      records, returns the keys. If `obj` is an object it will return the hash key. If 'obj' is an array, it will return the index
      @param {array|object} obj The object to query
      @param {object} qu The query to execute. See {@link module:ink/probe.queryOperators} for the operators you can use.
      @returns {array}
  */


  exports.findKeys = function(obj, qu) {
    var expression, query, _i, _len;

    query = parseQueryExpression(qu);
    for (_i = 0, _len = query.length; _i < _len; _i++) {
      expression = query[_i];
      if (expression.path) {
        expression.splitPath = splitPath(expression.path);
      }
    }
    return execQuery(obj, query).keyResults;
  };

  /**
      Returns the first record that matches the query. Aliased as `seek`.
      @param {array|object} obj The object to query
      @param {object} qu The query to execute. See {@link module:ink/probe.queryOperators} for the operators you can use.
      @returns {object}
  */


  exports.findOne = function(obj, qu) {
    var expression, query, results, _i, _len;

    query = parseQueryExpression(qu);
    for (_i = 0, _len = query.length; _i < _len; _i++) {
      expression = query[_i];
      if (expression.path) {
        expression.splitPath = splitPath(expression.path);
      }
    }
    results = execQuery(obj, query, false, true).arrayResults;
    if (results.length > 0) {
      return results[0];
    } else {
      return null;
    }
  };

  /**
      Returns the first record that matches the query and returns its key or index depending on whether `obj` is an object or array respectively.
      Aliased as `seekKey`.
      @param {array|object} obj The object to query
      @param {object} qu The query to execute. See {@link module:ink/probe.queryOperators} for the operators you can use.
      @returns {object}
  */


  exports.findOneKey = function(obj, qu) {
    var expression, query, results, _i, _len;

    query = parseQueryExpression(qu);
    for (_i = 0, _len = query.length; _i < _len; _i++) {
      expression = query[_i];
      if (expression.path) {
        expression.splitPath = splitPath(expression.path);
      }
    }
    results = execQuery(obj, query, false, true).keyResults;
    if (results.length > 0) {
      return results[0];
    } else {
      return null;
    }
  };

  /**
    Remove all items in the object/array that match the query
    @param {array|object} obj The object to query
    @param {object} qu The query to execute. See {@link module:ink/probe.queryOperators} for the operators you can use.
    @return {object|array} The array or object as appropriate without the records.
**/;

  exports.remove = function(obj, qu) {
    var expression, newArr, query, results, _i, _len;

    query = parseQueryExpression(qu);
    for (_i = 0, _len = query.length; _i < _len; _i++) {
      expression = query[_i];
      if (expression.path) {
        expression.splitPath = splitPath(expression.path);
      }
    }
    results = execQuery(obj, query, false, false).keyResults;
    if (sys.isArray(obj)) {
      newArr = [];
      sys.each(obj, function(item, index) {
        if (sys.indexOf(results, index) === -1) {
          return newArr.push(item);
        }
      });
      return newArr;
    } else {
      sys.each(results, function(key) {
        return delete obj[key];
      });
      return obj;
    }
  };

  /**
    Returns true if all items match the query

    @param {array|object} obj The object to query
    @param {object} qu The query to execute. See {@link module:ink/probe.queryOperators} for the operators you can use.
    @returns {boolean}
**/;

  exports.all = function(obj, qu) {
    return exports.find(obj, qu).length === sys.size(obj);
  };

  /**
    Returns true if any of the items match the query

    @param {array|object} obj The object to query
    @param {object} qu The query to execute. See {@link module:ink/probe.queryOperators} for the operators you can use.
    @returns {boolean}
**/;

  exports.any = function(obj, qu) {
    var expression, query, results, _i, _len;

    query = parseQueryExpression(qu);
    for (_i = 0, _len = query.length; _i < _len; _i++) {
      expression = query[_i];
      if (expression.path) {
        expression.splitPath = splitPath(expression.path);
      }
    }
    results = execQuery(obj, query, true, true).keyResults;
    return results.length > 0;
  };

  /**
Returns the set of unique records that match a query
@param {array|object} obj The object to query
@param {object} qu The query to execute. See {@link module:ink/probe.queryOperators} for the operators you can use.
@return {array}
**/;

  exports.unique = function(obj, qu) {
    var test;

    test = exports.find(obj, qu);
    return sys.unique(test, function(item) {
      return JSON.stringify(item);
    });
  };

  /**
  This will write the value into a record at the path, creating intervening objects if they don't exist. This does not work as filtered
  update and is meant to be used on a single record. It is a nice way of setting a property at an arbitrary depth at will.

  @param {array} path The split path of the element to work with
  @param {object} record The record to reach into
  @param {string} setter The set operation.  See {@link module:ink/probe.updateOperators} for the operators you can use.
  @param {object} newValue The value to write to the, or if the operator is $pull, the query of items to look for
*/;

  exports.set = function(record, path, setter, newValue) {
    return pushin(splitPath(path), record, setter, newValue);
  };

  /**
    Reaches into an object and allows you to get at a value deeply nested in an object. This is not a query, but a
    straight reach in, useful for event bindings

    @param {array} path The split path of the element to work with
    @param {object} record The record to reach into
    @return {*} Whatever was found in the record
**/;

  exports.get = function(record, path) {
    return reachin(splitPath(path), record);
  };

  /**
      Returns true if any of the items match the query. Aliases as `any`
       @function
      @param {array|object} obj The object to query
      @param {object} qu The query to execute
      @returns {boolean}
  */


  exports.some = exports.any;

  /**
      Returns true if all items match the query. Aliases as `all`
      @function
      @param {array|object} obj The object to query
      @param {object} qu The query to execute
      @returns {boolean}
  */


  exports.every = exports.all;

  exports.seek = exports.findOne;

  exports.seekKey = exports.findOneKey;

  bindables = {
    any: exports.any,
    all: exports.all,
    remove: exports.remove,
    seekKey: exports.seekKey,
    seek: exports.seek,
    findOneKey: exports.findOneKey,
    findOne: exports.findOne,
    findKeys: exports.findKeys,
    find: exports.find,
    update: exports.update,
    some: exports.some,
    every: exports.every
  };

  /**
   Binds the query and update methods to a specific object. When called these
   methods can skip the first parameter so that find(object, query) can just be called as find(query)
   @param {object|array} obj The object or array to bind to
   @return {object} An object with method bindings in place
**/;

  exports.bindTo = function(obj) {
    var retVal;

    retVal = {};
    sys.each(bindables, function(val, key) {
      return retVal[key] = sys.bind(val, obj, obj);
    });
    return retVal;
  };

  /**
   Binds the query and update methods to a specific object and adds the methods to that object. When called these
   methods can skip the first parameter so that find(object, query) can just be called as object.find(query)
   @param {object|array} obj The object or array to bind to
   @param {object|array=} collection If the collection is not the same as <code>this</code> but is a property, or even
   a whole other object, you specify that here. Otherwise the <code>obj</code> is assumed to be the same as the collecion
**/;

  exports.mixTo = function(obj, collection) {
    collection = collection || obj;
    return sys.each(bindables, function(val, key) {
      return obj[key] = sys.bind(val, obj, collection);
    });
  };

}).call(this);
ink-probe Copyright © 2012-2013 Terry Weiss. All rights reserved.
Documentation generated by JSDoc 3.2.0-dev on Sun Jun 09 2013 11:55:26 GMT-0400 (EDT) using the DocStrap template.