MatchDocumentSource.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. "use strict";
  2. var matcher = require("../matcher/Matcher2.js");
  3. /**
  4. * A match document source built off of FilterBaseDocumentSource
  5. *
  6. * NOTE: THIS IS A DEVIATION FROM THE MONGO IMPLEMENTATION.
  7. * TODO: internally uses `sift` to fake it, which has bugs, so we need to reimplement this by porting the MongoDB implementation
  8. *
  9. * @class MatchDocumentSource
  10. * @namespace mungedb-aggregate.pipeline.documentSources
  11. * @module mungedb-aggregate
  12. * @constructor
  13. * @param {Object} query the match query to use
  14. * @param [ctx] {ExpressionContext}
  15. **/
  16. var MatchDocumentSource = module.exports = function MatchDocumentSource(query, ctx){
  17. if (arguments.length > 2) throw new Error("up to two args expected");
  18. if (!query) throw new Error("arg `query` is required");
  19. base.call(this, ctx);
  20. this.query = query; // save the query, so we can check it for deps later. THIS IS A DEVIATION FROM THE MONGO IMPLEMENTATION
  21. this.matcher = new matcher(query);
  22. }, klass = MatchDocumentSource, base = require('./FilterBaseDocumentSource'), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  23. klass.matchName = "$match";
  24. proto.getSourceName = function getSourceName(){
  25. return klass.matchName;
  26. };
  27. /**
  28. * Create an object that represents the document source. The object
  29. * will have a single field whose name is the source's name. This
  30. * will be used by the default implementation of addToJsonArray()
  31. * to add this object to a pipeline being represented in JSON.
  32. *
  33. * @method sourceToJson
  34. * @param {Object} builder JSONObjBuilder: a blank object builder to write to
  35. * @param {Boolean} explain create explain output
  36. **/
  37. proto.sourceToJson = function sourceToJson(builder, explain) {
  38. builder[this.getSourceName()] = this.matcher._pattern;
  39. };
  40. /**
  41. * Test the given document against the predicate and report if it should be accepted or not.
  42. * @param {object} document the document to test
  43. * @returns {bool} true if the document matches the filter, false otherwise
  44. **/
  45. proto.accept = function accept(document) {
  46. /**
  47. * The matcher only takes BSON documents, so we have to make one.
  48. *
  49. * LATER
  50. * We could optimize this by making a document with only the
  51. * fields referenced by the Matcher. We could do this by looking inside
  52. * the Matcher's BSON before it is created, and recording those. The
  53. * easiest implementation might be to hold onto an ExpressionDocument
  54. * in here, and give that pDocument to create the created subset of
  55. * fields, and then convert that instead.
  56. **/
  57. return this.matcher.matches(document);
  58. };
  59. /**
  60. * Create a JSONObj suitable for Matcher construction.
  61. *
  62. * This is used after filter analysis has moved as many filters to
  63. * as early a point as possible in the document processing pipeline.
  64. * See db/Matcher.h and the associated wiki documentation for the
  65. * format. This conversion is used to move back to the low-level
  66. * find() Cursor mechanism.
  67. *
  68. * @param builder the builder to write to
  69. **/
  70. proto.toMatcherJson = function toMatcherJson(builder) {
  71. var q = this.matcher._pattern;
  72. for(var k in q){
  73. builder[k] = q[k];
  74. }
  75. };
  76. klass.uassertNoDisallowedClauses = function uassertNoDisallowedClauses(query) {
  77. for(var key in query){
  78. if(query.hasOwnProperty(key)){
  79. // can't use the Matcher API because this would segfault the constructor
  80. if (query[key] == "$where") throw new Error("code 16395; $where is not allowed inside of a $match aggregation expression");
  81. // geo breaks if it is not the first portion of the pipeline
  82. if (query[key] == "$near") throw new Error("code 16424; $near is not allowed inside of a $match aggregation expression");
  83. if (query[key] == "$within") throw new Error("code 16425; $within is not allowed inside of a $match aggregation expression");
  84. if (query[key] == "$nearSphere") throw new Error("code 16426; $nearSphere is not allowed inside of a $match aggregation expression");
  85. if (query[key] instanceof Object && query[key].constructor === Object) this.uassertNoDisallowedClauses(query[key]);
  86. }
  87. }
  88. };
  89. klass.createFromJson = function createFromJson(jsonElement, ctx) {
  90. if (!(jsonElement instanceof Object) || jsonElement.constructor !== Object) throw new Error("code 15959 ; the match filter must be an expression in an object");
  91. klass.uassertNoDisallowedClauses(jsonElement);
  92. var matcher = new MatchDocumentSource(jsonElement, ctx);
  93. return matcher;
  94. };