我有一个类似于
的模式"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 引擎中完成吗?
您可以采取的另一种方法是使用 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-触发器
目前用一条 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
感谢Mark Brown的评论,我发现以下内容似乎是此问题的正确解决方案。虽然不像一次性使用一些 SQL 那样优雅,但确实是所需要的。
本质上,您创建一个由 Cosmos 更改源触发的无服务器函数并更新物化视图,该视图本质上只是一个包含(在本例中)每个
data
最新的 deviceId
的文档。
特别是对于这种情况,它很可能会使用最新数据更新相应的
device
文档。
我同意 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