Skip to content

Commit

Permalink
toConstructor() working and tests written
Browse files Browse the repository at this point in the history
passing all tests. Additional fixes for dealing with `sort` passed via
`setOptions` Moral of the story: the storage format for sort sucks
  • Loading branch information
ebensing authored and aheckmann committed Aug 19, 2013
1 parent 9142d24 commit 74f0d4d
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 3 deletions.
28 changes: 25 additions & 3 deletions lib/query.js
Expand Up @@ -36,6 +36,7 @@ function Query(conditions, options, model, collection) {
} else {
// this is the case where we have a CustomQuery, we need to check if we got
// options passed in, and if we did, merge them in

if (options) {
var keys = Object.keys(options);
for (var i=0; i < keys.length; i++) {
Expand All @@ -48,6 +49,7 @@ function Query(conditions, options, model, collection) {
if (collection) {
this.mongooseCollection = collection;
}

if (model) {
this.model = model;
}
Expand All @@ -59,7 +61,7 @@ function Query(conditions, options, model, collection) {
}

// inherit mquery
mquery.call(this, collection, options);
mquery.call(this, this.mongooseCollection, options);

if (conditions) {
this.find(conditions);
Expand Down Expand Up @@ -136,7 +138,16 @@ Query.prototype.toConstructor = function toConstructor () {
var p = CustomQuery.prototype;

p.options = {};
p.setOptions(this.options);

// this is an ugly hack to deal with the terrible way that sort information
// is internally stored. [[field, 1]] syntax is causing trouble when we are
// calling `sort` via `apply` and the arguments are getting passed
// incorrectly
var opts = utils.clone(this.options);
if (opts.sort) {
opts.sort = [opts.sort];
}
p.setOptions(opts);

p.op = this.op;
p._conditions = utils.clone(this._conditions);
Expand Down Expand Up @@ -838,6 +849,16 @@ Query.prototype.setOptions = function (options, overwrite) {
return this;
}

// more terrible hacks to deal with the awful storage format of `sort`!
// This mainly comes up due to weird stuff with `toConstructor()`
if (options.sort) {
if (Array.isArray(options.sort)) {
if (Array.isArray(options.sort[0]) && !Array.isArray(options.sort[0][0])) {
options.sort = [options.sort];
}
}
}

return Query.base.setOptions.call(this, options);
}

Expand Down Expand Up @@ -1287,8 +1308,9 @@ Query.prototype.sort = function (arg) {
// time to deal with the terrible syntax
for (var i=0; i < arg.length; i++) {
if (!Array.isArray(arg[i])) throw new Error("Invalid sort() argument.");
nArg[arg[i][0]] = argi[i][1];
nArg[arg[i][0]] = arg[i][1];
}

} else {
nArg = arg;
}
Expand Down
148 changes: 148 additions & 0 deletions test/query.toconstructor.test.js
@@ -0,0 +1,148 @@

var start = require('./common')
, mongoose = start.mongoose
, DocumentObjectId = mongoose.Types.ObjectId
, Schema = mongoose.Schema
, assert = require('assert')
, random = require('../lib/utils').random
, Query = require('../lib/query');


var Comment = new Schema({
text: String
});

var Product = new Schema({
tags: {} // mixed
, array: Array
, ids: [Schema.ObjectId]
, strings: [String]
, numbers: [Number]
, comments: [Comment]
, title : String
});
var prodName = 'Product' + random();
var cName = 'Comment' + random();
mongoose.model(prodName, Product);
mongoose.model(cName, Comment);

describe('Query: ', function(){
describe('toConstructor', function () {
it('creates a query', function (done) {
var db = start();
var Product = db.model(prodName);
var prodQ = Product.find({ title : /test/ }).toConstructor();

assert.ok(prodQ() instanceof Query);

done();
});

it('copies all the right values', function (done) {
var db = start();
var Product = db.model(prodName);

var prodQ = Product.update({ title: /test/ }, { title : 'blah' });

var prodC = prodQ.toConstructor();

assert.deepEqual(prodQ._conditions, prodC()._conditions);
assert.deepEqual(prodQ._fields, prodC()._fields);
assert.deepEqual(prodQ._update, prodC()._update);
assert.equal(prodQ._path, prodC()._path);
assert.equal(prodQ._distinct, prodC()._distinct);
assert.deepEqual(prodQ._collection, prodC()._collection);
assert.deepEqual(prodQ.model, prodC().model);
assert.deepEqual(prodQ.mongooseCollection, prodC().mongooseCollection);
assert.deepEqual(prodQ._mongooseOptions, prodC()._mongooseOptions);
done();
});

it('gets expected results', function (done) {
var db = start();
var Product = db.model(prodName);
Product.create({ title : 'this is a test' }, function (err, p) {
assert.ifError(err);
var prodC = Product.find({ title : /test/ }).toConstructor();

prodC().exec(function (err, results) {
assert.ifError(err);
assert.equal(results.length, 1);
assert.equal(p.title, results[0].title);
done();
});
});
});

it('can be re-used multiple times', function (done) {
var db = start();
var Product = db.model(prodName);

Product.create([{ title : 'moar thing' }, {title : 'second thing' }], function (err, prod) {
assert.ifError(err);

var prodC = Product.find({ title : /thing/ }).toConstructor();

prodC().exec(function (err, results) {
assert.ifError(err);

assert.equal(results.length, 2);
prodC().find({ _id : prod.id }).exec(function (err, res) {
assert.ifError(err);
assert.equal(res.length, 1);

prodC().exec(function (err, res) {
assert.ifError(err);
assert.equal(res.length, 2);
done();
});
});
});

});
});

it('options get merged properly', function(done){
var db = start();
var Product = db.model(prodName);

var prodC = Product.find({ title : /blah/ }).setOptions({ sort : 'title', lean : true });
prodC = prodC.toConstructor();

var nq = prodC(null, { limit : 3 });
assert.deepEqual(nq._mongooseOptions, { lean : true, limit : 3 });
assert.deepEqual(nq.options, { sort : [['title', 1]], limit : 3 });
done();
})

it('creates subclasses of mquery', function(done) {
var db = start();
var Product = db.model(prodName);

var opts = { safe: { w: 'majority' }, readPreference: 'p' };
var match = { title: 'test', count: { $gt: 101 }};
var select = { name: 1, count: 0 }
var update = { $set: { title : 'thing' }};
var path = 'title';

var q = Product.update(match, update);
q.select(select);
q.where(path);
q.setOptions(opts);
q.find();

var M = q.toConstructor();
var m = M();

assert.ok(m instanceof Query);
assert.deepEqual(opts, m.options);
assert.deepEqual(match, m._conditions);
assert.deepEqual(select, m._fields);
assert.deepEqual(update, m._update);
assert.equal(path, m._path);
assert.equal('find', m.op);
done();
})
});
});

0 comments on commit 74f0d4d

Please sign in to comment.