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 倍)!