概要
Mongo/Mongoose で配列要素を populate した後に、要素のフィールドでソートする方法について。
コードサンプルは JavaScript で書いています。
サンプル
以下のようなデータを想定します。
// user { id: "foo" comments: ["1", "2"] // comment コレクションへの参照 ID } // comment { id: "1", text: "one one one", created_at: "2019/01/01" } { id: "2", text: "two two two" created_at: "2019/01/02" }
populate すると
普通に populate すると以下のようなコードになると思います。
const user = User .find({ id: "foo" }) .populate("comments")
実行結果は以下の通り。
{ id: "foo" comments: [ { id: "1", text: "one one one", created_at: "2019/01/01" } { id: "2", text: "two two two" created_at: "2019/01/02" } ] }
コメントへの参照が解決されています。
配列要素をそのフィールドに応じてソート
しかしクエリ時点で配列要素をソートしたい時があります。
例えば、comments
をそのフィールドである created_at
の降順にソートする場合、以下のように書けます。
const user = User,aggregate[ { $match: { id: "foo" } }, { $lookup: { from: 'comments', localField: 'comments', foreignField: '_id', as: 'items', }, }, { $unwind: '$items' }, { $sort: { 'items.created_at': 1 } }, { $group: { _id: '$id', id: { $first: '$id' }, comments: { $addToSet: '$items' }, }, }, ]
配列要素のソートは、$aggregation
の中で $unwind
と $sort
を組み合わせると実現できます。
ただし、元の user
データに差し戻すためにはいったんデータ名を $item
のように置き換えておく必要がありました。そのうえで、 $group
を使って最終形データを生成します。
また、 $aggregation
を使ったので、id マッチングは $match
を使用。
実行結果は以下の通り。配列要素 comments
がそのフィールドであるcreated_at
でソートされています。
{ id: "foo" comments: [ { id: "2", text: "two two two", created_at: "2019/01/02" } { id: "1", text: "one one one" created_at: "2019/01/01" } ] }
おしまい
やりたいことは実現できましたが、かなり複雑になってしまいました。実装方法を調べましたが、なかなか見つからずに試行錯誤しているうちに上述の方法に当たりました。
実のところ $aggregation
にはまだ馴染みが薄く、きちんと理解しているかは自信がありません…。ベターな方法がありましたら、コメント欄等でご一報いただけると幸いです。