MatchDocumentSource.js 4.5 KB

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