var MatchDocumentSource = module.exports = (function(){ // CONSTRUCTOR /** * A match document source built off of FilterBaseDocumentSource * Currently uses sift to fake it * * @class MatchDocumentSource * @namespace munge.pipepline.documentsource * @module munge * @constructor * @param {Object} query the match query to use **/ var klass = module.exports = MatchDocumentSource = function MatchDocumentSource(query /*, pCtx*/){ if(arguments.length !== 1) throw new Error("one arg expected"); base.call(this); this.matcher = sift(query); this.matchName = "$match"; }, base = require('./FilterBaseDocumentSource'), proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}}); proto.extend = function extend(obj, withObj){ var args = Array.prototype.slice.call(arguments), lastArg = args[args.length - 1]; for(var i = 1, n = args.length; i < n; i++){ withObj = args[i]; for(var key in withObj){ if(withObj.hasOwnProperty(key)){ var objVal = obj[key], withObjVal = withObj[key]; if(objVal instanceof Object && withObjVal.constructor === Object){ Object.extend(objVal, withObjVal, isRecursive); }else{ obj[key] = withObjVal; } } } } return obj; }; // DEPENDENCIES var sift = require("sift"); proto.getSourceName = function getSourceName(){ return this.matchName; }; /** * Create an object that represents the document source. The object * will have a single field whose name is the source's name. This * will be used by the default implementation of addToJsonArray() * to add this object to a pipeline being represented in JSON. * * @method sourceToJson * @param {Object} builder JSONObjBuilder: a blank object builder to write to * @param {Boolean} explain create explain output **/ proto.sourceToJson = function sourceToJson(builder, explain) { builder[this.matchName] = this.matcher.query; }; /** * Test the given document against the predicate and report if it * should be accepted or not. * * @param {object} document the document to test * @returns {bool} true if the document matches the filter, false otherwise **/ proto.accept = function accept(document) { /** * The matcher only takes BSON documents, so we have to make one. * * LATER * We could optimize this by making a document with only the * fields referenced by the Matcher. We could do this by looking inside * the Matcher's BSON before it is created, and recording those. The * easiest implementation might be to hold onto an ExpressionDocument * in here, and give that pDocument to create the created subset of * fields, and then convert that instead. **/ return this.matcher.test(document); }; /** * Create a JSONObj suitable for Matcher construction. * * This is used after filter analysis has moved as many filters to * as early a point as possible in the document processing pipeline. * See db/Matcher.h and the associated wiki documentation for the * format. This conversion is used to move back to the low-level * find() Cursor mechanism. * * @param builder the builder to write to **/ proto.toMatcherJson = function toMatcherJson(builder) { this.extend(builder, this.matcher.query); }; proto.uassertNoDisallowedClauses = function uassertNoDisallowedClauses(query) { for(var key in query){ if(query.hasOwnProperty(key)){ // can't use the Matcher API because this would segfault the constructor if ( query[key] == "$where") throw new Error("code 16395; $where is not allowed inside of a $match aggregation expression"); // geo breaks if it is not the first portion of the pipeline if ( query[key] == "$near") throw new Error("code 16424; $near is not allowed inside of a $match aggregation expression"); if ( query[key] == "$within") throw new Error("code 16425l $within is not allowed inside of a $match aggregation expression"); if ( query[key] == "$nearSphere") throw new Error("code 16426; $nearSphere is not allowed inside of a $match aggregation expression"); if(query[key] instanceof Object && query[key].constructor === Object) this.uassertNoDisallowedClauses(query[key]); } } }; proto.createFromJson = function createFromJson(JsonElement) { if (!(JsonElement instanceof Object) || JsonElement.constructor !== Object) throw new Error("code 15959 ; the match filter must be an expression in an object"); this.uassertNoDisallowedClauses(JsonElement); var matcher = new MatchDocumentSource(JsonElement); return matcher; }; return klass; })();