MatchDocumentSource.js 4.5 KB

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