web-dev-qa-db-ja.com

mongodbのネストされた配列の更新

Mongodbに、更新する必要があるオブジェクトの2レベルの深いネストされた配列を含むドキュメントがあります。

{
    id: 1,
    items: [
        {
            id: 2,
            blocks: [
                {
                    id: 3
                    txt: 'hello'
                }
            ]
        }
    ] 
}

1レベルの深さの配列しかない場合は、位置演算子を使用してそのオブジェクトを更新できますが、2番目のレベルでは、ネストされたオブジェクトのインデックスで位置演算子を使用するしかありません。

db.objects.update({'items.id': 2}, {'$set': {'items.$.blocks.0.txt': 'hi'}})

このアプローチは機能しますが、Webサービスを構築していて、インデックス番号は、たとえば100000をインデックスとして送信できるクライアントから取得する必要があるため、危険であると思われます。これにより、mongodbは強制的にnull値の100000インデックスを持つ配列を作成します。

位置の代わりにオブジェクトのIDを参照できるようなネストされたオブジェクトを更新する他の方法や、クエリで使用する前に指定されたインデックスが範囲外かどうかを確認する方法はありますか?

45
src091

ここに大きな質問があります。Mongoの「addToSet」および「Push」操作を活用する必要がありますか?配列内の個々の項目のみを変更する予定の場合は、おそらくこれらの配列をオブジェクトとして構築する必要があります。

これが私がこれを構造化する方法です:

_{
    id: 1,
    items: 
        { 
          "2" : { "blocks" : { "3" : { txt : 'hello' } } },
          "5" : { "blocks" : { "1" : { txt : 'foo'}, "2" : { txt : 'bar'} } }
        }
}
_

これは基本的にすべてを配列ではなくJSONオブジェクトに変換します。 _$Push_と_$addToSet_を使用できなくなりますが、これですべてが簡単になると思います。たとえば、クエリは次のようになります。

db.objects.update({'items.2': {$exists:true} }, {'$set': {'items.2.blocks.0.txt': 'hi'}})

また、「ID」をダンプしたことにも気づくでしょう。このようなものをネストしているときは、通常、 "ID"を単にその番号をインデックスとして使用することで置き換えることができます。 「ID」の概念は現在、暗示されています。

この機能は3.6に 表現力のある更新 で追加されました。

db.objects.update( {id: 1 }, { $set: { 'items.$[itm].blocks.$[blk].txt': "hi", } }, { multi: false, arrayFilters: [ { 'itm.id': 2 }, { 'blk.id': 3} ] } )

27
Gates VP

使用しているIDは線形番号であり、「max_idx」などの追加フィールドなどのどこかから取得する必要があります。つまり、IDを1回検索してから更新します。 UUID/ObjectIdをIDに使用すると、分散CRUDも確実に使用できるようになります。

3
Seraj