如何使用 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 那样优雅,但这确实是所需要的。
本质上,您创建了一个无服务器功能,它由 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
我有一个类似于
的模式"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 那样优雅,但这确实是所需要的。
本质上,您创建了一个无服务器功能,它由 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