Mocking DBRefs in Mongoose and nodeunit

14 April 2011   0 comments   MongoDB, Javascript

Mind That Age!

This blog post is 6 years old! Most likely, its content is outdated. Especially if it's technical.

Powered by Fusion×

Because this took me a long time to figure out I thought I'd share it with people in case other people get stuck on the same problem.

The problem is that Mongoose doesn't support DBRefs. A DBRef is just a little sub structure with a two keys: $ref and $id where $id is an ObjectId instance. Here's what it might look like on the mongodb shell:

> db.questions.findOne();
{
       "_id" : ObjectId("4d64322a6da68156b8000001"),
       "author" : {
               "$ref" : "users",
               "$id" : ObjectId("4d584fb86da681668b000000")
       },
       "text" : "Foo?",
       ...
       "answer" : "Bar"
       "genre" : {
               "$ref" : "question_genres",
               "$id" : ObjectId("4d64322a6da68156b8000000")
       }
}

DBRefs are very convenient because various wrappers on drivers can do automatic cross-fetching based on this. For example, with MongoKit I can do this:

for question in db.Question.find():
   print question.author.first_name

If we didn't have DBRefs you'd have to do this:

for question in db.Question.find():
   author = db.Authors.findOne({'_id': question.author})
   print author.first_name

Anyway, the problem Mongoose has is that it doesn't support DBRefs so when you define its structure you have to do this:

var QuestionSchema = new mongoose.Schema({
  text     : String
  , answer      : String
  ...
  , genre: {},
  , author : {}
});
mongoose.model('Question', QuestionSchema);
var Question = mongoose.model('Question', 'questions');

Now, that sucks but I can learn to live with it. Giving this schema here's how you can work with the defined model from an existing database:

Question.find({}, function(err, docs) {
  docs.forEach(function(each) {
     each.findAuthor(function(err, item) {
        console.log(item.doc.first_name);
     });     
  });
});

(note: I have no idea with this "doc" struct is but perhaps the gods of Mongoose can explain that one)

So, given that I can't work with DBRefs in Mongoose; how do to mock it? Here's how:

    var author = new models.Author();
    author.username = "peter";
    author.save(function(err) {
        var question = new models.Question();
        // fake a dbref
        question.author = {
           "$ref" : "users",
           "oid" : user._id
        };
        question.save(function(err) {
           question.findAuthor(function(err, u) {
              assert.ok(u);
              assert.ok(!err);
              test.equal(u.doc.first_name, 'peter');
              test.done();
           });
        });
     });

That's the magic. This way you can pretend, in your tests, that you have objects with proper DBRefs. It feels strangely convoluted and hackish but I'm sure once I've understood this better there might be a better way.

Perhaps I could have spent the time it took to figure this out and to write this blog I could have stepped up and written a DBRef plugin for Mongoose.

Follow @peterbe on Twitter

Comments

Thank you for posting a comment

Your email will never ever be published


Related posts

Previous:
TornadoGists.org - launched and ready! 06 April 2011
Next:
maxlength_countdown() - a useful jQuery plugin for showing characters left 01 May 2011
Related by Keyword:
Bye bye httpretty. Welcome back good old mock! 19 March 2015
Fastest database for Tornado 09 October 2013
Persistent caching with fire-and-forget updates 14 December 2011
Optimization story involving something silly I call "dict+" 13 June 2011
MongoUK 2011 - London conference all about MongoDB 21 March 2011
Related by Text:
json-schema-reducer 02 August 2016
Optimization story involving something silly I call "dict+" 13 June 2011
Strange socket related error with supervisord 05 April 2011
String comparison function in Python (alpha) 22 December 2007
How to slice a rune in Go 16 March 2015