CosmosDB 中的评分和平均评分

Ratings and average rate in CosmosDB

假设我正在使用 CosmosDB 并且我有一个电影文档:

Movie
{
    Guid Id 

    int ReviewsCount

    double AverageRate
}

和审核文档:

Review
{
    Guid Id

    Guid MovieId 

    int Rate
}

这些存储在单个容器中,但作为单独的文档(因为一部电影可能有很多评论)。添加新评论时,如何在适当的电影文档中更新 ReviewsCount 和 AverageRate?我不想只阅读这个项目并更新它,因为如果很多用户同时添加评论,可能会出现竞争条件。有没有办法进行一些原子更新?此外,最好确保无论何时添加评论,都会更新电影,因此它应该以某种方式进行交易。

我尝试使用 Patch 操作,但我只能增加字段,无法以这种方式计算 AverageRate。我知道有 PostTriggers,但我认为它们不接受任何属性,因此在添加评论后不可能更新正确的电影,对吗?

我该如何解决这个问题或者我应该如何更好地建模? (AverageRate和ReviewsCount会在查询Movie文档时用于排序)

要更新平均值,请参阅 this answer on Math Stack Exchange。这将允许您计算更新的平均值,而不必每次对所有评论求和和平均。

您可以在同一容器的事务中执行此操作。首先,我将重构此数据,以便您可以使用 movidId 作为新的分区键在单个查询中获取电影及其所有评论。您还需要存储所有评论的总和以计算新的平均值。

Movie
{
    Guid Id 
    Guid MovieId
    int ReviewsCount
    int RateSum
    double AverageRate
}

Review
{
    Guid Id
    Guid MovieId 
    int Rate
}

接下来,当添加新评论时使用 Transactional Batch 您将在其中执行四项操作,最后三项作为单个补丁操作。

  1. 插入新的评论项目。
  2. 增加电影的评论数量。
  3. 增加所有评分的总和。
  4. 更新所有评论的平均值。

您的代码应该与此类似。

List<PatchOperation> patchOperations = new List<PatchOperation>();
    patchOperations.Add(PatchOperation.Set("/AverageRate", newAverage));
    patchOperations.Add(PatchOperation.Increment("/RateSum", newRating));
    patchOperations.Add(PatchOperation.Increment("/ReviewsCount", 1));

var resp = container.CreateTransactionalBatch(new PartitionKey(movieId))
    .UpsertItem(newReview)
    .PatchItem(movieId, patchOperations);