如何使用 SQL 获取 CosmosDB 中每一项的最新记录

问题描述 投票:0回答:4

我有一个类似于

的模式
"id": "uuid",
"deviceId": "uuid",
"message": {
    "content": "string",
    "ts": 1
},
"data": {
    "temperature": 21
}

我想获取每个“deviceId”的最新“数据”(使用message.ts作为时间戳)。

到目前为止,我已经成功使用查询按时间戳顺序取回数据

SELECT c.deviceId, c.message.ts, c.data FROM c ORDER BY c.message.ts DESC
但我不知道如何删除重复的设备记录。

这可以在 CosmosDB SQL 引擎中完成吗?

sql azure-cosmosdb azure-cosmosdb-sqlapi
4个回答
1
投票

您可以采取的另一种方法是使用 CosmosDb 中的触发器函数。这样做的好处是您不需要部署 Azure 函数,只需使用 SQL 即可获取最新的项目。例如,当你得到一个新的item时,你可以使用预触发来设置一个字段,如下:

latest = true
,同时将上一个最新item的latest字段更改为false。那么您的 SQL 查询只需要
WHERE latest = true
即可返回每个项目的最新记录。 这是一个具有总体思路的触发函数:

function setLatest() {  
    var context = getContext();  
    var request = context.getRequest();  
  
    // item to be created in the current operation  
    var itemToCreate = request.getBody();  
  
    // validate properties  
    if (!("latest" in itemToCreate)) {
        itemToCreate["latest"] = true;  
    }
    // update the old latest to false
    removeCurrentLatest(itemToCreate["id"],..., );
    // save the newer item that will be created  
    request.setBody(itemToCreate);
}

function removeCurrentLatest(id, ...) {
    var collection = getContext().getCollection();
    var collectionLink = collection.getSelfLink();
        // Get the document. We keep it in the same collection.
        var isAccepted = collection.queryDocuments
            (collectionLink, `SELECT * FROM root r WHERE r.id = "${id}" AND .... AND r.latest = true`,
            function (err, feed, options) {
            if (err) throw err;
            if (feed && feed[0] != null) 
            {
                var oldDoc = feed[0];
                oldDoc.latest = false;
                var isAccepted = collection.replaceDocument(oldDoc._self, oldDoc, function (err) {
                    if (err) throw err;
                    });
                if (!isAccepted) throw new Error("The call replaceDocument(oldDoc) returned false.");
            }
        });
        if (!isAccepted) throw new Error("The call queryDocuments for oldDoc returned false.");
    }

我已经删除了您可能想要包含的一些其他条件,以确保您选择该项目的正确的先前版本 - 希望如何为您的特定资产添加这些条件应该是显而易见的。 这是一篇关于在 CosmosDB 中使用触发器的优秀文章:http://www.johndowns.co.nz/blog/2018/1/30/cosmos-db-server-side-programming-with-typescript-part-4-触发器


0
投票

目前用一条 SQL 是不可能实现这一点的。

也许这可以作为替代方案。

首先,运行此 SQL

SELECT c.deviceId,max(c.message.ts) as lastest FROM c group by c.deviceId

然后就可以通过这个SQL获取数据了,

SELECT * FROM c WHERE c.deviceId = 'xxx' AND c.message.ts = xxxx


0
投票

感谢Mark Brown的评论,我发现以下内容似乎是此问题的正确解决方案。虽然不像一次性使用一些 SQL 那样优雅,但确实是所需要的。

https://learn.microsoft.com/en-us/samples/azure-samples/cosmosdb-materialized-views/real-time-view-cosomos-azure-functions/

本质上,您创建一个由 Cosmos 更改源触发的无服务器函数并更新物化视图,该视图本质上只是一个包含(在本例中)每个

data
最新的
deviceId
的文档。

特别是对于这种情况,它很可能会使用最新数据更新相应的

device
文档。


0
投票

我同意 Steve Johnson 的回答,但我认为,在给出答案时,Cosmos 可能不支持连接,但到今天为止,它已经支持了。

您需要运行 2 个查询才能执行相同的操作。

首先, 运行查询以获取每个设备 Id 的每个 c.message.ts 的最大值,就像 Steve Johnson 的答案一样。

SELECT c.deviceId, max(c.message.ts) as lastest FROM c group by c.deviceId

然后,将上述查询的响应与 Cosmos DB 中的 JOIN 结合使用。

SELECT s.id
FROM
    <Container_name> AS s
JOIN m IN (
    SELECT VALUE [
        {deviceId: 'uuid1', lastest: '1'},
        {deviceId: 'uuid2', lastest: '2'}  
    ]
)
WHERE
    s.deviceId = m.deviceId
    and s.message.ts = m.lastest
© www.soinside.com 2019 - 2024. All rights reserved.