Azure CosmosDB 查询资源管理器的结果与 Azure Functions 结果之间的差异
Difference among the Azure CosmosDB Query Explorer's results and the Azure Functions results
我在 CosmosDB 查询资源管理器上执行了以下查询。
SELECT c.id, c.created_at FROM c
WHERE c.created_at_epoch <= 1499871600 - 86400*31
AND (CEILING(1499871600/86400) - CEILING(c.created_at_epoch / 86400)) % 31 = 0
结果如下
[
{
"id": "70251cbf-44b3-4cd9-991f-81127ad78bca",
"created_at": "2017-05-11 18:46:16"
},
{
"id": "0fa31de2-4832-49ea-a0c6-b517d64ede85",
"created_at": "2017-05-11 18:48:22"
},
{
"id": "b9959d15-92e7-41c3-8eff-718c4ab2be6e",
"created_at": "2017-05-11 19:01:43"
}
]
看来问题不存在
接下来,我将静态定义的纪元值替换为占位符,以便将其用作 Azure Functions DocumentDB 输入绑定的 sqlQuery。然后,我将调制符号替换为 %modulationsymbol%
以避免
SELECT c.id, c.created_at FROM c
WHERE c.created_at_epoch <= {epoch} - 86400*31
AND (CEILING({epoch}/86400) - CEILING(c.created_at_epoch / 86400)) %modulationsymbol% 31 = 0
并且我将 modulationsymbol = %
定义为应用程序设置。
然后,我指定函数如下。
// index.js
module.exports = function (context, myQueueItem) {
context.log(context.bindings.members, myQueueItem);
context.done();
};
// function.json
{
"bindings": [
{
"name": "myQueueItem",
"type": "queueTrigger",
"direction": "in",
"queueName": "myqueue",
"connection": "MYSTORAGE"
},
{
"type": "documentDB",
"name": "members",
"databaseName": "myproject-db",
"collectionName": "member",
"sqlQuery": "SELECT c.id, c.created_at FROM c WHERE {epoch} - c.created_at_epoch >= 86400*31 AND (CEILING({epoch}/86400) - CEILING(c.created_at_epoch / 86400)) %modulationsymbol% 31 = 0",
"connection": "MYCOSMOSDB",
"direction": "in"
}
],
"disabled": true
}
之后,我触发了函数,结果如下。
2017-07-05T03:57:29.640 Function started (Id=d980521e-d23a-4bda-a730-57a236bcd011)
2017-07-05T03:57:30.594 [] { epoch: 1499871600 }
2017-07-05T03:57:30.594 Function completed (Success, Id=d980521e-d23a-4bda-a730-57a236bcd011, Duration=951ms)
看起来 context.bindings.members
是一个空列表。它不同于 CosmosDB 查询资源管理器的结果。
为什么会出现这种差异?
打印出{epoch}后,我发现它的类型是字符串,期望的类型是数字而不是字符串。这就是使用相同查询时得到空列表的原因。
要解决此问题,您可以先将类型转换为数字,然后再使用它来过滤查询结果。以下步骤供您参考。
第1步,创建一个可以将字符串转换为数字的UDF。脚本资源管理器->创建用户定义函数
function toNumber(ts) {
return parseInt(ts);
}
第2步,创建ConvertToNumber函数后,您可以使用它来将{epoch}的类型转换为数字。
SELECT c.id, c.created_at FROM c
WHERE c.created_at_epoch <= udf.ConvertToNumber({epoch}) - 86400*31
AND (CEILING(udf.ConvertToNumber({epoch})/86400) - CEILING(c.created_at_epoch / 86400)) %modulationsymbol% 31 = 0
如果您熟悉 C#,则可以使用 C# 创建函数。由于 C# 是一种强类型语言。我们可以定义一个 class 用于反序列化队列中的消息。它将在语言层转换类型。
public class EpochMessage
{
public int epoch { get; set; }
}
整个函数可能是这样的
using System;
public static void Run(EpochMessage myQueueItem, TraceWriter log, IEnumerable<dynamic> members)
{
log.Info(context.bindings.members, myQueueItem);
}
public class EpochMessage
{
public int epoch { get; set; }
}
我在 CosmosDB 查询资源管理器上执行了以下查询。
SELECT c.id, c.created_at FROM c
WHERE c.created_at_epoch <= 1499871600 - 86400*31
AND (CEILING(1499871600/86400) - CEILING(c.created_at_epoch / 86400)) % 31 = 0
结果如下
[
{
"id": "70251cbf-44b3-4cd9-991f-81127ad78bca",
"created_at": "2017-05-11 18:46:16"
},
{
"id": "0fa31de2-4832-49ea-a0c6-b517d64ede85",
"created_at": "2017-05-11 18:48:22"
},
{
"id": "b9959d15-92e7-41c3-8eff-718c4ab2be6e",
"created_at": "2017-05-11 19:01:43"
}
]
看来问题不存在
接下来,我将静态定义的纪元值替换为占位符,以便将其用作 Azure Functions DocumentDB 输入绑定的 sqlQuery。然后,我将调制符号替换为 %modulationsymbol%
以避免
SELECT c.id, c.created_at FROM c
WHERE c.created_at_epoch <= {epoch} - 86400*31
AND (CEILING({epoch}/86400) - CEILING(c.created_at_epoch / 86400)) %modulationsymbol% 31 = 0
并且我将 modulationsymbol = %
定义为应用程序设置。
然后,我指定函数如下。
// index.js
module.exports = function (context, myQueueItem) {
context.log(context.bindings.members, myQueueItem);
context.done();
};
// function.json
{
"bindings": [
{
"name": "myQueueItem",
"type": "queueTrigger",
"direction": "in",
"queueName": "myqueue",
"connection": "MYSTORAGE"
},
{
"type": "documentDB",
"name": "members",
"databaseName": "myproject-db",
"collectionName": "member",
"sqlQuery": "SELECT c.id, c.created_at FROM c WHERE {epoch} - c.created_at_epoch >= 86400*31 AND (CEILING({epoch}/86400) - CEILING(c.created_at_epoch / 86400)) %modulationsymbol% 31 = 0",
"connection": "MYCOSMOSDB",
"direction": "in"
}
],
"disabled": true
}
之后,我触发了函数,结果如下。
2017-07-05T03:57:29.640 Function started (Id=d980521e-d23a-4bda-a730-57a236bcd011)
2017-07-05T03:57:30.594 [] { epoch: 1499871600 }
2017-07-05T03:57:30.594 Function completed (Success, Id=d980521e-d23a-4bda-a730-57a236bcd011, Duration=951ms)
看起来 context.bindings.members
是一个空列表。它不同于 CosmosDB 查询资源管理器的结果。
为什么会出现这种差异?
打印出{epoch}后,我发现它的类型是字符串,期望的类型是数字而不是字符串。这就是使用相同查询时得到空列表的原因。
要解决此问题,您可以先将类型转换为数字,然后再使用它来过滤查询结果。以下步骤供您参考。
第1步,创建一个可以将字符串转换为数字的UDF。脚本资源管理器->创建用户定义函数
function toNumber(ts) {
return parseInt(ts);
}
第2步,创建ConvertToNumber函数后,您可以使用它来将{epoch}的类型转换为数字。
SELECT c.id, c.created_at FROM c
WHERE c.created_at_epoch <= udf.ConvertToNumber({epoch}) - 86400*31
AND (CEILING(udf.ConvertToNumber({epoch})/86400) - CEILING(c.created_at_epoch / 86400)) %modulationsymbol% 31 = 0
如果您熟悉 C#,则可以使用 C# 创建函数。由于 C# 是一种强类型语言。我们可以定义一个 class 用于反序列化队列中的消息。它将在语言层转换类型。
public class EpochMessage
{
public int epoch { get; set; }
}
整个函数可能是这样的
using System;
public static void Run(EpochMessage myQueueItem, TraceWriter log, IEnumerable<dynamic> members)
{
log.Info(context.bindings.members, myQueueItem);
}
public class EpochMessage
{
public int epoch { get; set; }
}