【问题标题】:Neo4j Cypher return most consecutive "passes"Neo4j Cypher 返回最多连续“通过”
【发布时间】:2017-12-06 09:56:24
【问题描述】:

我正在尝试从图形数据库中返回连续通过一系列考试的学生。

以下是我当前的代码,但不确定我可以从当前状态中将其从何处获取来解决我的问题。

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。

【问题讨论】:

  • 让我理解您的问题:您的示例查询正在返回您问题的第一个示例输出,对吗?另外,您需要一个返回第二个样本输出的查询(学生,连续通过),对吧?
  • 另外,提供一些示例数据的console 是个好主意。

标签: neo4j cypher


【解决方案1】:

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

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

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)

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

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

在 (2) 中,它给出:

╒══════╤═══════════════════╕
│"s"   │"consecutivePasses"│
╞══════╪═══════════════════╡
│"matt"│3                  │
├──────┼───────────────────┤
│"joe" │2                  │
└──────┴───────────────────┘

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

【讨论】:

  • 很好,我很好奇纯密码解决方案会是什么样子!
【解决方案2】:

这是一个棘手的问题,据我所知,仅使用 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

【讨论】:

  • 谢谢,这解决了我的问题,我对 APOC 不熟悉,所以一定要仔细阅读。
  • 你可以用普通的 Cypher 来做,你只是不想——看我的回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-28
  • 1970-01-01
相关资源
最近更新 更多