Подтвердить что ты не робот

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'
}
4b9b3361

Ответ 1

Или вы можете использовать встроенную функцию 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); });

Некоторое объяснение:

Я использую для работы с массивами. Для получения дополнительной информации см. Обратите внимание на возвращаемые значения внутри первой "затем" функции, те объекты "[post, commentsUsers]" внутри массива также являются "обетованными" объектами. Это означает, что они не содержали данные о значении при их первом запуске, пока не получили значение. Таким образом, функция "распространения" будет ожидать, когда значение атрибута придет и продолжит выполнение остальных вещей.

Ответ 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;
);