You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Cosmos DB中编写满足多属性配对约束的查询?

问题背景与数据模型

我们在Cosmos DB容器中存储基于以下C#类的房间数据:

public abstract class MyItemBase
{
    public Guid Id { get; set; }
    public string PartitionKey { get; set; } = "some-customer-id-room";
    public Guid RevisionsId { get; set; }
    public int Revision { get; set; }
    public DateTime CreatedAt { get; set; }
    public Guid CreatedBy { get; set; }
    public bool IsDeleted { get; set; }
}

public class MyRoom : MyItemBase
{
    public string Name { get; set; }
    public int Capacity { get; set; }
}

房间的生命周期通过版本控制管理:每次变更都创建新条目,同一房间的所有条目共享RevisionsId,且Revision值逐次递增;通过切换IsDeleted标记实现删除与恢复。

单个房间的生命周期示例:

var sharedRevisionsId = Guid.NewGuid();
var roomChanges = new List<MyRoom>
{
    new() { Id = Guid.NewGuid(), RevisionsId = sharedRevisionsId, Revision = 1, CreatedAt = new DateTime(2024, 2, 16, 17, 0, 0), CreatedBy = Guid.NewGuid(), IsDeleted = false, Name = "Main Hall", Capacity = 100 },
    new() { Id = Guid.NewGuid(), RevisionsId = sharedRevisionsId, Revision = 2, CreatedAt = new DateTime(2024, 2, 17, 13, 0, 0), CreatedBy = Guid.NewGuid(), IsDeleted = false, Name = "Main Hall", Capacity = 90 },
    new() { Id = Guid.NewGuid(), RevisionsId = sharedRevisionsId, Revision = 3, CreatedAt = new DateTime(2024, 2, 18, 11, 0, 0), CreatedBy = Guid.NewGuid(), IsDeleted = true, Name = "Main Hall", Capacity = 90 },
    new() { Id = Guid.NewGuid(), RevisionsId = sharedRevisionsId, Revision = 4, CreatedAt = new DateTime(2024, 2, 19, 19, 0, 0), CreatedBy = Guid.NewGuid(), IsDeleted = false, Name = "Main Hall", Capacity = 90 },
    new() { Id = Guid.NewGuid(), RevisionsId = sharedRevisionsId, Revision = 5, CreatedAt = new DateTime(2024, 2, 20, 20, 0, 0), CreatedBy = Guid.NewGuid(), IsDeleted = true, Name = "Main Hall", Capacity = 90 },
};
现有筛选流程

为找出分区内当前处于删除状态的房间,我们先执行以下查询获取所有曾标记为删除的候选条目:

SELECT
  root.revisionsId,
  MAX(root.revision) as revision
FROM
  root
WHERE
  root.isDeleted
GROUP BY
  root.revisionsId

但这些候选条目未必是对应房间的最新版本(可能已被恢复),因此需要第二次查询获取这些候选的最新版本:

SELECT
  root.revisionsId,
  MAX(root.revision) as revision
FROM
  root
WHERE
  ARRAY_CONTAINS(@revisionsIds, root.revisionsId)
GROUP BY
  root.revisionsId

最后通过客户端LINQ筛选出当前处于删除状态的条目(即候选的删除版本等于最新版本):

var deletedItems = deletedCandidates
    .Join(highestRevisions,
        deleted => deleted.RevisionsId,
        highest => highest.RevisionsId,
        (deleted, highest) => (deleted, highest))
    .Where(pair => pair.deleted.Revision == pair.highest.Revision)
    .Select(pair => pair.highest)
    .ToList();
当前待解决问题

上述筛选结果仅包含RevisionsId和Revision属性,我们需要获取这些条目的完整文档(至少包含Id)。已知目标条目满足:匹配指定的多组(RevisionsId, Revision)配对,且IsDeleted=true,但之前尝试的查询写法均无效:

无效写法1:

SELECT
  *
FROM
  root
WHERE
  ARRAY_CONTAINS(
    [{"revisionsId":"abc...", "revision":5}, {"revisionsId":"123...", "revision":13}],
    { root.revisionsId, root.revision }
  )

无效写法2:

SELECT
  *
FROM
  root
WHERE
  ARRAY_CONTAINS(
    ["abc...", "123..."],
    root.revisionsId
  )
ORDER BY root.revision desc
解决方案

方法1:通过JOIN匹配多组属性配对

使用参数化的配对数组,通过JOIN语句匹配每个条目的RevisionsId和Revision:

SELECT *
FROM root
JOIN pair IN @revisionPairs
WHERE root.revisionsId = pair.revisionsId 
  AND root.revision = pair.revision 
  AND root.isDeleted = true

其中参数@revisionPairs的格式为:

[
  {"revisionsId": "abc...", "revision": 5},
  {"revisionsId": "123...", "revision": 13}
]

这种方式相比ARRAY_CONTAINS更可靠,因为ARRAY_CONTAINS对对象的匹配要求严格的结构一致性(包括属性顺序),而JOIN直接通过等值条件匹配属性,避免了结构匹配的问题。

方法2:单查询直接获取当前删除的完整文档

如果想跳过多次查询和客户端处理的步骤,可以用子查询直接获取每个房间的最新版本,再筛选IsDeleted=true的条目:

SELECT *
FROM root
WHERE root.revision = (
    SELECT MAX(r.revision)
    FROM r
    WHERE r.revisionsId = root.revisionsId
)
AND root.isDeleted = true

这个查询会直接返回所有当前处于删除状态的完整房间文档,无需额外的客户端处理,效率更高。


内容的提问来源于stack exchange,提问作者Oliver

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 03:24:53