CosmosDBのPythonSDKによる部分更新の備忘録(パッチ更新）

CosmosDBで部分更新のやり方でつまったので、メモ程度に残します。

普通にCosmosDBからPythonで読み取る方法はこちら

https://qiita.com/taisyu6789/items/e73a56255bf465e5978f

## 部分更新(部分的ドキュメント更新機能)とは

部分的ドキュメント更新機能により、全体更新に比べ、エクスペリエンスが大幅に向上します。 全ドキュメントの置き換え操作を行うことなく、クライアントからドキュメント内の変更されたプロパティとフィールドのみを送信することができるからです。

:::note info
通常は、ローカルで部分的に更新してもAPIの動き的には全体更新になります。
:::


| 特徴              | 部分更新 (`patch_item`)                                     | 完全更新 (`replace_item`)                                |
|-------------------|-----------------------------------------------------------|-------------------------------------------------------|
| **操作内容**       | 特定のフィールドや要素のみを更新                           | ドキュメント全体を置き換える                            |
| **消費RU**        | 更新対象が少ないほど低い                                    | ドキュメント全体に対する更新のため高い                   |
| **競合の可能性**   | 更新箇所が特定されるため低い                               | ドキュメント全体を更新するため競合が発生しやすい         |
| **使用シナリオ**   | 小規模な更新（例: 配列への要素追加、特定フィールドの変更）   | 大規模な変更や全フィールドの再構成が必要な場合           |
| **実装例**         | 部分的な操作 (`add`, `remove`, `replace`) を指定して更新    | ドキュメントを取得して全体を再構成して更新               |


## Pythonでの実装方法

### **パッチ演算子の種類と説明**

| 演算子        | 説明                                                                                                                             |
|---------------|----------------------------------------------------------------------------------------------------------------------------------|
| **Add**       | ターゲットパスに要素を追加します。存在する場合は値を置き換えます。配列の場合、インデックスを指定して挿入も可能です。                   |
| **Set**       | 配列以外のフィールドの場合、`Add`と同様に動作します。配列の場合、指定したインデックスの値を更新します。                             |
| **Replace**   | 指定したパスの値を厳密に置き換えます。指定されたパスが存在しない場合はエラーをスローします。                                           |
| **Remove**    | 指定したパスの値を削除します。配列の場合、インデックスを指定して要素を削除し、後続の要素がシフトされます。                              |
| **Increment** | フィールドの値を指定した量だけインクリメントします。存在しない場合は新規作成されます。                                                  |
| **Move**      | 指定したパスから値を移動し、ターゲットパスに追加します。`from`パスと`path`を指定する必要があります。                                   |

---

### **サンプルコード：フィードバックの追加または更新**

以下は、ユーザーからのフィードバックを配列に追加または更新する例です。
CosmosDBに以下のようなデータがある場合で
2番目のデータのみを更新する場合と
4番目(新しいデータ)を追加する場合を例に出します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3846056/eefd2d52-5f7a-3c34-06c8-d4071afe732f.png)




#### **コード例**
```python
# Partition Keyの指定 # 階層的パーティションキーを設定している場合はタプルで定義
partition_key  : tuple = ( PartitionKey1, PartitionKey2, PartitionKey3) 
print(f"Partition Key: {partition_key}")

# 追加または更新するデータ
feedback_dict = {
    'text_A': A,                 # Aのテキスト
    'text_B': B,                 # Bのテキスト
}

# パッチ操作: フィードバックをスレッド配列の2番目に追加または更新
patch_operations = [
    {
        'op': 'set',  # 配列のインデックス位置に値を設定（既存なら更新）
        'path': '/threads/2/user_feedback',
        'value': feedback_dict
    },
    {
        'op': 'add',  # 配列の最後尾に追加
        'path': '/threads/-/user_feedback',
        'value': feedback_dict
    }
]

# Cosmos DBパッチ操作の実行
cosmos_container.patch_item(
    item=id,                      # 更新対象ドキュメントのID
    partition_key=partition_key,  # パーティションキー
    patch_operations=patch_operations
)
```

### 注意点
 階層的partition_keyを設定している場合はタプルで送信。
 patch_operations は複数同時実行できる。

## 以下参考記事

https://learn.microsoft.com/ja-jp/azure/cosmos-db/partial-document-update

 以上