# Registering data to Azure CosmosDB ![image](https://hackmd.io/_uploads/BJXpy2e9p.png) ## What is Azure CosmosDB? Azure CosmosDB is a NoSQL database service provided by Microsoft Azure that specializes in large-scale data processing and scalability, and is suitable for building applications on a global scale ## Purpose Here, we will actually register data to Azure CosmosDB and learn how to register data. ## Execution steps Additional implementation to Functions & debugging using Postman 1. Create Azure [CosmosDB resources](https://hackmd.io/mDQdCx9CTs2Q20KAa6WWzA) 2. In function_app.py Make the following modifications add library in requirements.txt for function_app.py ``` azure-cosmos==4.5.1 ``` Import cosmos libraries ``` from azure.cosmos import CosmosClient from datetime import datetime from azure.cosmos.partition_key import PartitionKey import azure.cosmos.exceptions as exceptions ``` Connection setup keys and endpoint ``` cosmosdb_endpoint = "https://aoi35-cosmosdb.documents.azure.com:443/" cosmosdb_key ="keys" client = CosmosClient(url=cosmosdb_endpoint, credential=cosmosdb_key) ``` Add the following code section to the function_app.py ``` def generate_new_item(body): """HTTPレスポンスのbodyから抽出したデータを用いて、CosmosDBに登録するためのレコードを生成する関数 Args: body (dict[str, Union[str, list]]): App ServiceのバックエンドからPOST送信されたデータ Returns: dict: CosmosDBに登録するためのレコード('id', 'partitionKey', 'timestamp', 'history'のキーを持つ) """ new_item = { 'id' : body['id'], 'partitionKey' : 'Account', 'timestamp' : datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ"), "history": body['history'] } return new_item def create_or_get_database_and_container(client, db_name, container_name): """CosmosDB内で指定されたデータベースとコンテナを作成または取得する関数 指定されたデータベース名とコンテナ名を使用して、CosmosDBにデータベースとコンテナを作成する or 既に存在する場合はそれらを取得する Args: client (CosmosClient): CosmosClientのインスタンス db_name (str): 作成または取得するデータベース名 container_name (str): 作成または取得するコンテナ名 Returns: ContainerProxy: 指定されたデータベース内の特定コンテナを操作するためのコンテナプロキシインスタンス - 参考:https://learn.microsoft.com/ja-jp/python/api/azure-cosmos/azure.cosmos.container.containerproxy?view=azure-python Note: データベースとコンテナそれぞれに対し、以下2つのメソッドを使用している - CosmosDB上にデータベース/コンテナが存在しない場合、新規に作成する - create_database, create_container - CosmosDB上にデータベース/コンテナが既に存在している場合、作成済みのデータベース/コンテナの情報を取得する - get_database_client, get_container_client """ try: # DB, コンテナ作成 既に存在する場合error db = client.create_database(id=db_name) container = db.create_container(id=container_name, partition_key=PartitionKey(path='/partitionKey'))# コンテナを作成する際は`partition_key`が必須 except exceptions.CosmosResourceExistsError: # DB, コンテナのインスタンス作成 db = client.get_database_client(db_name) container = db.get_container_client(container_name) return container def create_cosmosdb(client, db_name, container_name, body): """指定されたCosmosDBのコンテナにデータを登録する関数 Args: client (CosmosClient): CosmosClientのインスタンス db_name (str): 作成または取得するデータベース名 container_name (str): 作成または取得するコンテナ名 body (dict[str, Union[str, list]]): App ServiceのバックエンドからPOST送信されたデータ Returns: int: データ登録が成功した場合は0、エラーが発生した場合は1を返す Note: upsert_itemメソッドを使用して以下の通りにデータをコンテナに登録する - new_item の 'id' と'partitionKey'が既存アイテム(コンテナ内に既に存在するレコード)と一致する場合、既存アイテムが上書きされる(アイテムの更新) - new_item の 'id' と'partitionKey'が新規の場合、新規アイテムとして追加される(新規アイテムの追加) """ new_item = generate_new_item(body) container = create_or_get_database_and_container(client, db_name, container_name) # データ送信 既に同一キーのデータがある場合Error # 指定されたコンテナから最大10個のアイテムを読み取り、読み取ったアイテムの数を出力する item_list = list(container.read_all_items(max_item_count=10)) print('Found {0} items'.format(item_list.__len__())) # TODO: 現状、upsert_item の実行時におけるエラーハンドリングは未実装のため、検討すること try: # container.create_item(new_item) container.upsert_item(new_item) except Exception as e: print(e) return 1 return 0 @app.route(route="cosmosdb_trigger") def cosmosdb_trigger(req: func.HttpRequest) -> func.HttpResponse: """HTTPリクエストに応じてCosmosDBにデータを登録し、結果に基づいたHTTPレスポンスを返す関数 本関数は、App ServiceのバックエンドからHTTPリクエストを受け取り、そのリクエストのボディに基づいてCosmosDBにデータを登録する Args: req (func.HttpRequest): 処理するHTTPリクエスト(リクエストのボディには、'id'と'history'のキーが含まれていることが期待される) Returns: func.HttpResponse: データ登録の成否に基づいたHTTPレスポンス Note: - データの登録に成功した場合、HTTPステータスコード200を伴う成功レスポンスを返す - データの登録に失敗した場合、HTTPステータスコード400を伴う失敗レスポンスを返す """ logging.info('Python HTTP trigger function processed a request.') body = req.get_json() result = create_cosmosdb(client, "chat_history", "container", body) if result == 0: return func.HttpResponse("成功しました。", status_code=200) else: return func.HttpResponse("失敗しました。", status_code=400) ``` 3. Fix the cosmosdb_endpoint and cosmosdb_key Execution and validarion 4. How to create a function to execute the above modified locally (check creating function and deploying section if needed) 5. Run the function locally and check either you get the following url so that we can use postman to debug ![image](https://hackmd.io/_uploads/S1ZmZAecT.png) If you are only using cosmosdb than only one trigger will open 6. Open postman for verification Example of the Body used in the above request ``` { "id": "hoge", "partitionKey" : "Account", "history":[ {"role":"user", "content":"毛利さんとは誰ですか"} , {"role":"assistant", "content":"毛利政弘はAMBL株式会社の代表取締役社長CEOです。"} ] } ``` With Postman I got an error ``` Code: BadRequest Message: Message: {"Errors":["Your account is currently configured with a total throughput limit of 1000 RU\/s. This operation failed because it would have increased the total throughput to 1400 RU\/s. See https:\/\/aka.ms\/cosmos-tp-limit for more information."]} ``` Error 2 ``` Resource not found ``` Fix: Removed all the exisitng database re-requested from the postman ![image](https://hackmd.io/_uploads/B1TvoAxqa.png) Confirmation from azure portal ![image](https://hackmd.io/_uploads/ry6niCxqT.png) ## Sending session info to cosmosDB test with app_train.py (local) additional endpoint for cosmosdb ``` functionendpoint_db = f"http://localhost:7071/api/cosmosdb_trigger" ``` customize similary for azure version(app.py) Add the following code send session when user deletes it ``` @app.route("/delete_message", methods=['POST']) def delete_message(): """削除ボタン押下時のルーティング(POSTメソッド) チャットの表示と会話履歴をリセット Returns: Any: トップページにリダイレクト """ send_json = { "id": session["user_id"], 'history': session["history"] } response = requests.post( functionendpoint_db, json=send_json ) reply = response.text sc = response.status_code logging.info([reply, sc]) # チャット表示、会話履歴をリセット session.pop("chat_history", None) session.pop("history", None) return redirect(url_for("index")) ``` ![image](https://hackmd.io/_uploads/ry7pEkb9p.png) After clicking delete ![image](https://hackmd.io/_uploads/HkICVJZ56.png) ___ Deploy function zip and upload to web application Search result ![image](https://hackmd.io/_uploads/H1Ty_k-cp.png) CosmosDB (before deletion) ![image](https://hackmd.io/_uploads/Sym4ukbcp.png) CosmosDB (after deletion) ![image](https://hackmd.io/_uploads/r1O5dJW9p.png)