Neo4j Cypher回归最多连续“传球”

时间:2017-12-06 09:56:25

标签: neo4j cypher

我试图从图表数据库中将连续通过最多的学生返回到一系列考试中。

以下是我当前的代码,但不知道我可以从当前状态中取出它来解决我的问题。

MATCH (s:Student)-[r:TAKEN]->(e:Exam)
RETURN s.name, e.date,
  CASE 
    WHEN r.score >= e.pass_mark THEN 1
  END as pass
ORDER BY e.date

我想要一个基本的表格,可以显示学生连续几次连续传球。

例如:

| student   | pass/fail |
| joe       | pass      |
| matt      | pass      |
| joe       | fail      |
| matt      | pass      |    
| joe       | pass      |
| matt      | pass      |
| joe       | pass      |
| matt      | fail      |

我希望我的查询结果能够连续显示每个学生和他们的最高连胜纪录。

| student  | passes in a row |
| joe      | 2               |
| matt     | 3               |

我一直在玩CASE但是还没有找到一个好的解决方案,目前,它只会列出每个考试的所有学生,如果他们通过了它就会得到1。

2 个答案:

答案 0 :(得分:1)

这是一个棘手的问题,据我所知,只有Cypher无法完成,但APOC Procedures中有一个程序可以提供帮助。

apoc.coll.split()采用一个集合和一个值进行拆分,并为每个生成的子集合生成记录。基本上,我们收集每个学生的有序结果,分为失败以获得连续通过的集合,然后从这些集合的大小获得最大连续通过:

MATCH (s:Student)-[r:TAKEN]->(e:Exam)
WITH s, r.score >= e.pass_mark as passed
ORDER BY e.date
WITH s, collect(passed) as resultsColl
CALL apoc.coll.split(resultsColl, false) YIELD value
WITH s, max(size(value)) as consecutivePasses
RETURN s.name as student, consecutivePasses

答案 1 :(得分:1)

你可以用普通的Cypher来做,但我认为它非常实用 - 你基本上需要用reduce编写一个程序。

基本上,"分裂"工作原理如下:初始化一个空的累加器列表并通过遍历通过/失败列表来计算条纹,检查当前元素是否与前一个元素相同。例如ViewModel保留条纹,['pass', 'pass']打破它。如果它中断(就像在列表的开头一样),则将新元素追加到累加器。如果保持,则将新元素附加到累加器的最后一个元素,例如使用新的['pass', 'fail']'fail'变为[['pass', 'pass'], ['fail']]

[['pass', 'pass'], ['fail', 'fail]]

在步骤(1)中,这计算条纹,例如:

UNWIND
  [
    ['joe',  'pass'],
    ['matt', 'pass'],
    ['joe',  'fail'],
    ['matt', 'pass'],
    ['joe',  'pass'],
    ['matt', 'pass'],
    ['joe',  'pass'],
    ['matt', 'fail']
  ] AS row
WITH row[0] AS s, row[1] AS passed
WITH s, collect(passed) AS p
WITH s, reduce(acc = [], i IN range(0, size(p) - 1) | 
    CASE p[i] = p[i-1]
      WHEN true THEN [j IN range(0, size(acc) - 1) |
          CASE j = size(acc) - 1
            WHEN true THEN acc[j] + [p[i]]
            ELSE acc[j]
          END
        ]
      ELSE acc + [[p[i]]]
    END
  ) AS streaks // (1)
UNWIND streaks AS streak
WITH s, streak
WHERE streak[0] <> 'fail'
RETURN s, max(size(streak)) AS consecutivePasses // (2)

在(2)中,它给出了:

╒══════╤═════════════════════════════════╕
│"s"   │"streaks"                        │
╞══════╪═════════════════════════════════╡
│"matt"│[["pass","pass","pass"],["fail"]]│
├──────┼─────────────────────────────────┤
│"joe" │[["fail"],["pass","pass"]]       │
└──────┴─────────────────────────────────┘

当然,在这种特殊情况下,没有必要进行拆分:简单计数就足够了。但在99%的实际情况中,APOC是要走的路,所以我没有打扰优化这个解决方案。