web-dev-qa-db-ja.com

MongoDBの$ unwind演算子とは何ですか?

これはMongoDBでの私の最初の日なので、私と一緒に簡単に行ってください:)

おそらく英語が私の母国語ではないため、$unwind演算子を理解できません。

db.article.aggregate(
    { $project : {
        author : 1 ,
        title : 1 ,
        tags : 1
    }},
    { $unwind : "$tags" }
);

プロジェクトオペレーターは、私が理解できるものだと思います(SELECTに似ていますよね?)。ただし、$unwind(引用)は、すべてのソースドキュメント内の展開された配列のメンバーごとに1つのドキュメントを返します

これはJOINのようなものですか?はいの場合、$projectの結果(_idauthortitle、およびtagsフィールドと)をtags配列と比較する方法?

[〜#〜] note [〜#〜]:MongoDB Webサイトから例を取り上げましたが、tags配列の構造はわかりません。タグ名の単純な配列だと思います。

69
gremo

まず、MongoDBへようこそ!

覚えておくべきことは、MongoDBがデータストレージに対して「NoSQL」アプローチを採用しているため、選択や結合などの考えがあなたの心から滅びることです。データを保存する方法は、ドキュメントとコレクションの形式であり、これにより、保存場所からデータを動的に追加および取得することができます。

つまり、$ unwindパラメーターの背後にある概念を理解するためには、まず、引用しようとしているユースケースが何を言っているのかを理解する必要があります。 mongodb.org のサンプルドキュメントは次のとおりです。

{
 title : "this is my title" ,
 author : "bob" ,
 posted : new Date () ,
 pageViews : 5 ,
 tags : [ "fun" , "good" , "fun" ] ,
 comments : [
             { author :"joe" , text : "this is cool" } ,
             { author :"sam" , text : "this is bad" }
 ],
 other : { foo : 5 }
}

タグが実際には3つのアイテムの配列であることに注意してください。この場合、「楽しい」、「良い」、「楽しい」です。

$ unwindが行うことは、各要素のドキュメントを剥がし、その結果のドキュメントを返すことです。これを古典的なアプローチで考えると、「タグ配列内の各アイテムについて、そのアイテムのみを含むドキュメントを返す」と同等です。

したがって、次を実行した結果:

db.article.aggregate(
    { $project : {
        author : 1 ,
        title : 1 ,
        tags : 1
    }},
    { $unwind : "$tags" }
);

次のドキュメントが返されます。

{
     "result" : [
             {
                     "_id" : ObjectId("4e6e4ef557b77501a49233f6"),
                     "title" : "this is my title",
                     "author" : "bob",
                     "tags" : "fun"
             },
             {
                     "_id" : ObjectId("4e6e4ef557b77501a49233f6"),
                     "title" : "this is my title",
                     "author" : "bob",
                     "tags" : "good"
             },
             {
                     "_id" : ObjectId("4e6e4ef557b77501a49233f6"),
                     "title" : "this is my title",
                     "author" : "bob",
                     "tags" : "fun"
             }
     ],
     "OK" : 1
}

結果の配列で変更されるのは、タグの値で返されるものだけであることに注意してください。これがどのように機能するかについての追加の参照が必要な場合は、リンク here を含めました。これがお役に立てば幸いであり、これまでに出会った中で最高のNoSQLシステムの1つへの進出をお祈りします。

172
HGS Labs

$unwindは、配列要素ごとに1回、パイプライン内の各ドキュメントを複製します。

したがって、入力パイプラインにtagsに2つの要素を持つ1つの記事ドキュメントが含まれる場合、{$unwind: '$tags'}は、tagsフィールドを除いて同じである2つの記事ドキュメントにパイプラインを変換します。最初のドキュメントでは、tagsには元のドキュメントの配列の最初の要素が含まれ、2番目のドキュメントでは、tagsに2番目の要素が含まれます。

36
JohnnyHK

例で理解しましょう

これは、companyドキュメントがどのように見えるかです:

original document

$unwindを使用すると、配列値フィールドを持つドキュメントを入力として取得し、配列内の各要素に1つの出力ドキュメントがあるように出力ドキュメントを生成できます。 ソース

The $unwind stage

それでは、会社の例に戻って、巻き戻しステージの使用を見てみましょう。このクエリ:


db.companies.aggregate([
    { $match: {"funding_rounds.investments.financial_org.permalink": "greylock" } },
    { $project: {
        _id: 0,
        name: 1,
        amount: "$funding_rounds.raised_amount",
        year: "$funding_rounds.funded_year"
    } }
])

量と年の両方の配列を持つドキュメントを作成します。

project output

資金調達ラウンド配列内のすべての要素の調達額と資金調達年にアクセスしているためです。これを修正するには、プロジェクトステージの前にこの集約パイプラインにアンワインドステージを含め、資金調達ラウンド配列をunwindしたいということでこれをパラメーター化できます。


db.companies.aggregate([
    { $match: {"funding_rounds.investments.financial_org.permalink": "greylock" } },
    { $unwind: "$funding_rounds" },
    { $project: {
        _id: 0,
        name: 1,
        amount: "$funding_rounds.raised_amount",
        year: "$funding_rounds.funded_year"
    } }
])

unwind has the effect of outputting to the next stage more documents than it receives as input

funding_rounds配列を見ると、funding_roundsごとにraised_amountフィールドとfunded_yearフィールドがあることがわかります。したがって、unwindは、funding_rounds配列の要素であるドキュメントごとに出力ドキュメントを生成します。さて、この例では、値はstringsです。ただし、配列内の要素の値のタイプに関係なく、unwindはこれらの値のそれぞれに対して出力ドキュメントを生成し、問題のフィールドにはその要素のみが含まれるようにします。 funding_roundsの場合、その要素は、projectステージに渡されるすべてのドキュメントのfunding_roundsの値として、これらのドキュメントの1つになります。これを実行した結果、amountyearが得られます。コレクション内のすべての会社の各資金調達ラウンドに1つ。これが意味することは、私たちのマッチが多くの会社のドキュメントを作成し、それらの会社のドキュメントのそれぞれが多くのドキュメントをもたらすということです。すべての会社文書内の資金調達ラウンドごとに1つ。 unwindは、matchステージから渡されたドキュメントを使用してこの操作を実行します。そして、すべての会社のこれらのドキュメントはすべて、projectステージに渡されます。

unwind output

そのため、資金提供者がGreylock(クエリの例のように)であったすべてのドキュメントは、複数のドキュメントに分割され、一致するすべての会社の資金調達ラウンドの数に等しくなります$match: {"funding_rounds.investments.financial_org.permalink": "greylock" }をフィルターします。そして、それらの結果のドキュメントはそれぞれ、projectに渡されます。現在、unwindは、入力として受信するすべてのドキュメントの正確なコピーを作成します。すべてのフィールドには同じキーと値がありますが、例外が1つあります。つまり、funding_roundsドキュメントの配列ではなく、funding_roundsフィールドには、個別の資金調達ラウンドである単一のドキュメントの値があります。そのため、4の資金調達ラウンドを持つ企業は、unwind4ドキュメントを作成します。ここで、funding_roundsフィールドを除くすべてのフィールドは正確なコピーであり、これらのコピーのそれぞれの配列ではなく、unwindが現在処理している会社文書のfunding_rounds配列の個々の要素になります。したがって、unwindは、入力として受け取るよりも多くのドキュメントを次のステージに出力する効果があります。これは、projectステージがfunding_roundsフィールドを取得することを意味します。このフィールドは、配列ではなく、raised_amountおよびfunded_yearフィールドを持つネストされたドキュメントです。したがって、projectは各企業の複数のドキュメントを受信しますmatchフィルターを使用して、各ドキュメントを個別に処理し、各資金調達ラウンドの個々の金額と年を識別できます会社

13
student

Mongodb公式ドキュメントによると:

$ unwind入力ドキュメントから配列フィールドを分解して、各要素のドキュメントを出力します。各出力ドキュメントは、配列フィールドの値が要素に置き換えられた入力ドキュメントです。

基本的な例による説明:

コレクションインベントリには、次のドキュメントがあります。

{ "_id" : 1, "item" : "ABC", "sizes": [ "S", "M", "L"] }
{ "_id" : 2, "item" : "EFG", "sizes" : [ ] }
{ "_id" : 3, "item" : "IJK", "sizes": "M" }
{ "_id" : 4, "item" : "LMN" }
{ "_id" : 5, "item" : "XYZ", "sizes" : null }

次の$unwind操作は同等であり、sizesフィールドの各要素のドキュメントを返します。サイズフィールドが配列に解決されないが、欠損、null、または空の配列ではない場合、$ unwindは非配列オペランドを単一要素配列として扱います。

db.inventory.aggregate( [ { $unwind: "$sizes" } ] )

または

db.inventory.aggregate( [ { $unwind: { path: "$sizes" } } ] 

上記のクエリ出力:

{ "_id" : 1, "item" : "ABC", "sizes" : "S" }
{ "_id" : 1, "item" : "ABC", "sizes" : "M" }
{ "_id" : 1, "item" : "ABC", "sizes" : "L" }
{ "_id" : 3, "item" : "IJK", "sizes" : "M" }

なぜそれが必要なのですか?

$ unwindは、集計の実行中に非常に便利です。ソート/検索などのさまざまな操作を実行する前に、複雑な/ネストされたドキュメントを単純なドキュメントに分割します。

$ unwindの詳細:

https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/

集約の詳細を知るには:

https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/

2
Amitesh

RDBMSの方法に関連する方法で説明させてください。これは声明です:

db.article.aggregate(
    { $project : {
        author : 1 ,
        title : 1 ,
        tags : 1
    }},
    { $unwind : "$tags" }
);

document/recordに適用するには:

{
 title : "this is my title" ,
 author : "bob" ,
 posted : new Date () ,
 pageViews : 5 ,
 tags : [ "fun" , "good" , "fun" ] ,
 comments : [
             { author :"joe" , text : "this is cool" } ,
             { author :"sam" , text : "this is bad" }
 ],
 other : { foo : 5 }
}

$ project/Selectは、これらのフィールド/列を単に

[〜#〜] select [〜#〜]著者、タイトル、タグ[〜#〜] from [ 〜#〜]記事

次はMongoの楽しい部分です。この配列tags : [ "fun" , "good" , "fun" ]「タグ」という名前の別の関連テーブル(値には重複があるため、ルックアップ/参照テーブルにできません)。 SELECTは一般に物事を垂直に生成するので、「タグ」はsplit()をテーブル「タグ」に垂直に戻すことを忘れないでください。

$ project + $ unwindの最終結果: enter image description here

出力をJSONに変換します。

{ "author": "bob", "title": "this is my title", "tags": "fun"},
{ "author": "bob", "title": "this is my title", "tags": "good"},
{ "author": "bob", "title": "this is my title", "tags": "fun"}

Mongoに「_id」フィールドを省略するように指示しなかったため、自動的に追加されます。

重要なのは、集計を実行するためにテーブルのようにすることです。

1
Jeb50

コレクション内のこのデータを理解するには、以下の例を検討してください

{
        "_id" : 1,
        "shirt" : "Half Sleeve",
        "sizes" : [
                "medium",
                "XL",
                "free"
        ]
}

クエリ-db.test1.aggregate([{$ unwind: "$ sizes"}]);

出力

{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "medium" }
{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "XL" }
{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "free" }
0
Pravin