GeoNearDocumentSource.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. "use strict";
  2. var DocumentSource = require("./DocumentSource"),
  3. FieldPath = require("../FieldPath");
  4. /**
  5. * @class GeoNearDocumentSource
  6. * @namespace mungedb-aggregate.pipeline.documentSources
  7. * @module mungedb-aggregate
  8. * @constructor
  9. * @param [ctx] {ExpressionContext}
  10. **/
  11. var GeoNearDocumentSource = module.exports = function GeoNearDocumentSource(ctx) {
  12. if (arguments.length > 1) throw new Error("up to one arg expected");
  13. base.call(this, ctx);
  14. // mongo defaults
  15. this.coordsIsArray = false;
  16. this.limit = 100;
  17. this.maxDistance = -1.0;
  18. this.spherical = false;
  19. this.distanceMultiplier = 1.0;
  20. this.uniqueDocs = true;
  21. }, klass = GeoNearDocumentSource, base = require("./DocumentSource"), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}}); //jshint ignore:line
  22. klass.geoNearName = "$geoNear";
  23. klass.create = function create(expCtx) {
  24. return new GeoNearDocumentSource(expCtx);
  25. };
  26. proto.getSourceName = function() {
  27. return klass.geoNearName;
  28. };
  29. proto.getNext = DocumentSource.GET_NEXT_PASS_THROUGH;
  30. proto.setSource = function(docSource) {
  31. throw new Error("code 16602; $geoNear is only allowed as the first pipeline stage");
  32. };
  33. proto.isValidInitialSource = function() {
  34. return true;
  35. };
  36. proto.serialize = function(explain) {
  37. var result = {};
  38. if (this.coordsIsArray)
  39. result.near = this.near;
  40. else
  41. result.near = [this.near];
  42. // not in buildGeoNearCmd
  43. result.distanceField = this.distanceField.getPath(false);
  44. result.limit = this.limit;
  45. if (this.maxDistance > 0)
  46. result.maxDistance = this.maxDistance;
  47. if (this.query)
  48. result.query = this.query;
  49. if (this.spherical)
  50. result.spherical = this.spherical;
  51. if (this.distanceMultiplier)
  52. result.distanceMultiplier = this.distanceMultiplier;
  53. if (this.includeLocs) {
  54. if (typeof this.includeLocs !== "string")
  55. throw new Error("code 16607; $geoNear requires that \"includeLocs\" option is a String");
  56. result.includeLocs = this.includeLocs.getPath(false);
  57. }
  58. if (this.uniqueDocs)
  59. result.uniqueDocs = this.uniqueDocs;
  60. var sourceName = this.getSourceName(),
  61. returnObj = {};
  62. returnObj[sourceName] = result;
  63. return returnObj;
  64. };
  65. klass.createFromJson = function(jsonElement, ctx) {
  66. var out = new GeoNearDocumentSource(ctx);
  67. out.parseOptions(jsonElement);
  68. return out;
  69. };
  70. proto.parseOptions = function(options) {
  71. // near and distanceField are required
  72. if (!options.near || !Array.isArray(options.near))
  73. throw new Error("code 16605; $geoNear requires a \"near\" option as an Array");
  74. this.coordsIsArray = options.near instanceof Array;
  75. if (typeof options.distanceField !== "string")
  76. throw new Error("code 16606: $geoNear a \"distanceNear\" option as a String");
  77. this.distanceField = new FieldPath(options.distanceField);
  78. // remaining fields are optional
  79. // num and limits are synonyms
  80. if (typeof options.limit === "number")
  81. this.limit = options.limit;
  82. if (typeof options.num === "number")
  83. this.limit = options.num;
  84. if (typeof options.maxDistance === "number")
  85. this.maxDistance = options.maxDistance;
  86. if (options.query instanceof Object)
  87. this.query = options.query;
  88. if (options.spherical)
  89. this.spherical = Boolean(options.spherical);
  90. if (typeof options.distanceMultiplier === "number")
  91. this.distanceMultiplier = options.distanceMultiplier;
  92. if (options.includeLocs) {
  93. if (typeof options.includeLocs !== "string")
  94. throw new Error("code 16607; $geoNear requires that \"includeLocs\" option is a String");
  95. this.includeLocs = new FieldPath(options.includeLocs);
  96. }
  97. if (options.uniqueDocs)
  98. this.uniqueDocs = Boolean(options.uniqueDocs);
  99. };