Sails.js заполняют вложенные ассоциации
У меня возник вопрос относительно ассоциаций в Sails.js версии 0.10-rc5. Я создаю приложение, в котором несколько моделей связаны друг с другом, и я пришел к точке, где мне нужно как-то связать ассоциации.
Там три части:
Сначала там что-то вроде сообщения в блоге, которое написано пользователем. В сообщении в блоге я хочу показать соответствующую информацию о пользователе, такую как имя пользователя. Теперь все отлично работает. До следующего шага: я пытаюсь показать комментарии, связанные с этим сообщением.
Комментарии представляют собой отдельную модель под названием Комментарий. Каждый из них также имеет автора (пользователя), связанного с ним. Я могу легко показать список комментариев, хотя, когда я хочу отображать информацию пользователя, связанную с комментарием, я не могу понять, как заполнить комментарий информацией пользователя.
В моем контроллере я пытаюсь сделать что-то вроде этого:
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.
});
В действии "show" моего сообщения я пытаюсь получить информацию, подобную этой (упрощенной):
<ul>
<%- _.each(post.comments, function(comment) { %>
<li>
<%= comment.user.name %>
<%= comment.description %>
</li>
<% }); %>
</ul>
Однако comment.user.name будет undefined. Если я попытаюсь просто получить доступ к свойству user, например comment.user, он покажет его ID. Который говорит мне, что он не автоматически заполняет информацию пользователя комментарию, когда я связываю комментарий с другой моделью.
Любые любые идеалы, чтобы решить это правильно:)?
Спасибо заранее!
P.S.
Для пояснения, вот как я в основном установил ассоциации в разных моделях:
// 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'
}
Ответы
Ответ 2
В настоящий момент нет встроенного способа заполнения вложенных ассоциаций. Лучше всего использовать 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);
}
);
Это не так хорошо, как вложенная совокупность (которая работает, но, вероятно, не для v0.10), но на яркой стороне она фактически достаточно эффективна.
Ответ 3
Стоит сказать, что запрос на добавление для добавления вложенного населения: https://github.com/balderdashy/waterline/pull/1052
Запрос Pull не объединяется в данный момент, но вы можете использовать его, устанавливая его непосредственно с помощью
npm i Atlantis-Software/waterline#deepPopulate
С ним вы можете сделать что-то вроде .populate('user.comments ...)'
.
Ответ 4
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);
}
);
Ответ 5
Вы можете использовать async библиотеку, которая очень проста и понятна для понимания. Для каждого комментария, связанного с сообщением, вы можете заполнить много полей, как вы хотите, выделенными задачами, выполнить их параллельно и получить результаты, когда все задачи будут выполнены. Наконец, вы должны вернуть окончательный результат.
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);
});
});
Ответ 6
Я создал модуль NPM для этого типа nested-pop. Вы можете найти его по ссылке ниже.
https://www.npmjs.com/package/nested-pop
Используйте его следующим образом.
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;
);