web-dev-qa-db-ja.com

Sails.jsはネストされた関連付けを設定します

Sails.jsバージョン0.10-rc5の関連付けに関する質問があります。複数のモデルが相互に関連付けられているアプリを作成してきましたが、何らかの方法で関連付けをネストする必要があるポイントに到達しました。

3つの部分があります。

まず、ブログ投稿のようなものがあり、それはユーザーによって書かれています。ブログの投稿では、ユーザー名などの関連するユーザーの情報を表示します。ここで、すべてが正常に機能します。次のステップまで:投稿に関連付けられているコメントを表示しようとしています。

コメントは、コメントと呼ばれる別個のモデルです。それぞれには、作成者(ユーザー)も関連付けられています。コメントのリストを簡単に表示できますが、コメントに関連付けられているユーザーの情報を表示したい場合、コメントにユーザーの情報を入力する方法がわかりません。

私のコントローラーでは、次のようなことをしようとしています:

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.
  });

私の投稿の「表示」アクションで、このような情報を取得しようとしています(簡略化):

<ul> 
  <%- _.each(post.comments, function(comment) { %>
    <li>
      <%= comment.user.name %>
      <%= comment.description %>
    </li>
  <% }); %>
</ul>

ただし、comment.user.nameは未定義です。 comment.userなどの 'user'プロパティにアクセスしようとすると、そのIDが表示されます。コメントを別のモデルに関連付けると、ユーザーの情報がコメントに自動的に入力されないことがわかります。

誰でもこれを適切に解決するための理想:)?

前もって感謝します!

追伸.

明確にするために、これは異なるモデルで基本的に関連付けを設定した方法です:

// 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'
}
48
Darkstra

または、組み込みの Blue Bird Promise機能を使用して作成できます。 ([email protected]の作業)

以下のコードを参照してください。

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);
  });

いくつかの説明:

  1. Lo-Dash を使用して配列を処理しています。詳細については、 公式ドキュメント を参照してください。
  2. 最初の「then」関数内の戻り値に注意してください。配列内の「[post、commentUsers]」オブジェクトも「promise」オブジェクトです。つまり、値が取得されるまで、最初に実行されたときに値データが含まれていませんでした。そのため、「スプレッド」機能は、アクター値が来るのを待って、残りの処理を続けます。
45
Fermin Yang

現時点では、ネストされた関連付けを作成するための組み込みの方法はありません。あなたの最善の策は、非同期を使用してマッピングを行うことです:

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向けではないかもしれません)、明るい面では実際にはかなり効率的です。

26
sgress454
 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);
   }
);
3
Aravind Kumar

nested-popというNPMモジュールを作成しました。以下のリンクで見つけることができます。

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;
);
3
Jam Risser

ネストされた人口を追加するプルリクエストがあると言う価値はあります: https://github.com/balderdashy/waterline/pull/1052

プルリクエストは現時点ではマージされていませんが、それを使用して直接インストールすることができます

_npm i Atlantis-Software/waterline#deepPopulate
_

これにより、.populate('user.comments ...)'のようなことができます。

3
Glen Swift

Sailsjs 1.0の時点では、 "deep populate" pull request はまだ開いていますが、次の非同期関数ソリューションは十分にエレガントな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');
}
2
har-wradim

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);
            });
        });
2

これは古い質問ですが、より簡単な解決策は、コメントをループして、非同期コメントを使用して各コメントの「ユーザー」プロパティ(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;
}

お役に立てれば!

0
Adim Victor