GeoNearDocumentSource.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  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. proto.getSourceName = function() {
  25. return klass.geoNearName;
  26. };
  27. proto.getNext = DocumentSource.GET_NEXT_PASS_THROUGH;
  28. proto.setSource = function(docSource) {
  29. throw new Error('code 16602; $geoNear is only allowed as the first pipeline stage');
  30. };
  31. proto.isValidInitialSource = function() {
  32. return true;
  33. };
  34. proto.serialize = function(explain) {
  35. var result = {};
  36. if (this.coordsIsArray)
  37. result.near = this.near;
  38. else
  39. result.near = [this.near];
  40. // not in buildGeoNearCmd
  41. result.distanceField = this.distanceField.getPath(false);
  42. result.limit = this.limit;
  43. if (this.maxDistance > 0)
  44. result.maxDistance = this.maxDistance;
  45. if (this.query)
  46. result.query = this.query;
  47. if (this.spherical)
  48. result.spherical = this.spherical;
  49. if (this.distanceMultiplier)
  50. result.distanceMultiplier = this.distanceMultiplier;
  51. if (this.includeLocs) {
  52. if (typeof this.includeLocs !== 'string')
  53. throw new Error('code 16607; $geoNear requires that \'includeLocs\' option is a String');
  54. result.includeLocs = this.includeLocs.getPath(false);
  55. }
  56. if (this.uniqueDocs)
  57. result.uniqueDocs = this.uniqueDocs;
  58. var sourceName = this.getSourceName(),
  59. returnObj = {};
  60. returnObj[sourceName] = result;
  61. return returnObj;
  62. };
  63. klass.createFromJson = function(jsonElement, ctx) {
  64. var out = new GeoNearDocumentSource(ctx);
  65. out.parseOptions(jsonElement);
  66. return out;
  67. };
  68. proto.parseOptions = function(options) {
  69. // near and distanceField are required
  70. if (!options.near || !Array.isArray(options.near))
  71. throw new Error('code 16605; $geoNear requires a \'near\' option as an Array');
  72. this.coordsIsArray = options.near instanceof Array;
  73. if (typeof options.distanceField !== 'string')
  74. throw new Error('code 16606: $geoNear a \'distanceNear\' option as a String');
  75. this.distanceField = new FieldPath(options.distanceField);
  76. // remaining fields are optional
  77. // num and limits are synonyms
  78. if (typeof options.limit === 'number')
  79. this.limit = options.limit;
  80. if (typeof options.num === 'number')
  81. this.limit = options.num;
  82. if (typeof options.maxDistance === 'number')
  83. this.maxDistance = options.maxDistance;
  84. if (options.query instanceof Object)
  85. this.query = options.query;
  86. if (options.spherical)
  87. this.spherical = options.spherical;
  88. if (typeof options.distanceMultiplier === 'number')
  89. this.distanceMultiplier = options.distanceMultiplier;
  90. if (options.includeLocs) {
  91. if (typeof options.includeLocs !== 'string')
  92. throw new Error('code 16607; $geoNear requires that \'includeLocs\' option is a String');
  93. this.includeLocs = new FieldPath(options.includeLocs);
  94. }
  95. if (options.uniqueDocs)
  96. this.uniqueDocs;
  97. };