1. ホーム
  2. mongodb

[解決済み] 配列内の ObjectId の $lookup による検索

2022-07-18 16:30:11

質問

単一の ObjectId ではなく ObjectId の配列であるフィールドに対して $lookup を実行するための構文は何ですか?

注文書の例です。

{
  _id: ObjectId("..."),
  products: [
    ObjectId("..<Car ObjectId>.."),
    ObjectId("..<Bike ObjectId>..")
  ]
}

動作しないクエリです。

db.orders.aggregate([
    {
       $lookup:
         {
           from: "products",
           localField: "products",
           foreignField: "_id",
           as: "productObjects"
         }
    }
])

希望する結果

{
  _id: ObjectId("..."),
  products: [
    ObjectId("..<Car ObjectId>.."),
    ObjectId("..<Bike ObjectId>..")
  ],
  productObjects: [
    {<Car Object>},
    {<Bike Object>}
  ],
}

どのように解決するのですか?

2017年最新情報

lookupがローカルフィールドとして直接配列を使用できるようになった . $unwind はもはや必要ありません。

古い回答

その $lookup 集約パイプラインステージは配列で直接動作することはありません。設計の主な目的は、関連する可能性のあるデータに対する1対多の結合(もしくはルックアップ)としてのquot;左結合(left join")です。しかし、値は単数であることが意図されており、配列ではありません。

従って、あなたは $lookup 操作を行う前に、コンテンツを正規化する必要があります。つまり $unwind :

db.orders.aggregate([
    // Unwind the source
    { "$unwind": "$products" },
    // Do the lookup matching
    { "$lookup": {
       "from": "products",
       "localField": "products",
       "foreignField": "_id",
       "as": "productObjects"
    }},
    // Unwind the result arrays ( likely one or none )
    { "$unwind": "$productObjects" },
    // Group back to arrays
    { "$group": {
        "_id": "$_id",
        "products": { "$push": "$products" },
        "productObjects": { "$push": "$productObjects" }
    }}
])

その後 $lookup が配列の各メンバーにマッチした場合、結果は配列そのものになります。 $unwind を再び実行し $group から $push を新しい配列に置き換えることで、最終的な結果を得ることができます。

見つからなかった左結合のマッチは、与えられた製品の"productObjects"に対して空の配列を作成し、その結果、2番目の $unwind が呼び出されたときに

配列に直接適用するのは良いことですが、これは現在、可能な限り多くの値に単一の値をマッチングさせることで動作する方法です。

として $lookup は基本的に非常に新しいものであり、現在は マングース の貧乏人版とでもいうべきものです。 .populate() メソッドの貧乏人バージョン" として提供されています。違いは $lookup がクライアント上ではなくサーバサイドで処理を行うことと、の一部が $lookup は現在 .populate() が提供するもの(例えば配列上で直接ルックアップを補間するようなもの)に欠けています。

これは実際に改良のために割り当てられた課題です SERVER-22881 であるため、運が良ければ次のリリースまたはそのすぐ後に適用されます。

設計の原則としては、現在の構造は良くも悪くもなく、ただ、quot;join" を作成するときにオーバーヘッドがかかるだけです。そのため、MongoDB の基本的な設立時の原則が適用され、1 つのコレクションでデータ "prejoined" を使用できる場合は、そうすることが最善です。

もう 1 つ言えることは $lookup は、一般的な原則としては、ここでの "join" の意図は、ここで示されているのとは逆の方法で動作することです。つまり、他のドキュメントの "related ID" を "parent" ドキュメント内に保持するのではなく、最も効果的な一般原則は "related documents" が "parent" への参照を含んでいる場合ということです。

そこで $lookup は、mongoose のようなものと逆に、quot;relation design"で最もよく働くと言うことができます。 .populate() のようなものがクライアント側で結合を行うのと逆です。それぞれの"many"の中の"one"をID化することで、関連するアイテムを引き出すだけで、を必要としなくなります。 $unwind を作成することなく、関連する項目を取り込むことができます。