| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 | 
							- "use strict";
 
- var FieldRangeExpression = module.exports = (function(){
 
- 	// CONSTRUCTOR
 
- 	/**
 
- 	 * Create a field range expression.
 
- 	 *
 
- 	 * Field ranges are meant to match up with classic Matcher semantics, and therefore are conjunctions.
 
- 	 *
 
- 	 * For example, these appear in mongo shell predicates in one of these forms:
 
- 	 *	{ a : C } -> (a == C) // degenerate "point" range
 
- 	 *	{ a : { $lt : C } } -> (a < C) // open range
 
- 	 *	{ a : { $gt : C1, $lte : C2 } } -> ((a > C1) && (a <= C2)) // closed
 
- 	 *
 
- 	 * When initially created, a field range only includes one end of the range.  Additional points may be added via intersect().
 
- 	 *
 
- 	 * Note that NE and CMP are not supported.
 
- 	 *
 
- 	 * @class FieldRangeExpression
 
- 	 * @namespace mungedb.aggregate.pipeline.expressions
 
- 	 * @module mungedb-aggregate
 
- 	 * @extends munge.pipeline.expressions.Expression
 
- 	 * @constructor
 
- 	 * @param pathExpr the field path for extracting the field value
 
- 	 * @param cmpOp the comparison operator
 
- 	 * @param value the value to compare against
 
- 	 * @returns the newly created field range expression
 
- 	 **/
 
- 	var klass = function FieldRangeExpression(pathExpr, cmpOp, value){
 
- 		if(arguments.length !== 3) throw new Error("args expected: pathExpr, cmpOp, and value");
 
- 		this.pathExpr = pathExpr;
 
- 		this.range = new Range({cmpOp:cmpOp, value:value});
 
- 	}, Expression = require("./Expression"), base = Expression, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
- 	// DEPENDENCIES
 
- 	var Value = require("../Value"),
 
- 		ConstantExpression = require("./ConstantExpression");
 
- 	// NESTED CLASSES
 
- 	var Range = (function(){
 
- 		/**
 
- 		 * create a new Range; opts is either {cmpOp:..., value:...} or {bottom:..., isBottomOpen:..., top:..., isTopOpen:...}
 
- 		 * @private
 
- 		 **/
 
- 		var klass = function Range(opts){
 
- 			this.isBottomOpen = this.isTopOpen = false;
 
- 			this.bottom = this.top = undefined;
 
- 			if(opts.hasOwnProperty("cmpOp") && opts.hasOwnProperty("value")){
 
- 				switch (opts.cmpOp) {
 
- 					case Expression.CmpOp.EQ:
 
- 						this.bottom = this.top = opts.value;
 
- 						break;
 
- 					case Expression.CmpOp.GT:
 
- 						this.isBottomOpen = true;
 
- 						/* falls through */
 
- 					case Expression.CmpOp.GTE:
 
- 						this.isTopOpen = true;
 
- 						this.bottom = opts.value;
 
- 						break;
 
- 					case Expression.CmpOp.LT:
 
- 						this.isTopOpen = true;
 
- 						/* falls through */
 
- 					case Expression.CmpOp.LTE:
 
- 						this.isBottomOpen = true;
 
- 						this.top = opts.value;
 
- 						break;
 
- 					case Expression.CmpOp.NE:
 
- 					case Expression.CmpOp.CMP:
 
- 						throw new Error("CmpOp not allowed: " + opts.cmpOp);
 
- 					default:
 
- 						throw new Error("Unexpected CmpOp: " + opts.cmpOp);
 
- 				}
 
- 			}else{
 
- 				this.bottom = opts.bottom;
 
- 				this.isBottomOpen = opts.isBottomOpen;
 
- 				this.top = opts.top;
 
- 				this.isTopOpen = opts.isTopOpen;
 
- 			}
 
- 		}, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
 
- 		// PROTOTYPE MEMBERS
 
- 		proto.intersect = function intersect(range){
 
- 			// Find the max of the bottom end of ranges
 
- 			var maxBottom = range.bottom,
 
- 				maxBottomOpen = range.isBottomOpen;
 
- 			if(this.bottom !== undefined){
 
- 				if(range.bottom === undefined){
 
- 					maxBottom = this.bottom;
 
- 					maxBottomOpen = this.isBottomOpen;
 
- 				}else{
 
- 					if(Value.compare(this.bottom, range.bottom) === 0){
 
- 						maxBottomOpen = this.isBottomOpen || range.isBottomOpen;
 
- 					}else{
 
- 						maxBottom = this.bottom;
 
- 						maxBottomOpen = this.isBottomOpen;
 
- 					}
 
- 				}
 
- 			}
 
- 			// Find the min of the tops of the ranges
 
- 			var minTop = range.top,
 
- 				minTopOpen = range.isTopOpen;
 
- 			if(this.top !== undefined){
 
- 				if(range.top === undefined){
 
- 					minTop = this.top;
 
- 					minTopOpen = this.isTopOpen;
 
- 				}else{
 
- 					if(Value.compare(this.top, range.top) === 0){
 
- 						minTopOpen = this.isTopOpen || range.isTopOpen;
 
- 					}else{
 
- 						minTop = this.top;
 
- 						minTopOpen = this.isTopOpen;
 
- 					}
 
- 				}
 
- 			}
 
- 			if(Value.compare(maxBottom, minTop) <= 0)
 
- 				return new Range({bottom:maxBottom, isBottomOpen:maxBottomOpen, top:minTop, isTopOpen:minTopOpen});
 
- 			return null; // empty intersection
 
- 		};
 
- 		proto.contains = function contains(value){
 
- 			var cmp;
 
- 			if(this.bottom !== undefined){
 
- 				cmp = Value.compare(value, this.bottom);
 
- 				if(cmp < 0) return false;
 
- 				if(this.isBottomOpen && cmp === 0) return false;
 
- 			}
 
- 			if(this.top !== undefined){
 
- 				cmp = Value.compare(value, this.top);
 
- 				if(cmp > 0) return false;
 
- 				if(this.isTopOpen && cmp === 0) return false;
 
- 			}
 
- 			return true;
 
- 		};
 
- 		return klass;
 
- 	})();
 
- 	// PROTOTYPE MEMBERS
 
- 	proto.evaluate = function evaluate(obj){
 
- 		if(this.range === undefined) return false;
 
- 		var value = this.pathExpr.evaluate(obj);
 
- 		return this.range.contains(value);
 
- 	};
 
- 	proto.optimize = function optimize(){
 
- 		if(this.range === undefined) return new ConstantExpression(false);
 
- 		if(this.range.bottom === undefined && this.range.top === undefined) return new ConstantExpression(true);
 
- 		return this;
 
- 	};
 
- 	proto.addDependencies = function(deps){
 
- 		return this.pathExpr.addDependencies(deps);
 
- 	};
 
- 	/**
 
- 	 * Add an intersecting range.
 
- 	 *
 
- 	 * This can be done any number of times after creation.  The range is
 
- 	 * internally optimized for each new addition.  If the new intersection
 
- 	 * extends or reduces the values within the range, the internal
 
- 	 * representation is adjusted to reflect that.
 
- 	 *
 
- 	 * Note that NE and CMP are not supported.
 
- 	 *
 
- 	 * @method intersect
 
- 	 * @param cmpOp the comparison operator
 
- 	 * @param pValue the value to compare against
 
- 	 **/
 
- 	proto.intersect = function intersect(cmpOp, value){
 
- 		this.range = this.range.intersect(new Range({cmpOp:cmpOp, value:value}));
 
- 	};
 
- 	proto.toJson = function toJson(){
 
- 		if (this.range === undefined) return false; //nothing will satisfy this predicate
 
- 		if (this.range.top === undefined && this.range.bottom === undefined) return true; // any value will satisfy this predicate
 
- 		// FIXME Append constant values using the $const operator.  SERVER-6769
 
- 		var json = {};
 
- 		if (this.range.top === this.range.bottom) {
 
- 			json[Expression.CmpOp.EQ] = [this.pathExpr.toJson(), this.range.top];
 
- 		}else{
 
- 			var leftOp = {};
 
- 			if (this.range.bottom !== undefined) {
 
- 				leftOp[this.range.isBottomOpen ? Expression.CmpOp.GT : Expression.CmpOp.GTE] = [this.pathExpr.toJson(), this.range.bottom];
 
- 				if (this.range.top === undefined) return leftOp;
 
- 			}
 
- 			var rightOp = {};
 
- 			if(this.range.top !== undefined){
 
- 				rightOp[this.range.isTopOpen ? Expression.CmpOp.LT : Expression.CmpOp.LTE] = [this.pathExpr.toJson(), this.range.top];
 
- 				if (this.range.bottom === undefined) return rightOp;
 
- 			}
 
- 			json.$and = [leftOp, rightOp];
 
- 		}
 
- 		return json;
 
- 	};
 
- //TODO: proto.addToBson = ...?
 
- //TODO: proto.addToBsonObj = ...?
 
- //TODO: proto.addToBsonArray = ...?
 
- //TODO: proto.toMatcherBson = ...? WILL PROBABLY NEED THESE...
 
- 	return klass;
 
- })();
 
 
  |