| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223 |
- "use strict";
- var FieldRef = require('./FieldRef'),
- ErrorCodes = require('../../Errors.js').ErrorCodes;
- var ElementPath = module.exports = function ElementPath(){
- this._fieldRef = new FieldRef();
- this._shouldTraverseLeafArray = false;
- }, klass = ElementPath, base = Object , proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
- proto._fieldRef = undefined;
- proto._shouldTraverseLeafArray = undefined;
- /**
- * getFieldDottedOrArray
- *
- * @method getFieldDottedArray
- * @param doc
- * @param path
- * @param idxPathObj This is an object with a pathID element. This allows for pass by ref in calling function.
- * */
- klass.getFieldDottedOrArray = function getFieldDottedOrArray(doc, path, idxPathObj){
- if (path.numParts() === 0 ) { return doc; }
- var res,curr = doc,
- stop = false,
- partNum = 0;
- while (partNum < path.numParts() && !stop) {
- res = curr[path.getPart( partNum)];
- if(res instanceof Object && Object.keys(res).length === 0){
- stop = true;
- } else if (res instanceof Object) {
- curr = res;
- partNum++;
- } else if (res instanceof Array) {
- stop = true;
- } else {
- if (partNum + 1 < path.numParts() ) {
- res = {};
- }
- stop = true;
- }
- }
- idxPathObj.pathID = partNum;
- return res;
- };
- /**
- * isAllDigits does what it says on the tin.
- *
- * @method isAllDigits
- * @param str
- */
- klass.isAllDigits = function isAllDigits ( str ){
- var digitCheck = /\D/g;
- if (digitCheck.exec(str) === null){ return true; }
- return false;
- };
- /**
- *
- * return the internal fieldRef object
- * @method fieldRef
- * @param
- *
- */
- proto.fieldRef = function fieldRef(){
- return this._fieldRef;
- };
- /**
- *
- * Initialize necessary items on this instance
- * @method init
- * @param path
- *
- */
- proto.init = function init( path ){
- this._shouldTraverseLeafArray = true;
- this._fieldRef.parse( path );
- return {'code':ErrorCodes.OK};
- };
- /**
- *
- * Set whether paths should traverse leaves inside arrays
- * @method setTraverseLeafArray
- * @param
- *
- */
- proto.setTraverseLeafArray = function setTraverseLeafArray( b ){
- this._shouldTraverseLeafArray = b;
- };
- /**
- *
- * Return whether arrays should traverse leaf arrays
- * @method shouldTraverseLeafArray
- * @param
- *
- */
- proto.shouldTraverseLeafArray = function shouldTraverseLeafArray( ){
- return this._shouldTraverseLeafArray;
- };
- proto.objAtPath = function objAtPath(doc) {
- return klass.objAtPath(doc, this._fieldRef._path);
- };
- klass.objAtPath = function objAtPath(doc, path) {
- if (path.length === 0) {
- return doc;
- }
- if (path.length > 0 && Object.keys(doc).length === 0) {
- return {};
- }
- if (doc === null || doc === undefined) {
- return doc;
- }
- var tpath = path.split('.');
- return klass.objAtPath(doc[tpath[0]], tpath.slice(1).join('.'));
- };
- /**
- *
- * Helper to wrap our path into the static method
- * @method _matches
- * @param doc
- * @param details
- * @param function checker this function is used to check for a valid item at the end of the path
- *
- */
- proto._matches = function _matches(doc, details, checker) {
- return klass._matches(doc, this._fieldRef._array, this._shouldTraverseLeafArray, details, checker);
- };
- /**
- *
- * _matches exists because we don't have pathIterators, so we need a recursive function call
- * through the path pieces
- * @method _matches
- * @param doc
- * @param path
- * @param details
- * @param function checker this function is used to check for a valid item at the end of the path
- *
- */
- klass._matches = function _matches(doc, path, shouldTraverseLeafArray, details, checker){
- var k, result, ii, il,
- curr = doc,
- item = doc;
- for (k = 0; k < path.length; k++) {
- if ((curr instanceof Object) && (path[k] in curr)) {
- item = curr[path[k]];
- }
- if (path[k].length === 0)
- continue;
- item = curr[path[k]];
- if (item instanceof Object && item.constructor === Object) {
- if (!(isNaN(parseInt(path[k], 10)))) {
- result = checker(item[path[k]]);
- if (result) {
- if (details && details.needRecord())
- details.setElemMatchKey(ii.toString());
- return result;
- }
- }
- curr = item;
- continue;
- } else if (item instanceof Object && item.constructor === Array) {
- if (k == path.length - 1) {
- if ((shouldTraverseLeafArray) && (isNaN(parseInt(path[k], 10)))) {
- for (ii = 0, il = item.length; ii < il; ii++) {
- result = checker(item[ii]);
- if (result) {
- if (details && details.needRecord())
- details.setElemMatchKey(ii.toString());
- return result;
- }
- }
- if (item.length === 0)
- return checker({});
- }
- curr = item;
- break; // this is the end of the path, so check this array
- } else if (!(isNaN(parseInt(path[k + 1], 10)))) {
- curr = item;
- continue; // the *next* path section is an item in the array so we don't check this whole array
- }
- // otherwise, check each item in the array against the rest of the path
- for (ii = 0, il = item.length; ii < il; ii++) {
- var subitem = item[ii];
- if (!subitem || subitem.constructor !== Object) continue; // can't look for a subfield in a non-object value.
- if (this._matches(subitem, path.slice(k), shouldTraverseLeafArray, null, checker)) { // check the item against the rest of the path
- if (details && details.needRecord())
- details.setElemMatchKey(ii.toString());
- return true;
- }
- }
- return false; // checked all items in the array and found no matches
- } else {
- if ( details === undefined && item !== null && curr[path[k+1]] !== undefined) {
- curr = curr[path[k+1]];
- }
- }
- }
- return checker(item);
- };
|