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.

Comments

Your email will never ever be published.

Previous:
TornadoGists.org - launched and ready! April 6, 2011 Python, Tornado
Next:
maxlength_countdown() - a useful jQuery plugin for showing characters left May 1, 2011 JavaScript
Related by category:
How to SSG a Vite SPA April 26, 2025 JavaScript
Switching from Next.js to Vite + wouter July 28, 2023 JavaScript
An ideal pattern to combine React Router with TanStack Query November 18, 2024 JavaScript
get in JavaScript is the same as property in Python February 13, 2025 JavaScript
Related by keyword:
Mocking os.stat in Python November 8, 2009 Python
Fastest database for Tornado October 9, 2013 Python, Tornado
Speed test between django_mongokit and postgresql_psycopg2 March 9, 2010 Python, Django
mongoengine vs. django-mongokit May 24, 2010 Python, Django