FieldPathExpression.js 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. var FieldPathExpression = module.exports = (function(){
  2. // CONSTRUCTOR
  3. /**
  4. * Create a field path expression. Evaluation will extract the value associated with the given field path from the source document.
  5. *
  6. * @class FieldPathExpression
  7. * @namespace munge.pipeline.expressions
  8. * @module munge
  9. * @extends munge.pipeline.expressions.Expression
  10. * @constructor
  11. * @param {String} fieldPath the field path string, without any leading document indicator
  12. **/
  13. var klass = function FieldPathExpression(path){
  14. if(arguments.length !== 1) throw new Error("args expected: path");
  15. this.path = new FieldPath(path);
  16. }, base = require("./Expression"), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  17. // DEPENDENCIES
  18. var FieldPath = require("../FieldPath");
  19. // PROTOTYPE MEMBERS
  20. proto.evaluate = function evaluate(obj){
  21. return this._evaluatePath(obj, 0, this.path.fields.length);
  22. };
  23. /**
  24. * Internal implementation of evaluate(), used recursively.
  25. *
  26. * The internal implementation doesn't just use a loop because of the
  27. * possibility that we need to skip over an array. If the path is "a.b.c",
  28. * and a is an array, then we fan out from there, and traverse "b.c" for each
  29. * element of a:[...]. This requires that a be an array of objects in order
  30. * to navigate more deeply.
  31. *
  32. * @param index current path field index to extract
  33. * @param pathLength maximum number of fields on field path
  34. * @param pDocument current document traversed to (not the top-level one)
  35. * @returns the field found; could be an array
  36. **/
  37. proto._evaluatePath = function _evaluatePath(obj, i, len){
  38. var fieldName = this.path.fields[i],
  39. field = obj[fieldName]; // It is possible we won't have an obj (document) and we need to not fail if that is the case
  40. // if the field doesn't exist, quit with an undefined value
  41. if (field === undefined) return undefined;
  42. // if we've hit the end of the path, stop
  43. if (++i >= len) return field;
  44. // We're diving deeper. If the value was null, return null
  45. if(field === null) return undefined;
  46. if (field.constructor === Object) {
  47. return this._evaluatePath(field, i, len);
  48. } else if (Array.isArray(field)) {
  49. var results = [];
  50. for (var i2 = 0, l2 = field.length; i2 < l2; i2++) {
  51. var subObj = field[i2],
  52. subObjType = typeof(subObj);
  53. if (subObjType === "undefined" || subObj === null) {
  54. results.push(subObj);
  55. } else if (subObj.constructor === Object) {
  56. results.push(this._evaluatePath(subObj, i, len));
  57. } else {
  58. throw new Error("the element '" + fieldName + "' along the dotted path '" + this.path.getPath() + "' is not an object, and cannot be navigated.; code 16014");
  59. }
  60. }
  61. return results;
  62. }
  63. return undefined;
  64. };
  65. proto.optimize = function(){
  66. return this;
  67. };
  68. proto.addDependencies = function addDependencies(deps){
  69. deps.push(this.path.getPath());
  70. return deps;
  71. };
  72. proto.toJson = function toJson(){
  73. return this.path.getPath(true);
  74. };
  75. //TODO: proto.addToBsonObj = ...?
  76. //TODO: proto.addToBsonArray = ...?
  77. //proto.writeFieldPath = ...? use #getFieldPath instead
  78. return klass;
  79. })();