用于执行日期比较的简单 Neptune Gremlin 查询因大型连接而降级

Simple Neptune Gremlin query to perform date comparison degrades due to large join

我们有一个包含客户和产品顶点的图表。对于给定的产品,我们想知道有多少客户 在 DATE 之前注册并购买了该产品。我的查询看起来像

g.V('PRODUCT_GUID') // get product vertex
  .out('product-customer') // get all customers who ever bought this product
  .has('created_on', gte(datetime('2020-11-28T00:33:44.536Z'))) // see if the customer was created after a given date
  .count() // count the results

这个查询非常慢,所以我查看了 neptune 分析器,发现了一些奇怪的东西。下面是完整的分析器输出。忽略探查器中经过的时间。这是在多次尝试同一个查询之后,所以缓存是热的。在野外,可能需要 45 秒或更长时间。

*******************************************************
                Neptune Gremlin Profile
*******************************************************

Query String
==================
g.V('PRODUCT_GUID').out('product-customer').has('created_on', gte(datetime('2020-11-28T00:33:44.536Z'))).count()

Original Traversal
==================
[GraphStep(vertex,[PRODUCT_GUID]), VertexStep(OUT,[product-customer],vertex), HasStep([created_on.gte(Sat Nov 28 00:33:44 UTC 2020)]), CountGlobalStep]

Optimized Traversal
===================
Neptune steps:
[
    NeptuneCountGlobalStep {
        JoinGroupNode {
            PatternNode[(?1=<PRODUCT_GUID>, ?5=<product-customer>, ?3, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=30586, expectedTotalOutput=30586, indexTime=0, joinTime=14, numSearches=1, actualTotalOutput=13424}
            PatternNode[(?3, <created_on>, ?7, ?) . project ask . CompareFilter(?7 >= Sat Nov 28 00:33:44 UTC 2020^^<DATETIME>) .], {estimatedCardinality=1285574, indexTime=10, joinTime=140, numSearches=13424}
        }, annotations={path=[Vertex(?1):GraphStep, Vertex(?3):VertexStep], joinStats=true, optimizationTime=0, maxVarId=8, executionTime=165}
    }
]

Physical Pipeline
=================
NeptuneCountGlobalStep
    |-- StartOp
    |-- JoinGroupOp
        |-- SpoolerOp(1000)
        |-- DynamicJoinOp(PatternNode[(?1=<PRODUCT_GUID>, ?5=<product-customer>, ?3, ?6) . project ?1,?3 . IsEdgeIdFilter(?6) .], {estimatedCardinality=30586, expectedTotalOutput=30586})
        |-- SpoolerOp(1000)
        |-- DynamicJoinOp(PatternNode[(?3, <created_on>, ?7, ?) . project ask . CompareFilter(?7 >= Sat Nov 28 00:33:44 UTC 2020^^<DATETIME>) .], {estimatedCardinality=1285574})

Runtime (ms)
============
Query Execution: 164.996

Traversal Metrics
=================
Step                                                               Count  Traversers       Time (ms)    % Dur
-------------------------------------------------------------------------------------------------------------
NeptuneCountGlobalStep                                                 1           1         164.919   100.00
                                            >TOTAL                     -           -         164.919        -

Predicates
==========
# of predicates: 131

Results
=======
Count: 1
Output: [22]


Index Operations
================
Query execution:
    # of statement index ops: 13425
    # of unique statement index ops: 13425
    Duplication ratio: 1.0
    # of terms materialized: 0

特别是

DynamicJoinOp(PatternNode[(?3, <created_on>, ?7, ?) . project ask . CompareFilter(?7 >= Sat Nov 28 00:33:44 UTC 2020^^) .], {estimatedCardinality=1285574})

这一行让我感到惊讶。我读这篇文章的方式是,海王星忽略了来自“.out('product-customer')”的顶点以满足“.has('created_on'...)”的要求,而是加入了在每个具有 created_on 属性的客户顶点上。

我原以为基数只是具有产品优势的客户数量,而不是每个客户。

我想知道是否有办法只运行比较来自“out('product-customer')”步骤的客户。

Neptune实际上必须解决第一个模式,

(?1=<PRODUCT_GUID>, ?5=<product-customer>, ?3, ?6)

在解决第二个问题之前,

(?3, <created_on>, ?7, ?)

每个四边形模式是由至少两个字段绑定的索引查找。因此,第一次查找使用 Neptune 中由主题(ID)和谓词(边缘标签)绑定的 SPOG 索引。这将 return 一组对象(产品-客户边缘另一端的顶点的顶点 ID)并通过下一个模式的 ?3 变量引用它们。

在下一个模式中,这些顶点 ID (?3) 与谓词 (属性 key of created-on) 绑定以评估日期范围的条件。因为这是条件评估,所以必须评估 ?3 集合中的每个顶点(必须读取每个顶点上的每个 'created-on' 属性)。