Skip to content
Browse files

All tests passing running much like Mongoose tests. Package.json test…

…ed. Makefile running tests. README.md up to date.
  • Loading branch information...
1 parent d1f14b7 commit 55f5d14430693f0a4a6a1b470c0dce6a28ebda0f @marksweiss committed
Showing with 307 additions and 267 deletions.
  1. +1 −0 Makefile
  2. +16 −15 README.md
  3. +179 −172 flatmatcher.js
  4. +2 −2 package.json
  5. +109 −78 test/flatmatcher.test.js
View
1 Makefile
@@ -1,3 +1,4 @@
+
TESTS = $(shell find test -name '*.test.js')
test:
View
31 README.md
@@ -5,19 +5,19 @@ An query pre-processing plugin for Mongoose node.js ORM.
mongoose-flatmatcher allows you fully encapsulate client access to your Mongoose Schemas by freeing them from needing to provide JSON objects with fully qualified MongodDB dot-notation paths and `$in` syntax for nested array document properties.
-Effectively, clients simply query for the document properties and values they care about with flat key/value JSON, without needing to know the Mongoose Schema or MongoDB document model it maps to.
+Effectively, clients can query for the document properties and values they care about with flat key/value JSON, without needing to know the Mongoose Schema or MongoDB document model it maps to.
-Schemas decorated with mongoose-flatmatcher can accept JSON objects and return JSON with dot-notation qualified paths and `$in` syntax to be used as a match predicate in find(), update() and remove() calls by Mongoose against MongoDB document collections.
+Schemas decorated with mongoose-flatmatcher can accept JSON objects and return JSON with dot-notation qualified paths and `$in` syntax to be used as a match predicate in `find()`, `update()` and `remove()` calls by Mongoose against MongoDB document collections.
## Schema Decoration
mongoose-flatmatcher decorates your Schemas with three methods to support flat matching. They are:
-1. `getFlatMatcher()` accepts flat JSON and returns the correct match predicate JSON, as described above. In normal usage, this is the only call you will need to use.
+1. `getMatchObj()` accepts flat JSON and returns the correct match predicate JSON, as described above. In normal usage, this is the only call you will need to use.
-2. `setFlatMatcherMaxDepth()` sets the maximum recursion allowed when building a matcher for a Schema. The default is 5. This setting prevents the matcher from recursing indefinitely since cycles in Mongoose Schemas are possible. The depth equates to the depth of nesting in your Schema, so you should only need to change this if you have a highly nested schema.
+2. `setMaxDepth()` sets the maximum recursion allowed when building a matcher for a Schema. The default is 5. This setting prevents the matcher from recursing indefinitely since cycles in Mongoose Schemas are possible. The depth equates to the depth of nesting in your Schema, so you should only need to change this if you have a highly nested schema.
-3. `getFlatMatcherMaxDepth()` gets the maximum recursion allowed when building a matcher for a Schema.
+3. `getMaxDepth()` gets the maximum recursion depth allowed when building a matcher for a Schema.
To decorate your schema:
@@ -35,13 +35,14 @@ To decorate your schema:
...
...
// FlatMatcher takes care of the dot-notation qualified path for us, visitors => meta.visitors
- var matchArgs = {title : testTitle, published : true, visitors : 5};
- BlogPost.plugin(flatMatcher, {}); // empty opts hash
- var matcher = BlogPost.getMatcher(matchArgs);
+ var matchArgs = {title : testTitle, published : true, visitors : 5};
+ var opts = {};
+ BlogPost.plugin(flatMatcher, opts);
+ var matchObj = BlogPost.getMatchObj(matchArgs);
...
...
- // Now use the matcher in a query
- BlogPost.find(matcher, function (err, found) {
+ // Now use the matchObj in a query
+ BlogPost.find(matchObj, function (err, found) {
...
...
```
@@ -54,13 +55,13 @@ This is a convenient idiom in any case where:
- Data is accessed through a REST API and you want to support a minimum of mapped accessor resources and allow name/value access through the query string. Since node web app frameworks typically expose the query string as a JSON object, this seamlessly extends that functionality to map the query string into an arbitrary Mongoose schema.
- Data is read or written by various front end calls from Ajax or form submits, and you don't want to leak the abstraction of full dot-notation and `$in` syntax qualification into the client code in many places
- The Schema may change often
-- Data may be dynamically selected, such as ad-hoc report querying, and you want to support simple query-string (or other flat access by key-value pair) for different combinations of properties
+- Data may be dynamically selected, such as ad-hoc report querying, and you want to support simple query-string access (or other flat access by key-value pair) for different combinations of properties
## Limitations and Issues
-- If a property name in a Schema is not unique, clients must, as they would without mongoose-flatmatcher, pass in a fully-qualified property name with dot notation to `getFlatMatcher`. flatmatcher cannot magically disambiguate properties at different levels in a Schema with the same name.
-- Retrieving from Mongoose Schema objects in an embedded array using `$in` is not supported fully, and the test for this case is incomplete. This is because retrieving objects in embedded arrays in MongoDB requires passing literals that fully match the objects in the database, but constructed Schema objects in Mongoose add properties for internal implementation that are then in the records in the DB but not easily available to the client needing to match those objects in those documents exactly.
-- mongoose-flatmatcher depends on the private implementation of the Mongoose lib Schema class, in particular the 'tree' property of that class.
+- If a property name in a Schema is not unique, clients must, as they would without mongoose-flatmatcher, pass in a fully-qualified property name with dot notation to `getMatchObj`. flatmatcher cannot magically disambiguate properties with the same name which live at different levels in a Schema.
+- Retrieving Mongoose Schema objects in an embedded array using `$in` is not supported fully, and the test for this case is incomplete. This is because retrieving objects in embedded arrays in MongoDB requires passing literals that fully match the objects in the database, but constructed Schema objects in Mongoose add properties for internal implementation that are then in the documents in the database but not easily available to the client needing to match those objects in those documents exactly.
+- mongoose-flatmatcher depends on the private implementation of the Mongoose Schema class, in particular the 'tree' property of that class.
## License
@@ -68,4 +69,4 @@ MIT License
## Author
-Mark Weiss
+Mark Weiss
View
351 flatmatcher.js
@@ -8,195 +8,193 @@
// - email aheckman
-var FlatMatcher = (function () {
+var FlatMatcher = (function () {
/**
- * Default maximum recursion depth for building flat matcher from Schema.tree. Protects against cycles.
- * @api private
- */
- var maxDepth = 5;
-
- /**
- * Internal helper supporting FlatMatcher#getMatcher.
+ * Returns the correct MongoDB query syntax JSON for flat JSON object of properties/values to be used
+ * as Mongo query predicate
*
- * @see FlatMatcher#getMatcher
- * @param {String} pathString dot-notation prefix for current traversal of properties in the Schema
- * @param {Object} tree the Schema#tree property for the Schema
- * @param {Object} matcherLookup the return value mapping flat property names to dot-notation paths, value casting functions and isArray flags
- * @param {Number} depth depth of recursion of call
- * @api private
- */
- var buildMatcherLookup = function (pathString, tree, matcherLookup, depth) {
- // Guard against arbitrarily deep recursion for properties that are
- // object/Schema types with properties that are other object/Schema types forming a cycle
- if (depth > maxDepth) {
- return matcherLookup;
- }
- depth += 1;
-
+ * @see FlatMatcher#buildMatcherLookup
+ * @param {Object} matchArgs JSON properties/values to use in a query matching documents of this Model's Schema
+ * @api public
+ */
+ var getMatchObj = function (matchArgs) {
+
// Helpers
- var MatcherLookupEntry = function (p, c, a) {
- return {pathString : p, caster : c, isArray : a};
- };
- // Guard against overwriting existing entries. As documented, this function will only behave as expected
- // if all properties in Schema at all levels have unique names. So the function creates an entry only for
- // the first instance of any name encountered without qualifying it with its full dot notation path (the
- // entire point of using the Matcher.) But it will create dot-notation qualified entries if there is a
- // clash, so these can be accessed by full path.
- var makeLookupEntry = function (prop, pathString, caster, isArrayVal) {
- var entry = MatcherLookupEntry(pathString, caster, isArrayVal);
- if (! matcherLookup.hasOwnProperty(prop)) {
- matcherLookup[prop] = entry;
+ var isArray = function (value) {
+ return Object.prototype.toString.apply(value) === '[object Array]';
+ };
+
+ /**
+ * Internal helper supporting FlatMatcher#getMatcher.
+ *
+ * @see FlatMatcher#getMatcher
+ * @param {String} pathString dot-notation prefix for current traversal of properties in the Schema
+ * @param {Object} tree the Schema#tree property for the Schema
+ * @param {Object} matcherLookup the return value mapping flat property names to dot-notation paths, value casting functions and isArray flags
+ * @param {Number} depth depth of recursion of call
+ * @api private
+ */
+ var buildMatcherLookup = function (pathString, tree, matcherLookup, depth) {
+ // Guard against arbitrarily deep recursion for properties that are
+ // object/Schema types with properties that are other object/Schema types forming a cycle
+ if (depth > maxDepth) {
+ return matcherLookup;
}
- else {
- matcherLookup[pathString] = entry;
- }
- };
+ depth += 1;
- var isArray = function (value) {
- return Object.prototype.toString.apply(value) === '[object Array]';
- };
+ // Helpers
+ var MatcherLookupEntry = function (p, c, a) {
+ return {pathString : p, caster : c, isArray : a};
+ };
+ // Guard against overwriting existing entries. As documented, this function will only behave as expected
+ // if all properties in Schema at all levels have unique names. So the function creates an entry only for
+ // the first instance of any name encountered without qualifying it with its full dot notation path (the
+ // entire point of using the Matcher.) But it will create dot-notation qualified entries if there is a
+ // clash, so these can be accessed by full path.
+ var makeLookupEntry = function (prop, pathString, caster, isArrayVal) {
+ var entry = MatcherLookupEntry(pathString, caster, isArrayVal);
+ if (! matcherLookup.hasOwnProperty(prop)) {
+ matcherLookup[prop] = entry;
+ }
+ else {
+ matcherLookup[pathString] = entry;
+ }
+ };
- var isFunction = function (value) {
- return typeof value === 'function';
- };
+ var isArray = function (value) {
+ return Object.prototype.toString.apply(value) === '[object Array]';
+ };
- var isObject = function (value, literal) {
- var type = typeof value, test = !!value && type === 'object';
- return test && literal ? value.constructor === Object : test;
- };
+ var isFunction = function (value) {
+ return typeof value === 'function';
+ };
- var isMongooseSchema = function (v) {
- return (isObject(v) && v.hasOwnProperty('paths') && v.hasOwnProperty('tree'));
- };
+ var isObject = function (value, literal) {
+ var type = typeof value, test = !!value && type === 'object';
+ return test && literal ? value.constructor === Object : test;
+ };
- var mongooseSchemaTreeEquals = function (s1, s2) {
- var objLen = function (o) {
- var l = 0;
- for (prop in o) {
- l += 1;
- }
- return l;
+ var isMongooseSchema = function (v) {
+ return (isObject(v) && v.hasOwnProperty('paths') && v.hasOwnProperty('tree'));
};
- var isSamePropertyVal = function (pv1, pv2) {
- if ( (isArray(pv1) && isArray(pv2)) || (isFunction(pv1) && isFunction(pv2)) ||
- (isObject(pv1) && isObject(pv2)) || (isMongooseSchema(pv1) && isMongooseSchema(pv2)) ) {
- return true;
- }
- return false;
- };
-
- // Compare the tree property of each Schema object for same length
- var l1, l2;
- if ((l1 = objLen(s1)) !== (l2 = objLen(s2))) {
- return false;
- }
- // Iterate each tree, insuring same property names and Schema data types
- for (prop in s1) {
- if (! prop in s2) {
+ var mongooseSchemaTreeEquals = function (s1, s2) {
+ var objLen = function (o) {
+ var l = 0;
+ for (prop in o) {
+ l += 1;
+ }
+ return l;
+ };
+ var isSamePropertyVal = function (pv1, pv2) {
+ if ( (isArray(pv1) && isArray(pv2)) || (isFunction(pv1) && isFunction(pv2)) ||
+ (isObject(pv1) && isObject(pv2)) || (isMongooseSchema(pv1) && isMongooseSchema(pv2)) ) {
+ return true;
+ }
return false;
+ };
+
+ // Compare the tree property of each Schema object for same length
+ var l1, l2;
+ if ((l1 = objLen(s1)) !== (l2 = objLen(s2))) {
+ return false;
}
- else if (! isSamePropertyVal(s1[prop], s2[prop])) {
- return false;
+
+ // Iterate each tree, insuring same property names and Schema data types
+ for (prop in s1) {
+ if (! prop in s2) {
+ return false;
+ }
+ else if (! isSamePropertyVal(s1[prop], s2[prop])) {
+ return false;
+ }
}
- }
- return true;
- };
+
+ return true;
+ };
- var noOpCaster = function (value) {
- return value;
- };
- // /Helpers
+ var noOpCaster = function (value) {
+ return value;
+ };
+ // /Helpers
- var propVal = null;
- var curPathString = pathString;
- var isArrayVal = false;
- var literal = 1;
+ var propVal = null;
+ var curPathString = pathString;
+ var isArrayVal = false;
+ var literal = 1;
- for (prop in tree) {
- pathString = curPathString;
- // Build dot-notation path
- if (pathString.length > 0) {
- pathString += '.';
- }
- pathString += prop;
+ for (prop in tree) {
+ pathString = curPathString;
+ // Build dot-notation path
+ if (pathString.length > 0) {
+ pathString += '.';
+ }
+ pathString += prop;
- // NOTE: Depends on internal Schema property 'tree' property implementation.
- // tree is an object with a structure mirroring the Schema definition.
- // Properties are fields in the Schema. Their value is:
- // - a casting function for the type of data the field holds, if the field is a simple type
- // i.e. string, number, boolean or date
- // - a nested object if the field contains nested objects
- // - a nested MongooseSchema (a specific object type) if the field holds nested Schemas
- // - an array if the field holds any type of value in an embedded array
- propVal = tree[prop];
+ // NOTE: Depends on internal Schema property 'tree' property implementation.
+ // tree is an object with a structure mirroring the Schema definition.
+ // Properties are fields in the Schema. Their value is:
+ // - a casting function for the type of data the field holds, if the field is a simple type
+ // i.e. string, number, boolean or date
+ // - a nested object if the field contains nested objects
+ // - a nested MongooseSchema (a specific object type) if the field holds nested Schemas
+ // - an array if the field holds any type of value in an embedded array
+ propVal = tree[prop];
- if (isArrayVal = isArray(propVal)) {
- // Arrays can be empty in Mongoose, contain a compound type (object or Schema) or a simple type
- // Arrays are empty or homogenous (have values of only one type). So if the tree value is an array:
- // - if the array in the tree contains a function, then the function is the caster
- // - if the array in the tree contains objects or Schema objects, create two entries:
- // - one is recurse to build dot notation for matching single objects in arrays
- // - one is an entry at the array level without dot notation into the object, passing literal through
- // because matching more than one object using $in requires matching on an array of literal values
- // - if the array in the tree is empty (which is legal in Mongoose), then we can't know what type the
- // array may contain, so create an entry that passes through literal values to match exactly
- if (propVal.length === 0)
- {
- makeLookupEntry(prop, pathString, noOpCaster, isArrayVal);
- continue;
- }
- else {
- propVal = propVal[0];
- }
- }
+ if (isArrayVal = isArray(propVal)) {
+ // Arrays can be empty in Mongoose, contain a compound type (object or Schema) or a simple type
+ // Arrays are empty or homogenous (have values of only one type). So if the tree value is an array:
+ // - if the array in the tree contains a function, then the function is the caster
+ // - if the array in the tree contains objects or Schema objects, create two entries:
+ // - one is recurse to build dot notation for matching single objects in arrays
+ // - one is an entry at the array level without dot notation into the object, passing literal through
+ // because matching more than one object using $in requires matching on an array of literal values
+ // - if the array in the tree is empty (which is legal in Mongoose), then we can't know what type the
+ // array may contain, so create an entry that passes through literal values to match exactly
+ if (propVal.length === 0)
+ {
+ makeLookupEntry(prop, pathString, noOpCaster, isArrayVal);
+ continue;
+ }
+ else {
+ propVal = propVal[0];
+ }
+ }
- if (isFunction(propVal, literal)) {
- makeLookupEntry(prop, pathString, propVal, isArrayVal);
- }
- else if(isMongooseSchema(propVal)) {
- // Embedded object in array, create entry for literal match as per comment above
- if (isArrayVal) {
- makeLookupEntry(prop, pathString, noOpCaster, isArrayVal);
+ if (isFunction(propVal, literal)) {
+ makeLookupEntry(prop, pathString, propVal, isArrayVal);
}
+ else if(isMongooseSchema(propVal)) {
+ // Embedded object in array, create entry for literal match as per comment above
+ if (isArrayVal) {
+ makeLookupEntry(prop, pathString, noOpCaster, isArrayVal);
+ }
- // If embedded Schema is a a child property of the same Schema, we know we have endless recursion,
- /// so just support one level of recursion from here
- if (mongooseSchemaTreeEquals(tree, propVal.tree) && depth < maxDepth) {
- depth = maxDepth;
- }
+ // If embedded Schema is a a child property of the same Schema, we know we have endless recursion,
+ /// so just support one level of recursion from here
+ if (mongooseSchemaTreeEquals(tree, propVal.tree) && depth < maxDepth) {
+ depth = maxDepth;
+ }
- // Recurse
- matcherLookup = this.buildMatcherLookup(pathString, propVal.tree, matcherLookup, depth);
- }
- else if (isObject(propVal)) {
- if (isArrayVal) {
- makeLookupEntry(prop, pathString, noOpCaster, isArrayVal);
+ // Recurse
+ matcherLookup = buildMatcherLookup(pathString, propVal.tree, matcherLookup, depth);
}
+ else if (isObject(propVal)) {
+ if (isArrayVal) {
+ makeLookupEntry(prop, pathString, noOpCaster, isArrayVal);
+ }
- // Always recurse on embedded plain objects (not Schemas), because these aren't as much "types"
- // as Schemas so there isn't as clear a case of identifying self-reference children and cycles. So
- // just allow and guard against stack overflow
- matcherLookup = this.buildMatcherLookup(pathString, propVal, matcherLookup, depth);
+ // Always recurse on embedded plain objects (not Schemas), because these aren't as much "types"
+ // as Schemas so there isn't as clear a case of identifying self-reference children and cycles. So
+ // just allow and guard against stack overflow
+ matcherLookup = buildMatcherLookup(pathString, propVal, matcherLookup, depth);
+ }
}
- }
- return matcherLookup;
- };
-
- /**
- * Returns the correct MongoDB query syntax JSON for flat JSON object of properties/values to be used
- * as Mongo query predicate
- *
- * @see FlatMatcher#buildMatcherLookup
- * @param {Object} matchArgs JSON properties/values to use in a query matching documents of this Model's Schema
- * @api public
- */
- var getMatcher = function (matchArgs) {
- // Helper
- var isArray = function (value) {
- return Object.prototype.toString.apply(value) === '[object Array]';
- };
+ return matcherLookup;
+ };
+ // /Helpers
+
// The JSON set of document properties/values to return
var pathString = '';
var matcherLookup = {};
@@ -204,7 +202,7 @@ var FlatMatcher = (function () {
matcherLookup = buildMatcherLookup(pathString, this.tree, matcherLookup, depth);
// JSON in valid MongoDB dot-notation and "$in" syntax to be match pred in find(), update() and delete() Mongo calls
- var matcher = {};
+ var matchObj = {};
var propVal = null;
var mlEntry = null;
var j = 0;
@@ -227,22 +225,27 @@ var FlatMatcher = (function () {
propVal[j] = mlEntry.caster(propVal[j]);
}
- matcher[mlEntry.pathString] = {"$in" : propVal};
+ matchObj[mlEntry.pathString] = {"$in" : propVal};
}
else {
- matcher[mlEntry.pathString] = mlEntry.caster(propVal);
+ matchObj[mlEntry.pathString] = mlEntry.caster(propVal);
}
}
// Matching single value field
else {
- matcher[mlEntry.pathString] = mlEntry.caster(propVal);
+ matchObj[mlEntry.pathString] = mlEntry.caster(propVal);
}
}
}
- return matcher;
+ return matchObj;
};
-
+
+ /**
+ * Default maximum recursion depth for building flat matcher from Schema.tree. Protects against cycles.
+ * @api private
+ */
+ var maxDepth = 5;
/**
* Gets the maximum depth the FlatMatcher will recurse on Schema to build matcher from args
* @api public
@@ -250,7 +253,6 @@ var FlatMatcher = (function () {
var getMaxDepth = function () {
return maxDepth;
};
-
/**
* Sets the maximum depth the FlatMatcher will recurse on Schema to build matcher from args. Default is 5.
* Note: protects against cycles which will never exit recursion, so should be set to some reasonable value.
@@ -264,9 +266,14 @@ var FlatMatcher = (function () {
return {
plugin : function (schema, opts) {
- schema.static('getFlatMatcher', getMatcher);
- schema.static('setFlatMatcherMaxDepth', setMaxDepth);
- schema.static('getFlatMatcherMaxDepth', getMaxDepth);
+ schema['getMatchObj'] = getMatchObj;
+ // Store schema tree as child prop of method, so it's a closure
+ // that includes this property of the schema being decorated.
+ // Must do this because 'this' context of getMatchObj() calls are its
+ // own scope and schema.tree wouldn't otherwise be available.
+ schema.getMatchObj.tree = schema.tree;
+ schema['setMaxDepth'] = setMaxDepth;
+ schema['getMaxDepth'] = getMaxDepth;
}
};
}());
View
4 package.json
@@ -14,8 +14,8 @@
"mongoose": ">=1.4.0"
},
"devDependencies": {
- "should": "0.2.1",
- "cli-table" : "0.0.1"
+ "should": ">=0.2.1",
+ "cli-table" : ">=0.0.1"
},
"engines": {
"node": ">=0.2.0"
View
187 test/flatmatcher.test.js
@@ -1,8 +1,15 @@
+#!/usr/local/bin/node
+
var should = require('should')
, mongoose = require('mongoose')
+ , start = require('../node_modules/mongoose/test/common.js')
, Schema = mongoose.Schema
+ , Table = require('cli-table')
, ObjectId = Schema.ObjectId
- , flatMatcher = require('../flatmatcher.js');
+ , flatMatcher = require('../flatmatcher.js').plugin
+ // Modeled after tests in Mongoose
+ , startTime = Date.now()
+ , testCount = 0;
// Setup Schemas for testing. These are identical to those used by Mongoose tests
var Comments = new Schema();
@@ -40,45 +47,37 @@ BlogPost.virtual('titleWithAuthor')
this.set('author', split[1]);
});
-BlogPost.method('cool', function(){
- return this;
-});
-
-BlogPost.static('woot', function(){
- return this;
-});
-
mongoose.model('BlogPost', BlogPost);
-var collection = 'blogposts_' + Math.random();
-
// Use of should for testing, style of tests modeled directly after Mongoose tests
+// Note however that because there are just a few tests and not a great reason to add dependencies for this simple plugin
+// these tests simply call themselves when the module is run. At time of this writing, Mongoose uses expresso to run its tests.
module.exports = {
-
- 'model.matcher() should return correct MongodDB matcher object with dot notation and use it to retrieve data': function () {
- var db = start()
- , BlogPost = db.model('BlogPost', collection);
+
+ 'schema.getMatchObj() should return correct MongodDB matcher object with dot notation and use it to retrieve data': (function () {
+ var collection = 'flatmatcher_1'
+ , db = start()
+ , BlogPostModel = db.model('BlogPost', collection);
// Matches attributes with simple types at root level and nested attributes
var testTitle = 'Dot Notation Test';
var matchArgs = {title : testTitle, published : true, visitors : 5};
- var expectedMatcher = {title: testTitle, published: true, 'meta.visitors': 5};
-
+ var expectedMatchObj = {title: testTitle, published: true, 'meta.visitors': 5};
// Now actually insert some data and retrieve it with find() using the matcher
- var post = new BlogPost({
+ var post = new BlogPostModel({
title: testTitle,
published : true
});
- var matcher = null;
- var opts = {};
- // Use Mongoose #plugin() to decorate Schema with getMatcher() function
+ // Use Mongoose #plugin() to decorate Schema with getMatchObj() function
+ var matchObj = null;
+ var opts = {};
BlogPost.plugin(flatMatcher, opts);
// Call matcher to transform matchArgs JSON to flattened predicate
- matcher = BlogPost.getMatcher(matchArgs);
+ matchObj = BlogPost.getMatchObj(matchArgs);
// Test that we get the expected matcher JSON object for the flat matchArgs
// matchArgs cover literal types at root level, nested object and embedded array
- should.eql(expectedMatcher, matcher);
+ expectedMatchObj.should.eql(matchObj);
post.save( function (err) {
should.strictEqual(null, err);
@@ -86,7 +85,7 @@ module.exports = {
post.save( function (err) {
should.strictEqual(null, err);
// Use the matcher generated from flat args here to match nested obj, embedded array
- BlogPost.find(matcher, function (err, found) {
+ BlogPostModel.find(matchObj, function (err, found) {
should.strictEqual(null, err);
found = found[0];
// Test the values of the document returned, matching literals a base level
@@ -94,66 +93,72 @@ module.exports = {
found.get('title').valueOf().should.equal(testTitle);
found.get('published').valueOf().should.equal(true);
found.get('meta.visitors').valueOf().should.equal(5);
+
+ testCount += 1;
db.close();
});
});
});
- },
+ }()),
- 'model.matcher() should return correct MongodDB matcher object with dot notation for matching single simple values in embedded arrays and use it to retrieve data': function () {
- var db = start()
- , BlogPost = db.model('BlogPost', collection);
-
- var testTitle = 'Dot Notation Simple Embedded Array Test';
+ 'schema.getMatchObj() should return correct MongodDB matcher object with dot notation for matching single simple values in embedded arrays and use it to retrieve data': (function () {
+ var collection = 'flatmatcher_2'
+ , db = start()
+ , BlogPostModel = db.model('BlogPost', collection);
+
+ var testTitle = 'Dot Notation Simple Embedded Array Test';
var matchArgs = {title : testTitle, numbers : 4};
- // Numbers is array type, at root level, passed one value, so dot notation path for matching one value is simply the property name
- var expectedMatcher = {title: testTitle, numbers : 4};
- var post = new BlogPost({
+ // Numbers is array type, at root level, passed one value, so dot notation path for matching one value is simply the property name
+ var expectedMatchObj = {title: testTitle, numbers : 4};
+ var post = new BlogPostModel({
title: testTitle,
numbers : [4]
});
- var matcher = null;
- var opts = {};
-
+
+ var matchObj = null;
+ var opts = {};
BlogPost.plugin(flatMatcher, opts);
- matcher = BlogPost.getMatcher(matchArgs);
- should.eql(expectedMatcher, matcher);
+ matchObj = BlogPost.getMatchObj(matchArgs);
+ expectedMatchObj.should.eql(matchObj);
post.save( function (err) {
should.strictEqual(null, err);
- BlogPost.find(matcher, function (err, found) {
+ BlogPostModel.find(matchObj, function (err, found) {
should.strictEqual(null, err);
found = found[0];
found.get('title').valueOf().should.equal(testTitle);
found.get('numbers').should.have.length(1);
found.get('numbers')[0].should.equal(4);
- db.close();
+
+ testCount += 1;
+ db.close();
});
});
- },
+ }()),
+
+ 'schema.getMatchObj() should return correct MongodDB matcher object with $in notation for matching multiple simple values in embedded arrays and use it to retrieve data': (function () {
+ var collection = 'flatmatcher_3'
+ , db = start()
+ , BlogPostModel = db.model('BlogPost', collection);
- 'model.matcher() should return correct MongodDB matcher object with $in notation for matching multiple simple values in embedded arrays and use it to retrieve data': function () {
- var db = start()
- , BlogPost = db.model('BlogPost', collection);
-
- var testTitle = 'Dot Notation MutliValue Embedded Array Test';
+ var testTitle = 'Dot Notation MutliValue Embedded Array Test';
var matchArgs = {title : testTitle, numbers : [4, 5]};
- // Numbers is array type, at root level, passed multiple values, so $in notation for matching
- var expectedMatcher = {title: testTitle, numbers : {'$in' : [4, 5]}};
- var matcher = null;
- var opts = {};
- var post = new BlogPost({
+ // Numbers is array type, at root level, passed multiple values, so $in notation for matching
+ var expectedMatchObj = {title: testTitle, numbers : {'$in' : [4, 5]}};
+ var post = new BlogPostModel({
title: testTitle,
numbers : [4, 5, 6]
});
-
+
+ var matchObj = null;
+ var opts = {};
BlogPost.plugin(flatMatcher, opts);
- matcher = BlogPost.getMatcher(matchArgs);
- should.eql(expectedMatcher, matcher);
+ matchObj = BlogPost.getMatchObj(matchArgs);
+ expectedMatchObj.should.eql(matchObj);
post.save( function (err) {
should.strictEqual(null, err);
- BlogPost.find(matcher, function (err, found) {
+ BlogPostModel.find(matchObj, function (err, found) {
should.strictEqual(null, err);
found = found[0];
found.get('title').valueOf().should.equal(testTitle);
@@ -162,54 +167,80 @@ module.exports = {
found.get('numbers').indexOf(4).should.not.equal(-1);
found.get('numbers').indexOf(5).should.not.equal(-1);
found.get('numbers').indexOf(6).should.not.equal(-1);
+
+ testCount += 1;
db.close();
});
});
- },
-
- 'model.matcher() should return correct MongodDB matcher object with dot notation for object type in array and use it to retrieve data': function () {
- var db = start()
- , BlogPost = db.model('BlogPost', collection);
+ }()),
- var testTitle = 'Dot Notation Object Type Embedded Array Test';
+ 'schema.getMatchObj() should return correct MongodDB matcher object with dot notation for object type in array and use it to retrieve data': (function () {
+ var collection = 'flatmatcher_4'
+ , db = start()
+ , BlogPostModel = db.model('BlogPost', collection);
+
+ var testTitle = 'Dot Notation Object Type Embedded Array Test';
var matchArgs = {title : testTitle, 'comments.title' : 'Great comment!'};
- // Comments is array type, at root level, storing objects, passed one value, so dot notation path for matching one value is the nested property name
- var expectedMatcher = {title: testTitle, 'comments.title' : 'Great comment!'};
- var post = new BlogPost({
+ // Comments is array type, at root level, storing objects, passed one value, so dot notation path for matching one value is the nested property name
+ var expectedMatchObj = {title: testTitle, 'comments.title' : 'Great comment!'};
+ var post = new BlogPostModel({
title: testTitle,
comments : [{title : 'Great comment!', date : new Date(), body : 'I totally agree!', comments : []}]
});
- var matcher = null;
- var opts = {};
+ var matchObj = null;
+ var opts = {};
BlogPost.plugin(flatMatcher, opts);
- matcher = BlogPost.getMatcher(matchArgs);
- should.eql(expectedMatcher, matcher);
+ matchObj = BlogPost.getMatchObj(matchArgs);
+ expectedMatchObj.should.eql(matchObj);
post.save( function (err) {
should.strictEqual(null, err);
- BlogPost.find(matcher, function (err, found) {
+ BlogPostModel.find(matchObj, function (err, found) {
should.strictEqual(null, err);
found = found[0];
found.get('title').valueOf().should.equal(testTitle);
found.get('comments').should.have.length(1);
found.get('comments')[0].title.should.equal('Great comment!');
found.get('comments')[0].body.should.equal('I totally agree!');
+
+ testCount += 1;
db.close();
});
});
- },
-
- 'model.matcher() should return correct MongodDB matcher object with $in notation for matching multiple objects in embedded arrays': function () {
+ }()),
+
+ 'schema.getMatchObj() should return correct MongodDB matcher object with $in notation for matching multiple objects in embedded arrays': (function () {
var testTitle = 'Dot Notation MutliValue Objects in Embedded Array Test';
- var postDate = new Date();
+ var postDate = new Date();
// Matching array of embedded objects in embedded arrays requires "$in" operator and an array of literal objects that exactly match complete objects in the DB
var matchArgs = {title : testTitle, comments : [{title : 'First comment', date: postDate, body : 'No way', comments : [{title: 'inside'}]}]};
// Numbers is array type, at root level, passed multiple values, so $in notation for matching
- var expectedMatcher = {title: testTitle, comments : {'$in' : [{title : 'First comment', date : postDate, body : 'No way', comments : [{title: 'inside'}]}]}};
+ var expectedMatchObj = {title: testTitle, comments : {'$in' : [{title : 'First comment', date : postDate, body : 'No way', comments : [{title: 'inside'}]}]}};
+ var opts = {};
BlogPost.plugin(flatMatcher, opts);
- var matcher = BlogPost.getMatcher(matchArgs);
-
- should.eql(expectedMatcher, matcher);
- }
+ var matchObj = BlogPost.getMatchObj(matchArgs);
+ expectedMatchObj.should.eql(matchObj);
+
+ testCount += 1;
+ }())
};
+
+// Pathetic violation of DRY to use near-identical code as found
+// in '../node_modules/mongoose/test/common.js', but that
+// handler isn't assigned to a var there and isn't exported.
+process.on('exit', function(){
+ var table = new Table({
+ head: ['Stat', 'Time (ms)']
+ , colWidths: [33, 15]
+ });
+
+ table.push(
+ ['mongoose-flatmatcher Tests Run', testCount]
+ , ['Time elapsed', Date.now() - startTime]
+ );
+
+ console.log(table.toString());
+});
+
+

0 comments on commit 55f5d14

Please sign in to comment.
Something went wrong with that request. Please try again.