123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- "use strict";
- /**
- * Represents a `Value` (i.e., an `Object`) in `mongo` but in `munge` this is only a set of static helpers since we treat all `Object`s like `Value`s.
- * @class Value
- * @namespace mungedb-aggregate.pipeline
- * @module mungedb-aggregate
- * @constructor
- **/
- var Value = module.exports = function Value(){
- if(this.constructor == Value) throw new Error("Never create instances of this! Use the static helpers only.");
- }, klass = Value, base = Object, proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
- // PRIVATE STUFF
- function getTypeVerifier(type, IClass, isStrict) {
- return function verifyType(value) {
- if (typeof(value) != type) throw new Error("typeof value is not: " + type + "; actual: " + typeof(value));
- if (typeof(IClass) == "function" && !(isStrict ? value.constructor == IClass : value instanceof IClass)) throw new Error("instanceof value is not: " + IClass.name + "; actual: " + value.constructor.name);
- return value;
- };
- }
- // STATIC MEMBERS
- klass.verifyNumber = getTypeVerifier("number", Number); //NOTE: replaces #getDouble(), #getInt(), and #getLong()
- klass.verifyString = getTypeVerifier("string", String);
- klass.verifyDocument = getTypeVerifier("object", Object, true); //TODO: change to verifyObject? since we're not using actual Document instances
- klass.verifyArray = getTypeVerifier("object", Array, true);
- klass.verifyDate = getTypeVerifier("object", Date, true);
- klass.verifyRegExp = getTypeVerifier("object", RegExp, true); //NOTE: renamed from #getRegex()
- //TODO: klass.verifyOid = ...?
- //TODO: klass.VerifyTimestamp = ...?
- klass.verifyBool = getTypeVerifier("boolean", Boolean, true);
- klass.coerceToBool = function coerceToBool(value) {
- if (typeof(value) == "string") return true;
- return !!value; // including null or undefined
- };
- klass.coerceToInt =
- klass.coerceToLong =
- klass.coerceToDouble =
- klass._coerceToNumber = function _coerceToNumber(value) { //NOTE: replaces .coerceToInt(), .coerceToLong(), and .coerceToDouble()
- if (value === null) return 0;
- switch (typeof(value)) {
- case "undefined":
- return 0;
- case "number":
- return value;
- default:
- throw new Error("can't convert from BSON type " + typeof(value) + " to int; codes 16003, 16004, 16005");
- }
- };
- klass.coerceToDate = function coerceToDate(value) {
- //TODO: Support Timestamp BSON type?
- if (value instanceof Date) return value;
- throw new Error("can't convert from BSON type " + typeof(value) + " to Date; uassert code 16006");
- };
- //TODO: klass.coerceToTimeT = ...? try to use as Date first rather than having coerceToDate return Date.parse or dateObj.getTime() or similar
- //TODO: klass.coerceToTm = ...?
- klass.coerceToString = function coerceToString(value) {
- if (value === null) return "";
- switch (typeof(value)) {
- case "undefined":
- return "";
- case "number":
- return value.toString();
- case "string":
- return value;
- default:
- throw new Error("can't convert from BSON type " + typeof(value) + " to String; uassert code 16007");
- }
- };
- //TODO: klass.coerceToTimestamp = ...?
- /**
- * Compare two Values.
- *
- * @static
- * @method compare
- * @param rL left value
- * @param rR right value
- * @returns an integer less than zero, zero, or an integer greater than zero, depending on whether rL < rR, rL == rR, or rL > rR
- **/
- var Document; // loaded lazily below //TODO: a dirty hack; need to investigate and clean up
- klass.compare = function compare(l, r) {
- var lt = typeof(l),
- rt = typeof(r);
- // Special handling for Undefined and NULL values ...
- if (lt === "undefined") {
- if (rt === "undefined") return 0;
- return -1;
- }
- if (l === null) {
- if (rt === "undefined") return 1;
- if (r === null) return 0;
- return -1;
- }
- // We know the left value isn't Undefined, because of the above. Count a NULL value as greater than an undefined one.
- if (rt === "undefined" || r === null) return 1;
- // Numbers
- if (lt === "number" && rt === "number"){
- //NOTE: deviation from Mongo code: they handle NaN a bit differently
- if (isNaN(l)) return isNaN(r) ? 0 : -1;
- if (isNaN(r)) return 1;
- return l < r ? -1 : l > r ? 1 : 0;
- }
- // hack: These should really get converted to their BSON type ids and then compared, we use int vs object in queries
- if (lt === "number" && rt === "object"){
- return -1;
- } else if (lt === "object" && rt === "number") {
- return 1;
- }
- // CW TODO for now, only compare like values
- if (lt !== rt) throw new Error("can't compare values of BSON types [" + lt + " " + l.constructor.name + "] and [" + rt + ":" + r.constructor.name + "]; code 16016");
- // Compare everything else
- switch (lt) {
- case "number":
- throw new Error("number types should have been handled earlier!");
- case "string":
- return l < r ? -1 : l > r ? 1 : 0;
- case "boolean":
- return l == r ? 0 : l ? 1 : -1;
- case "object":
- if (l instanceof Array) {
- for (var i = 0, ll = l.length, rl = r.length; true ; ++i) {
- if (i > ll) {
- if (i > rl) return 0; // arrays are same length
- return -1; // left array is shorter
- }
- if (i > rl) return 1; // right array is shorter
- var cmp = Value.compare(l[i], r[i]);
- if (cmp !== 0) return cmp;
- }
- throw new Error("logic error in Value.compare for Array types!");
- }
- if (l instanceof Date) return l < r ? -1 : l > r ? 1 : 0;
- if (l instanceof RegExp) return l < r ? -1 : l > r ? 1 : 0;
- if (Document === undefined) Document = require("./Document"); //TODO: a dirty hack; need to investigate and clean up
- return Document.compare(l, r);
- default:
- throw new Error("unhandled left hand type:" + lt);
- }
- };
- //TODO: klass.hashCombine = ...?
- //TODO: klass.getWidestNumeric = ...?
- //TODO: klass.getApproximateSize = ...?
- //TODO: klass.addRef = ...?
- //TODO: klass.release = ...?
|