Azure Cosmos DB Patch操作:节点不存在时如何创建节点结构?
问题场景
我尝试执行以下Patch操作更新Cosmos DB文档:
ItemResponse<dynamic> response = await _container.PatchItemAsync<dynamic>( id: loanParent.LoanNumber, partitionKey: new PartitionKey(loanParent.LoanNumber), patchOperations: new[] { PatchOperation.Replace("/loandetails/loanname", loanParent.Loan.LoanDetails.LoanName), PatchOperation.Replace("/loandetails/loandescription", loanParent.Loan.LoanDetails.LoanDescription) } );
当前目标文档结构如下:
{ "loannumber": "abc123", "id": "abc123", "participants": [ { "firstname": "alex", "lastname": "gordon" }, { "firstname": "liza", "lastname": "gordon" } ], "_rid": "1sAyAggggggA==", "_self": "dbs/1sAyAA=gggggAAAAAAAA==/", "_etag": "\"d900c069-0000-4440-0000-63642d840000\"", "_attachments": "attachments/", "_ts": 1ddd636 }
执行后触发异常:
Microsoft.Azure.Cosmos.CosmosException : Response status code does not indicate success: BadRequest (400); Substatus: 0; ActivityId: a6ab67d6-f73e-4b3c-b03e-1d9b6cc54dba; Reason: (Message: {"Errors":["For Operation(1): Given Operation can only create a child object of an existing node(array or object) and cannot create path recursively, no path found beyond: 'loandetails'. Learn more: https://aka.ms/cosmosdbpatchdocs"]}
请问如何创建不存在的节点结构?
解决方案
Cosmos DB的Patch操作不会自动递归创建父节点,必须先确保上层节点存在,再操作子属性。推荐两种实现方式:
方式一:先创建父节点,再更新子属性
先用PatchOperation.Add创建/loandetails空对象,再执行Replace操作更新子属性:
ItemResponse<dynamic> response = await _container.PatchItemAsync<dynamic>( id: loanParent.LoanNumber, partitionKey: new PartitionKey(loanParent.LoanNumber), patchOperations: new[] { // 先创建loandetails空对象 PatchOperation.Add("/loandetails", new {}), // 再替换子属性 PatchOperation.Replace("/loandetails/loanname", loanParent.Loan.LoanDetails.LoanName), PatchOperation.Replace("/loandetails/loandescription", loanParent.Loan.LoanDetails.LoanDescription) } );
方式二:一次性创建完整嵌套结构
直接用PatchOperation.Add添加包含所有子属性的loandetails对象,一步完成结构创建和赋值:
ItemResponse<dynamic> response = await _container.PatchItemAsync<dynamic>( id: loanParent.LoanNumber, partitionKey: new PartitionKey(loanParent.LoanNumber), patchOperations: new[] { PatchOperation.Add("/loandetails", new { loanname = loanParent.Loan.LoanDetails.LoanName, loandescription = loanParent.Loan.LoanDetails.LoanDescription }) } );
注意事项
如果loandetails节点可能已存在,建议先通过条件Patch判断节点是否存在,避免覆盖原有数据;或者使用PatchOperation.Upsert(部分SDK版本支持)来实现存在则更新、不存在则创建的逻辑。
内容的提问来源于stack exchange,提问作者Alex Gordon

