AQL优化——按月遍历
AQL Optimization - traversal by month
在 ArangoDB 中,我有一个图表,其中包含配置文件的消息。出于报告目的,我需要汇总过去 12 个月每个月特定类别消息的消息数。
每条消息的日期和类别位于配置文件和消息之间的每条边上。
目前,我有:
for mo in 0..11
let month = DATE_MONTH(DATE_SUBTRACT(DATE_NOW(),mo,"month"))
let msgCount = count(
for v, e in outbound "profileId" graph 'MyGraph'
filter e.category == "myCategory" && DATE_MONTH(e.date) == month
return 1
)
return {month: month, msgCount: msgCount}
此查询的执行时间超过一秒,对于此 Web 应用来说有点慢,因为我需要为多个类别创建和可视化此报告。
是否有任何优化可以更快地产生相同的结果?
正在分析查询
使用 db._explain()
显示如下:
Execution plan:
Id NodeType Est. Comment
1 SingletonNode 1 * ROOT
2 CalculationNode 1 - LET #7 = 0 .. 11 /* range */ /* simple expression */
3 EnumerateListNode 12 - FOR mo IN #7 /* list iteration */
4 CalculationNode 12 - LET month = DATE_MONTH(DATE_SUBTRACT(DATE_NOW(), mo, "month")) /* v8 expression */
11 SubqueryNode 12 - LET #4 = ... /* subquery */
5 SingletonNode 1 * ROOT
9 CalculationNode 1 - LET #11 = 1 /* json expression */ /* const assignment */
6 TraversalNode 1 - FOR v /* vertex */, e /* edge */ IN 1..1 /* min..maxPathDepth */ OUTBOUND 'profileId' /* startnode */ GRAPH 'MyGraph'
7 CalculationNode 1 - LET #9 = ((e.`category` == "myCategory") && (DATE_MONTH(e.`date`) == month)) /* v8 expression */
8 FilterNode 1 - FILTER #9
10 ReturnNode 1 - RETURN #11
13 CalculationNode 12 - LET #13 = { "month" : month, "msgCount" : COUNT(#4) } /* simple expression */
14 ReturnNode 12 - RETURN #13
Indexes used:
none
Traversals on graphs:
Id Depth Vertex collections Edge collections Filter conditions
6 1..1 persons knows
Optimization rules applied:
Id RuleName
1 move-calculations-up
2 remove-unnecessary-calculations
它可以告诉我们以下内容:
- 在遍历中我们使用了 V8 表达式,这很昂贵。
- 由于您正在执行一个复杂的过滤器表达式,因此无法在遍历器内部执行 - 因此在 遍历完成后应用过滤。
你可以做些什么来改善这种情况
您应该找到一种方法将复杂的计算移出迭代。
虽然使用 DATE_*
函数可能很方便,但速度并不快。您应该预先计算出要过滤的字符串 YEAR:MONTH ,然后在查询中对计算出的字符串进行范围比较:
FILTER e.date > '2016:04:' && e.date < '2016:05:'
Nate 的实现:
for mo in 0..11
let month = date_subtract(concat(left(date_iso8601(date_now()),7),'-01T00:00:00.000Z'), mo, "month")
let nextMonth = date_subtract(concat(left(date_iso8601(date_now()),7),'-01T00:00:00.000Z'), mo-1, "month")
let monthNumber = date_month(month)
let msgCount = count(
for v, e in outbound "profileId" graph "MyGraph"
filter e.category == 'myCategory' && e.date > month && e.date < nextMonth
return 1
)
return {month: monthNumber, msgCount: msgCount}
这显着加快了查询速度(快了 5 倍)!
在 ArangoDB 中,我有一个图表,其中包含配置文件的消息。出于报告目的,我需要汇总过去 12 个月每个月特定类别消息的消息数。
每条消息的日期和类别位于配置文件和消息之间的每条边上。
目前,我有:
for mo in 0..11
let month = DATE_MONTH(DATE_SUBTRACT(DATE_NOW(),mo,"month"))
let msgCount = count(
for v, e in outbound "profileId" graph 'MyGraph'
filter e.category == "myCategory" && DATE_MONTH(e.date) == month
return 1
)
return {month: month, msgCount: msgCount}
此查询的执行时间超过一秒,对于此 Web 应用来说有点慢,因为我需要为多个类别创建和可视化此报告。
是否有任何优化可以更快地产生相同的结果?
正在分析查询
使用 db._explain()
显示如下:
Execution plan:
Id NodeType Est. Comment
1 SingletonNode 1 * ROOT
2 CalculationNode 1 - LET #7 = 0 .. 11 /* range */ /* simple expression */
3 EnumerateListNode 12 - FOR mo IN #7 /* list iteration */
4 CalculationNode 12 - LET month = DATE_MONTH(DATE_SUBTRACT(DATE_NOW(), mo, "month")) /* v8 expression */
11 SubqueryNode 12 - LET #4 = ... /* subquery */
5 SingletonNode 1 * ROOT
9 CalculationNode 1 - LET #11 = 1 /* json expression */ /* const assignment */
6 TraversalNode 1 - FOR v /* vertex */, e /* edge */ IN 1..1 /* min..maxPathDepth */ OUTBOUND 'profileId' /* startnode */ GRAPH 'MyGraph'
7 CalculationNode 1 - LET #9 = ((e.`category` == "myCategory") && (DATE_MONTH(e.`date`) == month)) /* v8 expression */
8 FilterNode 1 - FILTER #9
10 ReturnNode 1 - RETURN #11
13 CalculationNode 12 - LET #13 = { "month" : month, "msgCount" : COUNT(#4) } /* simple expression */
14 ReturnNode 12 - RETURN #13
Indexes used:
none
Traversals on graphs:
Id Depth Vertex collections Edge collections Filter conditions
6 1..1 persons knows
Optimization rules applied:
Id RuleName
1 move-calculations-up
2 remove-unnecessary-calculations
它可以告诉我们以下内容:
- 在遍历中我们使用了 V8 表达式,这很昂贵。
- 由于您正在执行一个复杂的过滤器表达式,因此无法在遍历器内部执行 - 因此在 遍历完成后应用过滤。
你可以做些什么来改善这种情况
您应该找到一种方法将复杂的计算移出迭代。
虽然使用 DATE_*
函数可能很方便,但速度并不快。您应该预先计算出要过滤的字符串 YEAR:MONTH ,然后在查询中对计算出的字符串进行范围比较:
FILTER e.date > '2016:04:' && e.date < '2016:05:'
Nate 的实现:
for mo in 0..11
let month = date_subtract(concat(left(date_iso8601(date_now()),7),'-01T00:00:00.000Z'), mo, "month")
let nextMonth = date_subtract(concat(left(date_iso8601(date_now()),7),'-01T00:00:00.000Z'), mo-1, "month")
let monthNumber = date_month(month)
let msgCount = count(
for v, e in outbound "profileId" graph "MyGraph"
filter e.category == 'myCategory' && e.date > month && e.date < nextMonth
return 1
)
return {month: monthNumber, msgCount: msgCount}
这显着加快了查询速度(快了 5 倍)!