知识库

如何在 Cypher 中检查时间范围重叠

Neo4j 3.4 在 Cypher 中引入了时间类型,因此现在我们也拥有日期、dateTime 以及它们的本地版本,还有持续时间。

虽然我们没有专门的时间范围类型,但可以使用两个时间点作为范围的开始和结束。

检查某个时间点是否落在时间范围内很容易,但要计算两个时间范围是否重叠则要复杂得多。

本文提供了一个用于检查两个时间范围是否重叠的简单逻辑公式,并给出了在 Cypher 中应用该公式的示例。

我们将在示例中使用一个简单的图

CREATE (:Event {id:1, start:date(), end:date() + duration({days:5})}),
       (:Event {id:2, start:date() + duration({days:3}), end:date() + duration({days:8})})

事件 2 的开始和结束日期均比事件 1 晚 3 天,两个事件是重叠的。

检查时间范围重叠的简化公式

时间范围重叠有 4 种可能的情况,排除完全相同或仅相切(即一个结束正好等于另一个开始)的情况(对于这些情况我们可以使用包含性不等式)。

虽然我们不在此详述细节,但重点是计算重叠通常并非 trivial(简单),很容易写出遗漏某些重叠情况的计算,从而导致错误结果。

幸好有一个逻辑化简(已在此证明),最终相对简洁。

Max(StartA, StartB) <= Min(EndA, EndB)

要使范围重叠,较晚的开始时间必须在较早的结束时间之前(或恰好相同)。

Cypher 没有可直接使用的标量 min() 与 max() 函数(它们都是聚合函数,不能满足我们的需求),因此需要采用其他方法。

使用 CASE 的纯 Cypher 方法

我们可以利用 CASE 功能来实现标量 min() 与 max() 运算。

MATCH (e1:Event {id:1}), (e2:Event {id:2})
WITH CASE WHEN e1.start >= e2.start THEN e1.start ELSE e2.start END as maxStart,
     CASE WHEN e1.end <= e2.end THEN e1.end ELSE e2.end END as minEnd
RETURN maxStart <= minEnd as rangesOverlap

上述返回 true,因此两个范围确实重叠。

我们可以在 RETURN 中直接使用 CASE 计算,但将 min() 与 max() 的计算与重叠公式的应用分开,会更易于阅读和理解。

使用集合函数的 APOC 方法

APOC Procedures 提供了一些用于计算集合最大值和最小值的函数,apoc.coll.max()apoc.coll.min()

虽然这些看起来是合适的工具,但仍有一个问题:截至 2020 年 4 月,这些函数尚不支持时间类型,不过修复即将推出。

下面是修复完成后该怎么写的示例

MATCH (e1:Event {id:1}), (e2:Event {id:2})
RETURN apoc.coll.max([e1.start, e2.start]) <= apoc.coll.min([e1.end, e2.end]) as rangesOverlap

在此之前,还有另一种变通方法(除上述纯 Cypher CASE 外),即比较 epochMillis 值,但这要求我们使用 dateTime 类型。如果只有日期,可以通过 dateTime({date:date()}) as dateTimeFromADate 将其转换为 dateTime。

由于之前示例节点的属性是日期类型,我们需要进行上述转换。

MATCH (e1:Event {id:1}), (e2:Event {id:2})
WITH apoc.coll.max([dateTime({date:e1.start}).epochMillis, dateTime({date:e2.start}).epochMillis]) as maxStart,
     apoc.coll.min([dateTime({date:e1.end}).epochMillis, dateTime({date:e2.end}).epochMillis]) as minEnd
RETURN maxStart <= minEnd as rangesOverlap

你可能会认为如果有一个 overlap() 之类的函数可以直接调用、接受范围的开始和结束时间会更简单,我们也认同这一点。

我们正在开发一个 APOC 函数,届时可以大幅简化这些检查。

© . This site is unofficial and not affiliated with Neo4j, Inc.