node.js Sails.js 填充嵌套关联
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23446484/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sails.js populate nested associations
提问by Lars Dol
I've got myself a question regarding associations in Sails.js version 0.10-rc5. I've been building an app in which multiple models are associated to one another, and I've arrived at a point where I need to get to nest associations somehow.
我有一个关于 Sails.js 0.10-rc5 版中关联的问题。我一直在构建一个应用程序,其中多个模型相互关联,我已经到了需要以某种方式嵌套关联的地步。
There's three parts:
有三部分:
First there's something like a blog post, that's being written by a user. In the blog post I want to show the associated user's information like their username. Now, everything works fine here. Until the next step: I'm trying to show comments which are associated with the post.
首先是用户撰写的博客文章之类的内容。在博客文章中,我想显示关联用户的信息,例如他们的用户名。现在,这里一切正常。直到下一步:我正在尝试显示与帖子相关的评论。
The comments are a separate Model, called Comment. Each of which also has an author (user) associated with it. I can easily show a list of the Comments, although when I want to display the User's information associated with the comment, I can't figure out how to populate the Comment with the user's information.
注释是一个单独的模型,称为注释。每一个都有一个与之相关的作者(用户)。我可以轻松显示评论列表,但当我想显示与评论相关的用户信息时,我无法弄清楚如何用用户信息填充评论。
In my controller i'm trying to do something like this:
在我的控制器中,我试图做这样的事情:
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments') // I want to populate this comment with .populate('user') or something
.exec(function(err, post) {
// Handle errors & render view etc.
});
In my Post's 'show' action i'm trying to retrieve the information like this (simplified):
在我的帖子的“显示”操作中,我试图检索这样的信息(简化):
<ul>
<%- _.each(post.comments, function(comment) { %>
<li>
<%= comment.user.name %>
<%= comment.description %>
</li>
<% }); %>
</ul>
The comment.user.name will be undefined though. If I try to just access the 'user' property, like comment.user, it'll show it's ID. Which tells me it's not automatically populating the user's information to the comment when I associate the comment with another model.
但是 comment.user.name 将是未定义的。如果我尝试只访问“用户”属性,例如 comment.user,它会显示它的 ID。这告诉我,当我将评论与另一个模型相关联时,它不会自动将用户信息填充到评论中。
Anyone any ideals to solve this properly :)?
任何人有任何理想来正确解决这个问题:)?
Thanks in advance!
提前致谢!
P.S.
聚苯乙烯
For clarification, this is how i've basically set up the associations in different models:
为了澄清起见,这就是我在不同模型中基本上设置关联的方式:
// User.js
posts: {
collection: 'post'
},
hours: {
collection: 'hour'
},
comments: {
collection: 'comment'
}
// Post.js
user: {
model: 'user'
},
comments: {
collection: 'comment',
via: 'post'
}
// Comment.js
user: {
model: 'user'
},
post: {
model: 'post'
}
回答by Fermin Yang
Or you can use the built-in Blue BirdPromise feature to make it. (Working on [email protected])
或者您可以使用内置的Blue BirdPromise 功能来实现。(在 [email protected] 上工作)
See the codes below:
请参阅以下代码:
var _ = require('lodash');
...
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.then(function(post) {
var commentUsers = User.find({
id: _.pluck(post.comments, 'user')
//_.pluck: Retrieves the value of a 'user' property from all elements in the post.comments collection.
})
.then(function(commentUsers) {
return commentUsers;
});
return [post, commentUsers];
})
.spread(function(post, commentUsers) {
commentUsers = _.indexBy(commentUsers, 'id');
//_.indexBy: Creates an object composed of keys generated from the results of running each element of the collection through the given callback. The corresponding value of each key is the last element responsible for generating the key
post.comments = _.map(post.comments, function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
res.json(post);
})
.catch(function(err) {
return res.serverError(err);
});
Some explanation:
一些解释:
- I'm using the Lo-Dashto deal with the arrays. For more details, please refer to the Official Doc
- Notice the return values inside the first "then" function, those objects "[post, commentUsers]" inside the array are also "promise" objects. Which means that they didn't contain the value data when they first been executed, until they got the value. So that "spread" function will wait the acture value come and continue doing the rest stuffs.
回答by sgress454
At the moment, there's no built in way to populate nested associations. Your best bet is to use async to do a mapping:
目前,没有内置的方法来填充嵌套关联。最好的办法是使用 async 进行映射:
async.auto({
// First get the post
post: function(cb) {
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.exec(cb);
},
// Then all of the comment users, using an "in" query by
// setting "id" criteria to an array of user IDs
commentUsers: ['post', function(cb, results) {
User.find({id: _.pluck(results.post.comments, 'user')}).exec(cb);
}],
// Map the comment users to their comments
map: ['commentUsers', function(cb, results) {
// Index comment users by ID
var commentUsers = _.indexBy(results.commentUsers, 'id');
// Get a plain object version of post & comments
var post = results.post.toObject();
// Map users onto comments
post.comments = post.comments.map(function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
return cb(null, post);
}]
},
// After all the async magic is finished, return the mapped result
// (or an error if any occurred during the async block)
function finish(err, results) {
if (err) {return res.serverError(err);}
return res.json(results.map);
}
);
It's not as pretty as nested population (which is in the works, but probably not for v0.10), but on the bright side it's actually fairly efficient.
它不像嵌套人口那么漂亮(正在开发中,但可能不适用于 v0.10),但从好的方面来说,它实际上相当有效。
回答by Jam Risser
I created an NPM module for this called nested-pop. You can find it at the link below.
我为此创建了一个 NPM 模块,称为nested-pop。您可以在下面的链接中找到它。
https://www.npmjs.com/package/nested-pop
https://www.npmjs.com/package/nested-pop
Use it in the following way.
按照以下方式使用它。
var nestedPop = require('nested-pop');
User.find()
.populate('dogs')
.then(function(users) {
return nestedPop(users, {
dogs: [
'breed'
]
}).then(function(users) {
return users
}).catch(function(err) {
throw err;
});
}).catch(function(err) {
throw err;
);
回答by Glen Swift
Worth saying there's a pull request to add nested population: https://github.com/balderdashy/waterline/pull/1052
值得一提的是,有一个添加嵌套人口的拉取请求:https: //github.com/balderdashy/waterline/pull/1052
Pull request isn't merged at the moment but you can use it installing one directly with
拉取请求目前未合并,但您可以使用它直接安装一个
npm i Atlantis-Software/waterline#deepPopulate
With it you can do something like .populate('user.comments ...)'.
有了它,你可以做类似的事情.populate('user.comments ...)'。
回答by Aravind Kumar
sails v0.11 doesn't support _.pluck and _.indexBy use sails.util.pluck and sails.util.indexBy instead.
async.auto({
// First get the post
post: function(cb) {
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.exec(cb);
},
// Then all of the comment users, using an "in" query by
// setting "id" criteria to an array of user IDs
commentUsers: ['post', function(cb, results) {
User.find({id:sails.util.pluck(results.post.comments, 'user')}).exec(cb);
}],
// Map the comment users to their comments
map: ['commentUsers', function(cb, results) {
// Index comment users by ID
var commentUsers = sails.util.indexBy(results.commentUsers, 'id');
// Get a plain object version of post & comments
var post = results.post.toObject();
// Map users onto comments
post.comments = post.comments.map(function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
return cb(null, post);
}]
},
// After all the async magic is finished, return the mapped result
// (or an error if any occurred during the async block)
function finish(err, results) {
if (err) {return res.serverError(err);}
return res.json(results.map);
}
);
回答by Mariusz Wiazowski
You could use asynclibrary which is very clean and simple to understand. For each comment related to a post you can populate many fields as you want with dedicated tasks, execute them in parallel and retrieve the results when all tasks are done. Finally, you only have to return the final result.
您可以使用非常干净且易于理解的异步库。对于与帖子相关的每条评论,您可以根据需要使用专用任务填充多个字段,并行执行它们并在所有任务完成后检索结果。最后,您只需返回最终结果。
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments') // I want to populate this comment with .populate('user') or something
.exec(function (err, post) {
// populate each post in parallel
async.each(post.comments, function (comment, callback) {
// you can populate many elements or only one...
var populateTasks = {
user: function (cb) {
User.findOne({ id: comment.user })
.exec(function (err, result) {
cb(err, result);
});
}
}
async.parallel(populateTasks, function (err, resultSet) {
if (err) { return next(err); }
post.comments = resultSet.user;
// finish
callback();
});
}, function (err) {// final callback
if (err) { return next(err); }
return res.json(post);
});
});
回答by har-wradim
As of sailsjs 1.0 the "deep populate" pull requestis still open, but the following async function solution looks elegant enough IMO:
从sailsjs 1.0 开始,“深度填充”拉取请求仍处于打开状态,但以下异步函数解决方案在 IMO 中看起来足够优雅:
const post = await Post
.findOne({ id: req.param('id') })
.populate('user')
.populate('comments');
if (post && post.comments.length > 0) {
const ids = post.comments.map(comment => comment.id);
post.comments = await Comment
.find({ id: commentId })
.populate('user');
}
回答by Adim Victor
Granted this is an old question, but a much simpler solution would be to loop over the comments,replacing each comment's 'user' property (which is an id) with the user's full detail using async await.
当然,这是一个老问题,但更简单的解决方案是循环评论,使用异步等待将每个评论的“用户”属性(这是一个 ID)替换为用户的完整详细信息。
async function getPost(postId){
let post = await Post.findOne(postId).populate('user').populate('comments');
for(let comment of post.comments){
comment.user = await User.findOne({id:comment.user});
}
return post;
}
Hope this helps!
希望这可以帮助!

