ElementPath.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. "use strict";
  2. var FieldRef = require('./FieldRef'),
  3. ErrorCodes = require('../../Errors.js').ErrorCodes;
  4. var ElementPath = module.exports = function ElementPath(){
  5. this._fieldRef = new FieldRef();
  6. this._shouldTraverseLeafArray = false;
  7. }, klass = ElementPath, base = Object , proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  8. proto._fieldRef = undefined;
  9. proto._shouldTraverseLeafArray = undefined;
  10. /**
  11. * getFieldDottedOrArray
  12. *
  13. * @method getFieldDottedArray
  14. * @param doc
  15. * @param path
  16. * @param idxPathObj This is an object with a pathID element. This allows for pass by ref in calling function.
  17. * */
  18. klass.getFieldDottedOrArray = function getFieldDottedOrArray(doc, path, idxPathObj){
  19. if (path.numParts() === 0 ) { return doc; }
  20. var res,curr = doc,
  21. stop = false,
  22. partNum = 0;
  23. while (partNum < path.numParts() && !stop) {
  24. res = curr[path.getPart( partNum)];
  25. if(res instanceof Object && Object.keys(res).length === 0){
  26. stop = true;
  27. } else if (res instanceof Object) {
  28. curr = res;
  29. partNum++;
  30. } else if (res instanceof Array) {
  31. stop = true;
  32. } else {
  33. if (partNum + 1 < path.numParts() ) {
  34. res = {};
  35. }
  36. stop = true;
  37. }
  38. }
  39. idxPathObj.pathID = partNum;
  40. return res;
  41. };
  42. /**
  43. * isAllDigits does what it says on the tin.
  44. *
  45. * @method isAllDigits
  46. * @param str
  47. */
  48. klass.isAllDigits = function isAllDigits ( str ){
  49. var digitCheck = /\D/g;
  50. if (digitCheck.exec(str) === null){ return true; }
  51. return false;
  52. };
  53. /**
  54. *
  55. * return the internal fieldRef object
  56. * @method fieldRef
  57. * @param
  58. *
  59. */
  60. proto.fieldRef = function fieldRef(){
  61. return this._fieldRef;
  62. };
  63. /**
  64. *
  65. * Initialize necessary items on this instance
  66. * @method init
  67. * @param path
  68. *
  69. */
  70. proto.init = function init( path ){
  71. this._shouldTraverseLeafArray = true;
  72. this._fieldRef.parse( path );
  73. return {'code':ErrorCodes.OK};
  74. };
  75. /**
  76. *
  77. * Set whether paths should traverse leaves inside arrays
  78. * @method setTraverseLeafArray
  79. * @param
  80. *
  81. */
  82. proto.setTraverseLeafArray = function setTraverseLeafArray( b ){
  83. this._shouldTraverseLeafArray = b;
  84. };
  85. /**
  86. *
  87. * Return whether arrays should traverse leaf arrays
  88. * @method shouldTraverseLeafArray
  89. * @param
  90. *
  91. */
  92. proto.shouldTraverseLeafArray = function shouldTraverseLeafArray( ){
  93. return this._shouldTraverseLeafArray;
  94. };
  95. proto.objAtPath = function objAtPath(doc) {
  96. return klass.objAtPath(doc, this._fieldRef._path);
  97. };
  98. klass.objAtPath = function objAtPath(doc, path) {
  99. if (path.length === 0) {
  100. return doc;
  101. }
  102. if (path.length > 0 && Object.keys(doc).length === 0) {
  103. return {};
  104. }
  105. if (doc === null || doc === undefined) {
  106. return doc;
  107. }
  108. var tpath = path.split('.');
  109. return klass.objAtPath(doc[tpath[0]], tpath.slice(1).join('.'));
  110. };
  111. /**
  112. *
  113. * Helper to wrap our path into the static method
  114. * @method _matches
  115. * @param doc
  116. * @param details
  117. * @param function checker this function is used to check for a valid item at the end of the path
  118. *
  119. */
  120. proto._matches = function _matches(doc, details, checker) {
  121. return klass._matches(doc, this._fieldRef._array, this._shouldTraverseLeafArray, details, checker);
  122. };
  123. /**
  124. *
  125. * _matches exists because we don't have pathIterators, so we need a recursive function call
  126. * through the path pieces
  127. * @method _matches
  128. * @param doc
  129. * @param path
  130. * @param details
  131. * @param function checker this function is used to check for a valid item at the end of the path
  132. *
  133. */
  134. klass._matches = function _matches(doc, path, shouldTraverseLeafArray, details, checker) {
  135. // File: expression_array.cpp lines: 34-53
  136. var k, result, ii, il,
  137. curr = doc,
  138. item = doc;
  139. for (k = 0; k < path.length; k++) {
  140. if ((curr instanceof Object) && (path[k] in curr)) {
  141. item = curr[path[k]];
  142. }
  143. if (path[k].length === 0)
  144. continue;
  145. item = curr[path[k]];
  146. if (item instanceof Object && item.constructor === Object) {
  147. if (!(isNaN(parseInt(path[k], 10)))) {
  148. result = checker(item[path[k]]);
  149. if (result) {
  150. if (details && details.needRecord())
  151. details.setElemMatchKey(ii.toString());
  152. return result;
  153. }
  154. }
  155. curr = item;
  156. continue;
  157. } else if (item instanceof Object && item.constructor === Array) {
  158. if (k == path.length - 1) {
  159. if ((shouldTraverseLeafArray) && (isNaN(parseInt(path[k], 10)))) {
  160. for (ii = 0, il = item.length; ii < il; ii++) {
  161. result = checker(item[ii]);
  162. if (result) {
  163. if (details && details.needRecord())
  164. details.setElemMatchKey(ii.toString());
  165. return result;
  166. }
  167. }
  168. if (item.length === 0)
  169. return checker({});
  170. }
  171. curr = item;
  172. break; // this is the end of the path, so check this array
  173. } else if (!(isNaN(parseInt(path[k + 1], 10)))) {
  174. curr = item;
  175. continue; // the *next* path section is an item in the array so we don't check this whole array
  176. }
  177. // otherwise, check each item in the array against the rest of the path
  178. for (ii = 0, il = item.length; ii < il; ii++) {
  179. var subitem = item[ii];
  180. if (subitem.constructor !== Object) continue; // can't look for a subfield in a non-object value.
  181. if (this._matches(subitem, path.slice(k), shouldTraverseLeafArray, null, checker)) { // check the item against the rest of the path
  182. if (details && details.needRecord())
  183. details.setElemMatchKey(ii.toString());
  184. return true;
  185. }
  186. }
  187. return false; // checked all items in the array and found no matches
  188. }
  189. }
  190. return checker(item);
  191. };