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);
});
});
};
参照
- Can't make nested populate in new mongoose 3.6rc0