在容器中,我们存储一些具有以下基类和派生类(简化)的项目:
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; }
}
监视(在本例中)房间的生命周期,我们为每次更改在 Cosmos 中创建一个新项目。要知道哪些物品属于一生,按什么顺序,一个房间的所有物品共享相同的
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
此查询将返回生命线中某处将
IsDeleted
标志设置为 true 的所有项目,但这一定不是最高版本,因为它可能已经被恢复。因此,我们发送第二个查询,从第一个查询中取出所有候选者,并要求其最高修订版本,无论删除状态如何:
SELECT
root.revisionsId,
MAX(root.revision) as revision
FROM
root
WHERE
ARRAY_CONTAINS(@revisionsIds, root.revisionsId)
GROUP BY
root.revisionsId
通过在客户端使用简单的 LINQ-to-object,我们可以找出哪些项目当前处于删除状态:
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();
备注:如果您知道如何直接在 Cosmos 中的一个查询中完成上述查找,我将很高兴听到它。
到目前为止一切看起来都很好,但现在我的问题来了: 已删除项目的列表包含整个对象,但由于使用的查询,它们的属性仅部分填充。唯一有意义的属性是
RevisionsId
和 Revision
,但在下一步中我至少需要这些对象的 Id 或(甚至更好)它们的整个内容。
此时我们拥有的是一个元素列表,我们从每个元素中知道两件事,它们的
RevisionsId
、最高的 Revision
以及 IsDeleted
是 true。
如何制定针对 Cosmos 的查询,以返回所有两个属性对匹配的所有元素?
一些不是的工作想法是
SELECT
*
FROM
root
WHERE
ARRAY_CONTAINS(
[{"revisionsId":"abc...", "revision":5}, {"revisionsId":"123...", "revision":13}],
{ root.revisionsId, root.revision }
)
SELECT
*
FROM
root
WHERE
ARRAY_CONTAINS(
["abc...", "123..."],
root.revisionsId
)
ORDER BY root.revision desc
那么关于如何搜索多个元素(在本例中为房间),其中每个项目必须单独满足多个约束(在本例中为 RevisionsId 和 Revision),有什么想法吗?这个问题听起来很简单,但我确实无法通过给定的模型找到好的方法。
根据此文档,匹配对象的语法如下所示。
ARRAY_CONTAINS([<list_of_object>], <object_to_check>)
你所给予的是
ARRAY_CONTAINS(
[{"revisionsId":"abc...", "revision":5}, {"revisionsId":"123...", "revision":13}],
{ root.revisionsId, root.revision }
)
{ root.revisionsId, root.revision }
不是要检查的有效对象。
这就是正确的奉献方式。
ARRAY_CONTAINS(
[{"revisionsId":"abc...", "revision":5}, {"revisionsId":"123...", "revision":13}],
{ "revisionsId":root.revisionsId, "revision":root.revision }
)
这是示例数据的输出。
查询
SELECT
root.revisionsId,
root.revision
FROM
root
WHERE
ARRAY_CONTAINS(
[{"revisionsId":"e326de52-1a84-42d9-bc01-7613a42dc96f", "revision":1}, {"revisionsId":"e326de52-1a84-42d9-bc01-7613a42dc96f", "revision":2}],
{ "revisionsId":root.revisionsId, "revision":root.revision }
)
输出:
[
{
"revisionsId": "e326de52-1a84-42d9-bc01-7613a42dc96f",
"revision": 1
},
{
"revisionsId": "e326de52-1a84-42d9-bc01-7613a42dc96f",
"revision": 2
}
]