Cosmos DB - 查询所选分区的最新文档?

Posted

技术标签:

【中文标题】Cosmos DB - 查询所选分区的最新文档?【英文标题】:Cosmos DB - Query for newest document of select partitions? 【发布时间】:2021-08-27 01:27:09 【问题描述】:

考虑一个具有以下文档模型的 CosmosDB 容器:


  id: <string>,
  userId: <string>, // partition key
  data: <string>

我需要提供一个包含 N 个用户 ID 的查询并获取每个用户 ID 的最新文档。

例如,如果我在容器中有这些数据:

 id: '1', userId: 'user1', data: 'a', _ts: 1 ,
 id: '2', userId: 'user1', data: 'b', _ts: 2 ,
 id: '3', userId: 'user2', data: 'c', _ts: 10 ,
 id: '4', userId: 'user2', data: 'd', _ts: 5 ,
 id: '5', userId: 'user3', data: 'e', _ts: 3 ,
 id: '6', userId: 'user3', data: 'f', _ts: 4 ,
 id: '7', userId: 'user4', data: 'g', _ts: 100 ,
 id: '8', userId: 'user4', data: 'h', _ts: 99 ,
 id: '9', userId: 'user5', data: 'i', _ts: 1 ,
 id: '10', userId: 'user5', data: 'j', _ts: 2 ,

我想做这样的事情:

-- This doesn't work
SELECT c.userId, (SELECT TOP 1 d.id, d.data WHERE d.userId = c.userId FROM d ORDER BY d._ts DESC) AS newest
WHERE c.userId IN ['user1', 'user2', 'user4', 'user5']

要得到这个结果:

 userId: 'user1', newest:  id: '2', data: 'b'  ,
 userId: 'user2', newest:  id: '3', data: 'c'  ,
 userId: 'user4', newest:  id: '7', data: 'g'  ,
 userId: 'user5', newest:  id: '10', data: 'j'  ,

据我所知,CosmosDB 中的 JOIN 不能用于过滤相关文档。还有办法做到这一点吗?我愿意使用存储过程,但据我所知,存储过程的执行只能在特定分区上发生,因为它是关键。在我的例子中,主要分组是分区键。

我考虑过扇出请求方法,但我可能在查询中一次查询 50 到 100 个用户 ID。在这种情况下,只获取每个分区中的所有文档并在迭代时只保留最新的文档可能会更快——但这是一个需要筛选的大页面响应。

我最后的想法是,我可以使用 ASB/EventGrid/Function 和另一个依赖的 CosmosDB 容器来始终在每次更新文档时克隆最新更新的文档,但这似乎有点矫枉过正。肯定有一种方法可以构造一个查询来做我想做的事吗?

谢谢

【问题讨论】:

select value max(c._ts) from c group by c.userId 可以得到每个用户最新列的时间戳列表。 即使有办法,这也可能是一个昂贵的查询。我的直觉是使用更改提要将用户的最新项目保存在单独的容器中以优化读取。 【参考方案1】:

我有个想法

select c._ts from c where ARRAY_CONTAINS((select value max(c._ts) from c group by c.userId), c._ts)

但它无法得到结果,因为select value max(c._ts) from c group by c.userId 不被识别为数组,如果我使用Array(select value max(c._ts) from c group by c.userId) 代替,它会返回所有项目。

那么执行两次sql怎么样?

先获取时间戳数组:select value max(c._ts) from c where c.userId in ('user1','user2') group by c.userId ,

然后将结果复制为输入以使用array_contains函数:

select c._ts,c.data from c where ARRAY_CONTAINS([1623306298,1623306259,1623306217], c._ts)

【讨论】:

【参考方案2】:

这样做的一种方法是使用以下方法。

SELECT t.userid, 
       SUBSTRING(t.concat, 28,8000) AS data
FROM 
(
SELECT  c.userid,
        MAX(CONCAT(TimestampToDateTime(c._ts*1000),c.data)) AS concat
FROM c
WHERE c.userid IN ('user1', 'user2')
GROUP BY c.userid
) AS t

返回类似的结果

[
    
        "userid": "user1",
        "data": "b"
    ,
    
        "userid": "user2",
        "data": "d"
    
]

派生表t 返回如下结果...

[
    
        "userid": "user2",
        "concat": "2021-06-11T17:42:03.0000000Zd"
    ,
    
        "userid": "user1",
        "concat": "2021-06-11T17:41:41.0000000Zb"
    
]

每个用户具有最高_ts 的文档将在连接字符串中具有字典顺序最高的日期时间前缀,并且附加在其后面的辅助数据将使用SUBSTRING 提取。

它应该能够使用WHERE 子句的索引 - 但随后将需要查看给定userids 的所有文档(因此,如果每个用户有很多文档,则单独执行TOP 1 查询每个都可能会好得多)

【讨论】:

以上是关于Cosmos DB - 查询所选分区的最新文档?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在查询 Cosmos Db 中对数据进行逻辑分区?就像您可以使用散列和模函数一样[关闭]

Cosmos DB:Gremlin API请求太大异常。如何重试通话

Cosmos DB 存储过程

Azure Cosmos DB 分区键 - 主键是不是可接受?

Azure Cosmos DB 中托管的 MongoDB:分片与分区

来自 Databricks Notebook 的 COSMOS DB 写入问题