GeoNearDocumentSource.js 3.5 KB

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