sql >> データベース >  >> NoSQL >> MongoDB

MongoDB:サブドキュメントをアップサート

    いいえ、これに対するより良い解決策は実際にはないので、おそらく説明が必要です。

    次のような構造のドキュメントが配置されているとします。

    { 
      "name": "foo", 
      "bars": [{ 
           "name": "qux", 
           "somefield": 1 
      }] 
    }
    

    このような更新を行う場合

    db.foo.update(
        { "name": "foo", "bars.name": "qux" },
        { "$set": { "bars.$.somefield": 2 } },
        { "upsert": true }
    )
    

    その後、一致するドキュメントが見つかったため、すべて問題ありません。ただし、「bars.name」の値を変更した場合:

    db.foo.update(
        { "name": "foo", "bars.name": "xyz" },
        { "$set": { "bars.$.somefield": 2 } },
        { "upsert": true }
    )
    

    その後、失敗します。ここで実際に変更されたのは、MongoDB 2.6以降では、エラーがもう少し簡潔になっていることだけです。

    WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
            "code" : 16836,
            "errmsg" : "The positional operator did not find the match needed from the query. Unexpanded update: bars.$.somefield"
        }
    })
    

    それはいくつかの点で優れていますが、とにかく「アップサート」したくないのです。実行したいのは、「名前」が現在存在しない配列に要素を追加することです。

    したがって、本当に必要なのは、影響を受けたドキュメントがあるかどうかを確認するための「upsert」フラグなしの更新試行の「結果」です。

    db.foo.update(
        { "name": "foo", "bars.name": "xyz" },
        { "$set": { "bars.$.somefield": 2 } }
    )
    

    それに応じて譲歩する:

    WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
    

    したがって、変更されたドキュメントが0の場合 次に、次の更新を発行する必要があることがわかります。

    db.foo.update(
        { "name": "foo" },
        { "$push": { "bars": {
            "name": "xyz",
            "somefield": 2
        }}
    )
    

    あなたが望むことを正確に行う他の方法は本当にありません。配列への追加は厳密には「セット」タイプの操作ではないため、$addToSetを使用することはできません。 そこで「一括更新」機能と組み合わせて、更新要求を「カスケード」できるようにします。

    この場合、結果を確認するか、ドキュメント全体を読んで、コードに新しい配列要素を更新するか挿入するかを確認する必要があるようです。



    1. Meteor.jsで複数のMongodbデータベースを使用する

    2. オープンソースデータベースを管理するための新しい方法

    3. Mongooseがサブドキュメント配列アイテムの_idプロパティを作成しないようにします

    4. 明示的に名前を付けずにMongoDBの任意のフィールドの値を検索する