ElementPath.js 5.5 KB

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