2

したがって、コレクション内のドキュメントを構造的に次のように表示する必要があります。

{
    "_id": "123",
    "systems": [
        {
            "_id": "1338",
            "metrics": [
                "TEST"
            ]
        }
    ]
}

私の目標は、システムおよび/またはメトリックがそれぞれの配列に存在する/存在しないインスタンスに対して、単一の更新/挿入 (upsert=True を使用) を実行できるようにすることです。現在、私の唯一の回避策は、次のように呼び出しを更新することです。

if not collection.find_one({"_id": "123", "systems._id": "1338"}):
    collection.update(
        {"_id": "123"}, 
        {"$addToSet": {"systems": {"_id": "1338"}}}, 
        upsert=True)
collection.update(
    {"_id": "123", "systems._id": "1338"}, 
    {"$addToSet": {"systems.$.metrics": "TEST"}}, 
    upsert=True)

ありがとう

4

1 に答える 1

3

array を含む対応するクエリ フィールドがなければ位置演算子を適用することはできませんが、 $addToSet へのシステム配列内のインデックスを知るために位置演算子が必要です{"metrics": ["TEST"]}{"systems._id": "1338"}このフィールドはドキュメントにまだ存在しない可能性があるため、 に基づいてクエリを実行することもできません{"_id": "123"}。したがって、同じフィールドに触れる複数の操作を組み合わせることができない場合、単一のリクエストでそれを行うことはできませんが、 update で競合する mod を持つことはできません。

最後の希望は、更新ドキュメント構文に$where 演算子に似たものがあれば、任意の JavaScript を実行してドキュメントを更新できるようになることです。私たちはこれを持っていない(そして持っていない)と思います。

systems主な問題は、フィールドの配列インデックスです。_id属性を新しいインデックスとして使用して、スキーマを再設計してこのインデックスを取り除くことができます。

{
    "_id": "123",
    "systems": {
        "1338": {
            "metrics": [
                "TEST"
            ]
        }
    }
}

この変更により、すべてを 1 つの操作で実行できます。

db.test.update({_id: "123"}, {$addToSet: {"systems.1338.metrics": "TEST"}}, {upsert: true})

別のオプションは、次の設計を使用することです。

{
    "_id": "123",
    "systems": {
        "metrics": {
            "1338": [
                "TEST"
            ]
        }
    }
}

最適な設計は、目的によって異なります。

編集:

アプリケーションでドキュメントをローカルで更新してから DB に送り返すオプションもありますが、このオプションでは、目的と思われる種類の原子性が提供されない場合があります。

そして、現在の回避策について: if. 両方のクエリを順番に実行できます。ただし、データベースにアクセスするクライアントが複数ある場合は安全ではありません。

于 2013-05-18T02:23:43.753 に答える