Reputation: 413
Technologies: Cosmos DB Emulator, Microsoft.Azure.Cosmos 3.23.0
Goal : Add the first item of an array using Path Add
Initial data :
{
"id": "P1-86",
"ProjectType_Id": "1",
"Description": "AAA"
}
Requested final data :
{
"id": "P1-86",
"ProjectType_Id": "1",
"Description": "AAA",
"Products" : [{"Id" : "P-1", "Description" : "My Product"}]
}
My code :
PatchOperation operation = PatchOperation.Add("/Products/-", command);
TransactionalBatch batch = container.CreateTransactionalBatch(new PartitionKey(project_Id))
.PatchItem(project_Id, new[] { operation },
new TransactionalBatchPatchItemRequestOptions
{
EnableContentResponseOnWrite = false,
IndexingDirective = IndexingDirective.Exclude
});
TransactionalBatchResponse batchResponse = await batch.ExecuteAsync();
I get a BadRequest Error.
I try :
"/Products/" => insert the item as an object and not as an array's item
{
"id": "P1-86",
"ProjectType_Id": "1",
"Description": "AAA",
"Products" : {"Id" : "P-1", "Description" : "My Product"}
}
"/Products/0/" => BadRequest
"/Products/0" => BadRequest
"/Products/-" => BadRequest
"/Products/-/" => BadRequest
"/Products//" => BadRequest
According to the doc : https://learn.microsoft.com/en-us/azure/cosmos-db/partial-document-update
Add Add performs one of the following, depending on the target path: If the target path specifies an element that does not exist, it is added. If the target path specifies an element that already exists, its value is replaced. If the target path is a valid array index, a new element will be inserted into the array at the specified index. It shifts existing elements to the right. If the index specified is equal to the length of the array, it will append an element to the array. Instead of specifying an index, you can also use the - character. It will also result in the element being appended to the array.
Note: Specifying an index greater than the array length will result in an error.
Is there a Cosmos Db Emulator limitation ? What's wrong with my code?
Edit 1 :
More informations : If initial data are :
{
"id": "P1-86",
"ProjectType_Id": "1",
"Description": "AAA",
"Products" : []
}
PatchOperation.Add("/Products/-", command); => Work
but in my case the Products array didn't previously exists (migration of data model)
Upvotes: 3
Views: 4430
Reputation: 413
Finaly I think that it's not possible to do what i want automaticaly.
From Exception Message when you didn't use Transaction :
BadRequest (400); Substatus: 0; ActivityId: c12f915a-3c4a-4d76-bd7d-ad519bbb8f02; Reason: (Message: {"Errors":["For Operation(1): Add Operation can only create a child object of an existing node(array or object) and cannot create path recursively, no path found beyond: 'Products'.
So I manage to do it by my self, it's not realy cleaver but it's work's.
try
{
PatchOperation operationAdd = PatchOperation.Add("/Products/-", command);
var patchAddResponse = await container.PatchItemAsync<ProjectResponse>(project_Id, new PartitionKey(project_Id), new[] { operationAdd },
new PatchItemRequestOptions
{
FilterPredicate = $"from c where IS_DEFINED(c.Products) AND c.id = '{project_Id}'",
EnableContentResponseOnWrite = false,
IndexingDirective = IndexingDirective.Exclude
});
this.log.LogInformation($"ru : {patchAddResponse.RequestCharge} - AddProductAsync " + command.Id);
return patchAddResponse.Resource;
}
catch (CosmosException ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.PreconditionFailed)
{
PatchOperation operationCreate = PatchOperation.Add("/Products", new IProduct[] { command });
var patchCreateResponse = await container.PatchItemAsync<ProjectResponse>(project_Id, new PartitionKey(project_Id), new[] { operationCreate },
new PatchItemRequestOptions
{
EnableContentResponseOnWrite = false,
IndexingDirective = IndexingDirective.Exclude
});
this.log.LogInformation($"ru : {patchCreateResponse.RequestCharge} - AddProductAsync " + command.Id);
return patchCreateResponse.Resource;
}
else
throw ex;
}
precondition use 1 RU so it's acceptable
Upvotes: 4