22
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Mongoose でネストした populate の書き方

Last updated at Posted at 2013-05-16

Mongoose の 3.6 から ネストした populate に対応したそうなので動作確認を試みたのですが、GitHub の Issue で問い合わせをするまでやり方が分かりませんでした。

他の方もハマるかもしれないので、ここにまとめておきます。

誤った書き方

最初、以下のようなコードを書いていました。

package.json
{
  "name": "mongoose-populate-test",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "mongoose": "3.6.4",
    "async": "0.1.22"
  },
  "engines": {
    "node": ">=0.10.2",
    "npm": ">=1.2.15"
  },
  "scripts": {
    "start": "node test.js"
  }
}
test.js
var assert = require('assert');
var mongoose = require('mongoose');
var async = require('async');
var Schema = mongoose.Schema;
var ObjectId = mongoose.Types.ObjectId;

console.log('\n===========');
console.log('    mongoose version: %s', mongoose.version);
console.log('========\n\n');

var dbname = 'testing_populateAdInfinitum';
console.log('dbname: %s', dbname);
mongoose.connect('localhost', dbname);
mongoose.connection.on('error', function () {
  console.error('connection error', arguments);
});

var phone = new Schema({
    name: String
});
var Phone = mongoose.model('Phone', phone);

var user = new Schema({
    name: String
  , phone: { type: Schema.ObjectId, ref: 'Phone' }
});
var User = mongoose.model('User', user);

var blogpost = Schema({
    title: String
  , tags: [String]
  , author: { type: Schema.ObjectId, ref: 'User' }
});
var BlogPost = mongoose.model('BlogPost', blogpost);

var createPhones = function(callback) {
  var phoneIds = [new ObjectId, new ObjectId];
  var phones = [];

  phones.push({
    _id: phoneIds[0]
    , name: 'iPhone 5'
  });
  phones.push({
    _id: phoneIds[1]
    , name: 'GALAXY S'
  });

  Phone.create(phones, function(err, docs) {
    assert.ifError(err);
    callback(null, phoneIds);
  });
};

var createUsers = function(phoneIds, callback) {
  var userIds = [new ObjectId, new ObjectId];
  var users = [];

  users.push({
    _id: userIds[0]
    , name: 'mary'
    , phone: phoneIds[0]
  });
  users.push({
    _id: userIds[1]
    , name: 'bob'
    , phone: phoneIds[1]
  });

  User.create(users, function(err, docs) {
    assert.ifError(err);
    callback(null, userIds);
  });
};

var createBlogPosts = function(userIds, callback) {
  var blogposts = [];

  blogposts.push({ title: 'blog 0', tags: ['fun', 'cool'], author: userIds[0] });
  blogposts.push({ title: 'blog 1', tags: ['cool'], author: userIds[1] });

  BlogPost.create(blogposts, function(err, docs) {
    assert.ifError(err);
    callback(null);
  });
};

var findFunBlogPosts = function(callback) {
  BlogPost.find({ tags: 'fun' }).lean().populate('author').exec(function (err, docs) {
    assert.ifError(err);

    var opts = {
        path: 'author.phone'
      , select: 'name'
    };

    BlogPost.populate(docs, opts, function(err, docs) {
      assert.ifError(err);
      docs.forEach(function(doc) {
        console.log(doc);
      });
      callback(null);
    });
  });
};

mongoose.connection.on('open', function () {
  async.waterfall([
      createPhones, 
      createUsers, 
      createBlogPosts,  
      findFunBlogPosts
    ], 
    function(err, result) {
      if (err) {
        console.error(err.stack);
      }
      mongoose.connection.db.dropDatabase(function () {
        mongoose.connection.close();
      });
    });
});
OUTPUT
===========
    mongoose version: 3.6.4
========


dbname: testing_populateAdInfinitum
{ title: 'blog 0',
  author:
   { _id: 5194634c2860ba0824000003,
     name: 'mary',
     phone: null,
     __v: 0 },
  _id: 5194634c2860ba0824000005,
  tags: [ 'fun', 'cool' ],
  __v: 0 }

OUTPUT の author.phone が以下のように出力されることを期待していましたが、上記のとおり、該当の箇所は null として出力されてしまいました。

phone {
  _id: xxxxxxxxxxxxxxxx,
  name: 'iPhone 5'
}

正しい書き方

問題のあるコードは以下の箇所です。

var findFunBlogPosts = function(callback) {
  BlogPost.find({ tags: 'fun' }).lean().populate('author').exec(function (err, docs) {
    assert.ifError(err);

    var opts = {
        path: 'author.phone'
      , select: 'name'
    };

    BlogPost.populate(docs, opts, function(err, docs) {
      assert.ifError(err);
      docs.forEach(function(doc) {
        console.log(doc);
      });
      callback(null);
    });
  });
};

この部分を以下のどちらかに書き換えると、正常に動作します。

1
var findFunBlogPosts = function(callback) {
  BlogPost.find({ tags: 'fun' }).lean().populate('author').exec(function (err, docs) {
    assert.ifError(err);

    var opts = {
      path: 'author.phone',
      select: 'name',
      model: Phone
    };

    User.populate(docs, opts, function(err, docs) {
      assert.ifError(err);
      docs.forEach(function(doc) {
        console.log(doc);
      });
      callback(null);
    });
  });
};
2
var findFunBlogPosts = function(callback) {
  BlogPost.find({ tags: 'fun' }).lean().populate('author').exec(function (err, docs) {
    assert.ifError(err);

    var opts = {
      path: 'author.phone',
      select: 'name',
    };

    Phone.populate(docs, opts, function(err, docs) {
      assert.ifError(err);
      docs.forEach(function(doc) {
        console.log(doc);
      });
      callback(null);
    });
  });
};

参照

22
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?