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

How do I get the latest record for each item in CosmosDB using SQL

我有一个类似于

的模式
"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 是不可能实现的。

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

首先,运行这个SQLSELECT 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

获取数据

感谢Mark Brown的评论,我发现以下似乎是解决此问题的正确方法。不像只使用一些 SQL 来代替 one-off 那样优雅,但这确实是所需要的。

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

本质上,您创建了一个无服务器功能,它由 Cosmos 更改提要触发并更新物化视图,它本质上只是一个文档,其中(在本例中)每个文档都是最新的 data deviceId.

特别针对这种情况,它很可能会用最新数据更新相应的 device 文档。

您可以采用的另一种方法是在 CosmosDb 中使用触发器函数。这样做的好处是您无需部署 Azure 函数,只需使用 SQL 即可获取最新的项目。例如,当你得到一个新的item时,你可以使用pre-trigger设置一个字段如下:latest = true,同时将之前的最新item的latest字段改为false。那么您的 SQL 查询只需要 WHERE latest = true 到 return 每个项目的最新记录。 这是一个具有总体思路的触发函数:

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.");
    }

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