Cypher中的数据透视表类型查询(一次通过)

时间:2018-06-07 10:33:35

标签: neo4j cypher query-performance

我试图在一次传递中执行以下查询,但我得出结论认为这是不可能的,并且还会导致某种形式的“嵌套”结构,这在性能方面绝不是好消息。

但是我可能会在这里遗漏一些东西,所以我想我可能会问。

底层数据结构是两个实体A<---0:*--->B

之间的多对多关系

最终目标是获取在特定时间间隔内分配给实体B的对象的实体A的对象的次数占总分配的百分比

正是这个问题的后半部分引起了头痛。

实体A包含item_date字段 实体B包含item_category字段。

结果的显示可以扩展到一个表,其列是不同的item_date,行是不同的item_category 规范化计数。我只是为了清楚起见提到这一点,查询不必以确切的形式返回结果。

我的尝试:

with 12*30*24*3600 as window_length, "1980-1-1" as start_date, 
     "1985-12-31" as end_date 
     unwind range(apoc.date.parse(start_date,"s","yyyy-MM-dd"),apoc.date.parse(end_date,"s","yyyy-MM-dd"),window_length) as date_step
     match (a:A)<-[r:RELATOB]-(b:B) 
         where apoc.date.parse(a.item_date,"s","yyyy-MM-dd")>=date_step and apoc.date.parse(a.item_date,"s","yyyy-MM-dd")<(date_step+window_length)
         with window_length, date_step, count(r) as total_count unwind ["code_A", "code_B", "code_C"] as the_code [MATCH THE PATTERN AGAIN TO COUNT SPECIFIC `item_code` this time.

我发现很难在一次传递中表达这一点,因为在图形模式的定义之后它需要相当于两个独立的GROUP BY - 类子句。你不能并行表达这两个,所以你必须解开它们。我担心的是,这导致两个评估:一个用于总计数,一个用于部分计数。我试图优化的位是重写查询的某种方式,这样它就不必计算它之前已“捕获”的节点,但这对于将集合函数应用于集合的隐含方式来说非常困难。

基本上,任何不是聚合函数的属性都会成为分层变量。我必须在这里说一个简单的简单双重分层(“抓住一切,按item_date产生一个等级的计数,产生item_code的另一个计数水平”对我来说不起作用,因为没有办法控制window_length的宽度。这意味着我无法在item_code s的不同分配率的两个时间段之间进行比较,因为时间段不相等:(

请注意,检索item_code的计数然后在一段时间内(在cypher外部)对那些特定代码的总和进行标准化将不会导致准确的百分比,因为规范化将针对 item_code的特定子集而不是总数。

是否可以在一段时间内同时执行r计数,然后(以某种方式)重新使用已匹配的ab节点子集,以便现在评估b的具体(b:{item_code:the_code})-[r2:RELATOB]-(a) where a.item_date...的部分计数?

如果没有,那么我将转向下一个最快的事情,即执行两个独立的查询(一个用于总计数,一个用于部分)然后在外部进行除法:/。

1 个答案:

答案 0 :(得分:0)

Tomaz Bratanic在评论中提出的解决方案是(我认为)沿着这些方向:

with 1*30*24*3600 as window_length, 
     "1980-01-01" as start_date, 
     "1985-12-31" as end_date 
     unwind range(apoc.date.parse(start_date,"s","yyyy-MM-dd"),apoc.date.parse(end_date,"s","yyyy-MM-dd"),window_length) as date_step 
     unwind ["code_A","code_B","code_c"] as the_code 
         match (a:A)<-[r:RELATOB]-(b:B) 
             where apoc.date.parse(a.item_date,"s","yyyy-MM-dd")>=date_step and apoc.date.parse(a.item_category,"s","yyyy-MM-dd")<(date_step+window_length) 
         return the_code, date_step, tofloat(sum(case when b.item_category=code then 1 else 0 end)/count(r)) as perc_count order by date_step asc

此:

  1. 正在工作
  2. 它完全符合我的目标(经过一些小修改)
  3. 它甚至会将缺失值填充为零,因为ELSE 0即使没有计数数据也会强制为零。
  4. 但在现实条件下,它比我正在使用的重新匹配至少慢30秒(不是,请参见编辑)。 (不,这不是因为填充缺失数据时现在返回的额外数据,这是原始查询时间。)

    我认为可能值得在此附加查询计划:

    这是应用相同模式两次但快速方式的计划:

    enter image description here

    这是一次性执行计数但是这样做的计划:

    enter image description here

    我可能会看到时间尺度如何与输入中的数据进行比较,也许这两个是以不同的速率缩放,但此时,&#34;一次通过&#34;似乎已经慢了&#34;双通&#34;坦率地说,我看不出它如何能够通过更多数据获得更快的速度。这已经是在18个项目(约)中分配的3个类别的12个月的简单计数。

    希望这也可以帮助其他人。

    修改

    虽然我最初做过这个,但还有另一个修改,我没有包括第二次放松 AFTER 匹配的地方。这会将时间缩短20秒 &#34;双重匹配&#34;因为展开会影响返回,而不是现在变为同一查询的多次执行:

    with 1*30*24*3600 as window_length, 
         "1980-01-01" as start_date, 
         "1985-12-31" as end_date 
         unwind range(apoc.date.parse(start_date,"s","yyyy-MM-dd"),apoc.date.parse(end_date,"s","yyyy-MM-dd"),window_length) as date_step  
             match (a:A)<-[r:RELATOB]-(b:B) 
                 where apoc.date.parse(a.item_date,"s","yyyy-MM-dd")>=date_step and apoc.date.parse(a.item_category,"s","yyyy-MM-dd")<(date_step+window_length) 
                     unwind ["code_A","code_B","code_c"] as the_code
             return the_code, date_step, tofloat(sum(case when b.item_category=code then 1 else 0 end)/count(r)) as perc_count order by date_step asc
    

    这也是它的执行计划:

    enter image description here

    原创双人赛大约55790ms,一次性完成(两场比赛前都展开) 82306ms ,一次性完成(第二次放松 匹配后)<强> 23461ms