1319 lines
36 KiB
HTML
Executable File
1319 lines
36 KiB
HTML
Executable File
<!DOCTYPE html>
|
|
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>DocStrap Source: documents/probe.js</title>
|
|
|
|
<!--[if lt IE 9]>
|
|
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
<![endif]-->
|
|
<link type="text/css" rel="stylesheet" href="styles/sunlight.default.css">
|
|
|
|
<link type="text/css" rel="stylesheet" href="styles/site.cosmo.css">
|
|
|
|
</head>
|
|
|
|
<body>
|
|
<div class="container-fluid">
|
|
<div class="navbar navbar-fixed-top navbar-inverse">
|
|
<div class="navbar-inner">
|
|
<a class="brand" href="index.html">DocStrap</a>
|
|
<ul class="nav">
|
|
|
|
<li class="dropdown">
|
|
<a href="modules.list.html" class="dropdown-toggle" data-toggle="dropdown">Modules<b
|
|
class="caret"></b></a>
|
|
|
|
<ul class="dropdown-menu ">
|
|
|
|
<li>
|
|
<a href="module-base.html">base</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="chains_.html">base/chains</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="binder.html">documents/binder</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="model_.html">documents/model</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="probe.html">documents/probe</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="schema_.html">documents/schema</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="collector.html">ink/collector</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="bussable_.html">mixins/bussable</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="signalable_.html">mixins/signalable</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="format.html">strings/format</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="logger.html">utils/logger</a>
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</li>
|
|
|
|
<li class="dropdown">
|
|
<a href="classes.list.html" class="dropdown-toggle" data-toggle="dropdown">Classes<b
|
|
class="caret"></b></a>
|
|
|
|
<ul class="dropdown-menu ">
|
|
|
|
<li>
|
|
<a href="base.html">base</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="chains.html">base/chains</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="model.html">documents/model</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="probe.queryOperators.html">documents/probe.queryOperators</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="probe.updateOperators.html">documents/probe.updateOperators</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="collector-ACollector.html">ink/collector~ACollector</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="collector-CollectorBase_.html">ink/collector~CollectorBase</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="collector-OCollector.html">ink/collector~OCollector</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="signalable-Signal.html">mixins/signalable~Signal</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="logger.Logger.html">utils/logger.Logger</a>
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</li>
|
|
|
|
<li class="dropdown">
|
|
<a href="mixins.list.html" class="dropdown-toggle" data-toggle="dropdown">Mixins<b
|
|
class="caret"></b></a>
|
|
|
|
<ul class="dropdown-menu ">
|
|
|
|
<li>
|
|
<a href="schema.html">documents/schema</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="bussable.html">mixins/bussable</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="signalable.html">mixins/signalable</a>
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</li>
|
|
|
|
<li class="dropdown">
|
|
<a href="tutorials.list.html" class="dropdown-toggle" data-toggle="dropdown">Tutorials<b
|
|
class="caret"></b></a>
|
|
|
|
<ul class="dropdown-menu ">
|
|
|
|
<li>
|
|
<a href="tutorial-Teeth.html">Brush Teeth</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="tutorial-Car.html">Drive Car</a>
|
|
</li>
|
|
|
|
<li>
|
|
<a href="tutorial-Test.html">Fence Test</a>
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</li>
|
|
|
|
<li class="dropdown">
|
|
<a href="global.html" class="dropdown-toggle" data-toggle="dropdown">Global<b
|
|
class="caret"></b></a>
|
|
|
|
<ul class="dropdown-menu ">
|
|
|
|
<li>
|
|
<a href="global.html#utils/logger">utils/logger</a>
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
</li>
|
|
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row-fluid">
|
|
|
|
|
|
<div class="span12">
|
|
|
|
<div id="main">
|
|
|
|
|
|
|
|
<h1 class="page-title">Source: documents/probe.js</h1>
|
|
|
|
<section>
|
|
<article>
|
|
<pre
|
|
class="sunlight-highlight-javascript linenums">"use strict";
|
|
/**
|
|
@fileOverview Queries objects in memory using a mongo-like notation for reaching into objects and filtering for records
|
|
|
|
@module documents/probe
|
|
@author Terry Weiss
|
|
@license MIT
|
|
@requires lodash
|
|
*/
|
|
|
|
var sys = require( "lodash" );
|
|
/**
|
|
The list of operators that are nested within the expression object. These take the form <code>{path:{operator:operand}}</code>
|
|
@private
|
|
@type {array.<string>}
|
|
**/
|
|
var 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
|
|
@type {array.<string>}
|
|
**/
|
|
var 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
|
|
**/
|
|
function processNestedOperator( path, operand ) {
|
|
var opKeys = Object.keys( operand );
|
|
return {
|
|
operation : opKeys[ 0 ],
|
|
operands : [operand[ opKeys[ 0 ] ]],
|
|
path : path
|
|
};
|
|
}
|
|
|
|
/**
|
|
Interrogates 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
|
|
**/
|
|
function processExpressionObject( val, key ) {
|
|
var operator;
|
|
if ( sys.isObject( val ) ) {
|
|
var opKeys = Object.keys( val );
|
|
var 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" ) {
|
|
// special handling for regex options
|
|
operator = processNestedOperator( key, val );
|
|
} else if ( op === "$elemMatch" ) {
|
|
// elemMatch is just a weird duck
|
|
operator = {
|
|
path : key,
|
|
operation : op,
|
|
operands : []
|
|
};
|
|
sys.each( val[ op ], function ( entry ) {
|
|
operator.operands = parseQueryExpression( entry );
|
|
} );
|
|
}
|
|
else {
|
|
throw new Error( "Unrecognized operator" );
|
|
}
|
|
} else {
|
|
operator = processNestedOperator( key, { $eq : val } );
|
|
}
|
|
return operator;
|
|
}
|
|
|
|
/**
|
|
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
|
|
**/
|
|
function processPrefixOperator( operation, operand ) {
|
|
var component = {
|
|
operation : operation,
|
|
path : null,
|
|
operands : []
|
|
};
|
|
|
|
if ( sys.isArray( operand ) ) {
|
|
//if it is an array we need to loop through the array and parse each operand
|
|
//if it is an array we need to loop through the array and parse each operand
|
|
sys.each( operand, function ( obj ) {
|
|
sys.each( obj, function ( val, key ) {
|
|
component.operands.push( processExpressionObject( val, key ) );
|
|
} );
|
|
} );
|
|
} else {
|
|
//otherwise it is an object and we can parse it directly
|
|
sys.each( operand, function ( val, key ) {
|
|
component.operands.push( processExpressionObject( val, key ) );
|
|
} );
|
|
}
|
|
return component;
|
|
|
|
}
|
|
|
|
/**
|
|
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
|
|
**/
|
|
|
|
function parseQueryExpression( obj ) {
|
|
if ( sys.size( obj ) > 1 ) {
|
|
var arr = sys.map( obj, function ( v, k ) {
|
|
var entry = {};
|
|
entry[k] = v;
|
|
return entry;
|
|
} );
|
|
obj = {
|
|
$and : arr
|
|
};
|
|
}
|
|
var payload = [];
|
|
sys.each( obj, function ( val, key ) {
|
|
|
|
var exprObj = processExpressionObject( val, key );
|
|
|
|
if ( exprObj.operation === "$regex" ) {
|
|
exprObj.options = val.$options;
|
|
}
|
|
|
|
payload.push( exprObj );
|
|
} );
|
|
|
|
return payload;
|
|
}
|
|
|
|
/**
|
|
The delimiter to use when splitting an expression
|
|
@type {string}
|
|
@static
|
|
@default '.'
|
|
**/
|
|
|
|
exports.delimiter = '.';
|
|
|
|
/**
|
|
Splits a path expression into its component parts
|
|
@private
|
|
@param {string} path The path to split
|
|
@returns {array}
|
|
**/
|
|
|
|
function splitPath( 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
|
|
**/
|
|
function reachin( path, record ) {
|
|
var context = record;
|
|
var part;
|
|
var _i;
|
|
var _len;
|
|
|
|
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 {string} setter The set command, defaults to $set
|
|
@param {object} newValue The value to write to the, or if the operator is $pull, the query of items to look for
|
|
*/
|
|
function pushin( path, record, setter, newValue ) {
|
|
var context = record;
|
|
var parent = record;
|
|
var lastPart = null;
|
|
var _i;
|
|
var _len;
|
|
var part;
|
|
var keys;
|
|
|
|
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' ) {
|
|
parent[lastPart] = newValue;
|
|
return parent[lastPart];
|
|
} else {
|
|
switch ( setter ) {
|
|
case '$inc':
|
|
/**
|
|
* Increments a field by the amount you specify. It takes the form
|
|
* `{ $inc: { field1: amount } }`
|
|
* @name $inc
|
|
* @memberOf module:documents/probe.updateOperators
|
|
* @example
|
|
* var probe = require("documents/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] ) ) {
|
|
parent[lastPart] = parent[lastPart] + newValue;
|
|
return parent[lastPart];
|
|
}
|
|
break;
|
|
case '$dec':
|
|
/**
|
|
* Decrements a field by the amount you specify. It takes the form
|
|
* `{ $dec: { field1: amount }`
|
|
* @name $dec
|
|
* @memberOf module:documents/probe.updateOperators
|
|
* @example
|
|
* var probe = require("documents/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] ) ) {
|
|
parent[lastPart] = parent[lastPart] - newValue;
|
|
return parent[lastPart];
|
|
}
|
|
break;
|
|
case '$unset':
|
|
/**
|
|
* Removes the field from the object. It takes the form
|
|
* `{ $unset: { field1: "" } }`
|
|
* @name $unset
|
|
* @memberOf module:documents/probe.updateOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.updateOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.updateOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.updateOperators
|
|
* @example
|
|
* var probe = require("documents/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];
|
|
} );
|
|
parent[lastPart] = sys.compact( parent[lastPart] );
|
|
return parent[lastPart];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
The query operations that evaluate directly from an operation
|
|
@private
|
|
**/
|
|
var 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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/probe");
|
|
* probe.find( data, {"age" : {$mod : [2, 0]}} );
|
|
*/
|
|
|
|
$mod : function ( qu, value ) {
|
|
var operands = sys.flatten( qu.operands );
|
|
if ( operands.length !== 2 ) {
|
|
throw new Error( "$mod requires two operands" );
|
|
}
|
|
var mod = operands[0];
|
|
var 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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/probe");
|
|
* probe.find( data, {"name.first" : {$regex : "m*", $options : "i"}} );
|
|
*/
|
|
|
|
$regex : function ( qu, value ) {
|
|
var 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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/probe");
|
|
* probe.find( data, {attr : {$elemMatch : [
|
|
* {color : "red", "hand" : "left"}
|
|
* ]}} );
|
|
*/
|
|
$elemMatch : function ( qu, value ) {
|
|
var expression, test, _i, _len;
|
|
|
|
if ( sys.isArray( value ) ) {
|
|
var _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:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/probe");
|
|
* probe.find( data, {$and : [
|
|
* {"name.first" : "Mildred"},
|
|
* {"name.last" : "Graves"}
|
|
* ]} );
|
|
*/
|
|
|
|
$and : function ( qu, value, record ) {
|
|
var isAnd = false;
|
|
|
|
sys.each( qu.operands, function ( expr ) {
|
|
if ( expr.path ) {
|
|
expr.splitPath = expr.splitPath || splitPath( expr.path );
|
|
}
|
|
var test = reachin( expr.splitPath, record, expr.operation );
|
|
isAnd = operations[expr.operation]( expr, test, record );
|
|
if ( !isAnd ) {
|
|
return false;
|
|
}
|
|
} );
|
|
|
|
return isAnd;
|
|
},
|
|
/**
|
|
* Returns true if any of the conditions of the query are met
|
|
* `{$or: [query1, query2, query3]}`
|
|
* @name $or
|
|
* @memberOf module:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/probe");
|
|
* probe.find( data, {$or : [
|
|
* "age" : {$in : [24, 28, 60]}},
|
|
* {categories : "cat1"}
|
|
* ]} );
|
|
*/
|
|
$or : function ( qu, value, record ) {
|
|
var isOr = false;
|
|
sys.each( qu.operands, function ( expr ) {
|
|
if ( expr.path ) {
|
|
expr.splitPath = expr.splitPath || splitPath( expr.path );
|
|
}
|
|
var test = reachin( expr.splitPath, record, expr.operation );
|
|
isOr = operations[expr.operation]( expr, test, record );
|
|
if ( isOr ) {
|
|
return false;
|
|
}
|
|
} );
|
|
|
|
return isOr;
|
|
},
|
|
/**
|
|
* Returns true if none of the conditions of the query are met
|
|
* `{$nor: [query1, query2, query3]}`
|
|
* @name $nor
|
|
* @memberOf module:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/probe");
|
|
* probe.find( data, {$nor : [
|
|
* {"age" : {$in : [24, 28, 60]}},
|
|
* {categories : "cat1"}
|
|
* ]} );
|
|
*/
|
|
$nor : function ( qu, value, record ) {
|
|
var isOr = false;
|
|
sys.each( qu.operands, function ( expr ) {
|
|
if ( expr.path ) {
|
|
expr.splitPath = expr.splitPath || splitPath( expr.path );
|
|
}
|
|
var test = reachin( expr.splitPath, record, expr.operation );
|
|
isOr = operations[expr.operation]( expr, test, record );
|
|
if ( isOr ) {
|
|
return false;
|
|
}
|
|
} );
|
|
|
|
return !isOr;
|
|
},
|
|
/**
|
|
* Logical NOT on the conditions of the query
|
|
* `{$not: [query1, query2, query3]}`
|
|
* @name $not
|
|
* @memberOf module:documents/probe.queryOperators
|
|
* @example
|
|
* var probe = require("documents/probe");
|
|
* probe.find( data, {$not : {"age" : {$lt : 24}}} );
|
|
*/
|
|
$not : function ( qu, value, record ) {
|
|
|
|
var result = false;
|
|
sys.each( qu.operands, function ( expr ) {
|
|
if ( expr.path ) {
|
|
expr.splitPath = expr.splitPath || splitPath( expr.path );
|
|
}
|
|
var test = reachin( expr.splitPath, record, expr.operation );
|
|
result = operations[expr.operation]( expr, test, record );
|
|
if ( result ) {
|
|
return false;
|
|
}
|
|
} );
|
|
|
|
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
|
|
**/
|
|
function execQuery( obj, qu, shortCircuit, stopOnFirst ) {
|
|
var arrayResults = [];
|
|
var 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:documents/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:documents/probe.updateOperators}
|
|
*/
|
|
exports.update = function ( obj, qu, setDocument ) {
|
|
var 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:documents/probe.queryOperators} for the operators you can use.
|
|
@returns {array} The results
|
|
**/
|
|
exports.find = function ( obj, qu ) {
|
|
var expression, _i, _len;
|
|
|
|
var 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:documents/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:documents/probe.queryOperators} for the operators you can use.
|
|
@returns {array}
|
|
*/
|
|
exports.findKeys = function ( obj, qu ) {
|
|
var expression, _i, _len;
|
|
|
|
var 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:documents/probe.queryOperators} for the operators you can use.
|
|
@returns {object}
|
|
*/
|
|
exports.findOne = function ( obj, qu ) {
|
|
var expression, _i, _len;
|
|
|
|
var query = parseQueryExpression( qu );
|
|
for ( _i = 0, _len = query.length; _i < _len; _i++ ) {
|
|
expression = query[_i];
|
|
if ( expression.path ) {
|
|
expression.splitPath = splitPath( expression.path );
|
|
}
|
|
}
|
|
var results = execQuery( obj, query, false, true ).arrayResults;
|
|
if ( results.length > 0 ) {
|
|
return results[0];
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
exports.seek = exports.findOne;
|
|
/**
|
|
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:documents/probe.queryOperators} for the operators you can use.
|
|
@returns {object}
|
|
*/
|
|
exports.findOneKey = function ( obj, qu ) {
|
|
var expression, _i, _len;
|
|
|
|
var query = parseQueryExpression( qu );
|
|
for ( _i = 0, _len = query.length; _i < _len; _i++ ) {
|
|
expression = query[_i];
|
|
if ( expression.path ) {
|
|
expression.splitPath = splitPath( expression.path );
|
|
}
|
|
}
|
|
var results = execQuery( obj, query, false, true ).keyResults;
|
|
if ( results.length > 0 ) {
|
|
return results[0];
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
exports.seekKey = exports.findOneKey;
|
|
|
|
/**
|
|
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:documents/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, _i, _len;
|
|
|
|
var query = parseQueryExpression( qu );
|
|
for ( _i = 0, _len = query.length; _i < _len; _i++ ) {
|
|
expression = query[_i];
|
|
if ( expression.path ) {
|
|
expression.splitPath = splitPath( expression.path );
|
|
}
|
|
}
|
|
var results = execQuery( obj, query, false, false ).keyResults;
|
|
if ( sys.isArray( obj ) ) {
|
|
var 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:documents/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:documents/probe.queryOperators} for the operators you can use.
|
|
@returns {boolean}
|
|
**/
|
|
exports.any = function ( obj, qu ) {
|
|
var expression, _i, _len;
|
|
|
|
var query = parseQueryExpression( qu );
|
|
for ( _i = 0, _len = query.length; _i < _len; _i++ ) {
|
|
expression = query[_i];
|
|
if ( expression.path ) {
|
|
expression.splitPath = splitPath( expression.path );
|
|
}
|
|
}
|
|
var 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:documents/probe.queryOperators} for the operators you can use.
|
|
@return {array}
|
|
**/
|
|
exports.unique = function ( obj, qu ) {
|
|
var 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:documents/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;
|
|
|
|
var 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,
|
|
"get" : exports.get,
|
|
"set" : exports.set
|
|
};
|
|
|
|
/**
|
|
Binds the query and update methods to a new 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.proxy = function ( obj ) {
|
|
var retVal;
|
|
|
|
retVal = {};
|
|
sys.each( bindables, function ( val, key ) {
|
|
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.mixin = function ( obj, collection ) {
|
|
collection = collection || obj;
|
|
return sys.each( bindables, function ( val, key ) {
|
|
obj[key] = sys.bind( val, obj, collection );
|
|
} );
|
|
};
|
|
|
|
/**
|
|
* These are the supported query operators
|
|
*
|
|
* @memberOf module:documents/probe
|
|
* @name queryOperators
|
|
* @class This is not actually a class, but an artifact of the documentation system
|
|
*/
|
|
|
|
/**
|
|
* These are the supported update operators
|
|
*
|
|
* @memberOf module:documents/probe
|
|
* @name updateOperators
|
|
* @class This is not actually a class, but an artifact of the documentation system
|
|
*/
|
|
</pre>
|
|
</article>
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<div class="clearfix"></div>
|
|
<footer>
|
|
|
|
|
|
<span class="copyright">
|
|
DocStrap Copyright © 2012-2013 The contributors to the JSDoc3 and DocStrap projects.
|
|
</span>
|
|
<br />
|
|
|
|
<span class="jsdoc-message">
|
|
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.0-alpha5</a>
|
|
on Mon Jul 7th 2014 using the <a
|
|
href="https://github.com/terryweiss/docstrap">DocStrap template</a>.
|
|
</span>
|
|
</footer>
|
|
</div>
|
|
|
|
|
|
<br clear="both">
|
|
</div>
|
|
|
|
</div>
|
|
<!--<script src="scripts/sunlight.js"></script>-->
|
|
<script src="scripts/docstrap.lib.js"></script>
|
|
<script src="scripts/bootstrap-dropdown.js"></script>
|
|
<script src="scripts/toc.js"></script>
|
|
|
|
<script>
|
|
$( function () {
|
|
$( "[id*='$']" ).each( function () {
|
|
var $this = $( this );
|
|
|
|
$this.attr( "id", $this.attr( "id" ).replace( "$", "__" ) );
|
|
} );
|
|
|
|
$( "#toc" ).toc( {
|
|
anchorName : function ( i, heading, prefix ) {
|
|
return $( heading ).attr( "id" ) || ( prefix + i );
|
|
},
|
|
selectors : "h1,h2,h3,h4",
|
|
showAndHide : false,
|
|
scrollTo : "100px"
|
|
} );
|
|
|
|
$( "#toc>ul" ).addClass( "nav nav-pills nav-stacked" );
|
|
$( "#main span[id^='toc']" ).addClass( "toc-shim" );
|
|
$( '.dropdown-toggle' ).dropdown();
|
|
// $( ".tutorial-section pre, .readme-section pre" ).addClass( "sunlight-highlight-javascript" ).addClass( "linenums" );
|
|
|
|
$( ".tutorial-section pre, .readme-section pre" ).each( function () {
|
|
var $this = $( this );
|
|
|
|
var example = $this.find( "code" );
|
|
exampleText = example.html();
|
|
var lang = /{@lang (.*?)}/.exec( exampleText );
|
|
if ( lang && lang[1] ) {
|
|
exampleText = exampleText.replace( lang[0], "" );
|
|
example.html( exampleText );
|
|
lang = lang[1];
|
|
} else {
|
|
lang = "javascript";
|
|
}
|
|
|
|
if ( lang ) {
|
|
|
|
$this
|
|
.addClass( "sunlight-highlight-" + lang )
|
|
.addClass( "linenums" )
|
|
.html( example.html() );
|
|
|
|
}
|
|
} );
|
|
|
|
Sunlight.highlightAll( {
|
|
lineNumbers : true,
|
|
showMenu : true,
|
|
enableDoclinks : true
|
|
} );
|
|
} );
|
|
</script>
|
|
|
|
|
|
|
|
<!--Navigation and Symbol Display-->
|
|
|
|
|
|
|
|
<!--Google Analytics-->
|
|
|
|
|
|
</body>
|
|
</html>
|