鉴于我们有以下Neo4j架构(简化但它显示了重要的一点)。有两种类型的节点NODE
和VERSION
。 VERSION
已通过NODE
关系与VERSION_OF
s连接。 VERSION
个节点有两个属性from
和until
表示有效时间跨度 - 其中一个或两个都可以NULL
(在Neo4j术语中不存在)表示无限< / em>的。 NODE
可以通过HAS_CHILD
关系进行连接。同样,这些关系有两个属性from
和until
表示有效时间跨度 - 其中一个或两个都可以NULL
(在Neo4j术语中不存在)来表示无限。
编辑:VERSION
个节点和HAS_CHILD
关系的有效日期是独立的(即使示例巧合地显示它们已对齐)。
该示例显示了两个NODE
s A 和 B 。 A 有两个VERSION
s AV1 ,直到2017年6月30日, AV2 从7/1/17开始,而 B 只有一个版本 BV1 无限制。 B 通过HAS_CHILD
关系与 A 相关联,直到17/17/17。
现在的挑战是在一个特定时刻查询不是子(即根节点)的所有节点的图表。根据上面的示例,如果查询日期是例如,则查询应仅返回 B 。 17/1/17,但如果查询日期为例如,则应返回 B 和 A 8/1/17(因为 A 不再是 B 的孩子,从7/1/17开始)。
今天的当前查询大致类似于:
MATCH (n1:NODE)
OPTIONAL MATCH (n1)<-[c]-(n2:NODE), (n2)<-[:VERSION_OF]-(nv2:ITEM_VERSION)
WHERE (c.from <= {date} <= c.until)
AND (nv2.from <= {date} <= nv2.until)
WITH n1 WHERE c IS NULL
MATCH (n1)<-[:VERSION_OF]-(nv1:ITEM_VERSION)
WHERE nv1.from <= {date} <= nv1.until
RETURN n1, nv1
ORDER BY toLower(nv1.title) ASC
SKIP 0 LIMIT 15
这个查询通常工作得相对较好,但是当它用在大型数据集上时(与真实的生产数据集相比),它开始变慢。使用20-30k NODE
s(大约是VERSION
s的两倍)(实际)查询在Mac OS X上运行的小型docker容器上大约需要500-700 ms),这是可以接受的。但是使用1.5M NODE
s(大约是VERSION
s的两倍),(真实)查询在裸机服务器上运行时间超过1分钟(除了Neo4j之外别无其他)。这是不可接受的。
我们可以选择调整此查询吗?是否有更好的方法来处理NODE
的版本控制(我怀疑这里是性能问题)还是关系的有效性?我知道关系属性无法编入索引,因此可能有更好的模式来处理这些关系的有效性。
非常感谢任何帮助甚至是最轻微的暗示。
在answer from Michael Hunger之后编辑:
根节点的百分比:
使用当前示例数据集(1.5M节点),结果集包含大约2k行。那不到1%。
ITEM_VERSION
中的 MATCH
节点:
我们正在使用ITEM_VERSION
nv2
将结果集过滤到在给定日期没有连接其他ITEM
个节点的ITEM
个节点。这意味着要么不存在对于给定日期有效的关系,要么连接的项目不能具有对于给定日期有效的ITEM_VERSION
。我试图说明这一点:
// date 6/1/17
// n1 returned because relationship not valid
(nv1 ...)->(n1)-[X_HAS_CHILD ...6/30/17]->(n2)<-(nv2 ...)
// n1 not returned because relationship and connected item n2 valid
(nv1 ...)->(n1)-[X_HAS_CHILD ...]->(n2)<-(nv2 ...)
// n1 returned because connected item n2 not valid even though relationship is valid
(nv1 ...)->(n1)-[X_HAS_CHILD ...]->(n2)<-(nv2 ...6/30/17)
不使用关系类型:
这里的问题是该软件具有用户定义的模式,ITEM
节点通过自定义关系类型连接。由于我们在关系中不能有多种类型/标签,因此这类关系的唯一共同特征是它们都以X_
开头。这里没有简化的例子。是否会使用谓词type(r) STARTS WITH 'X_'
进行搜索?
答案 0 :(得分:2)
你在使用什么Neo4j版本。
在您的示例日期,您的1.5M节点中有多少百分比将作为根发现,如果您没有限制,那么有多少数据会回来?也许这个问题不在匹配中,而不是在最后的排序中?
我不确定为什么你的第一部分中有VERSION节点,至少你没有把它们描述为确定根节点的相关性。
您没有使用关系类型。
MATCH (n1:NODE) // matches 1.5M nodes
// has to do 1.5M * degree optional matches
OPTIONAL MATCH (n1)<-[c:HAS_CHILD]-(n2) WHERE (c.from <= {date} <= c.until)
WITH n1 WHERE c IS NULL
// how many root nodes are left?
// # root nodes * version degree (1..2)
MATCH (n1)<-[:VERSION_OF]-(nv1:ITEM_VERSION)
WHERE nv1.from <= {date} <= nv1.until
// has to sort all those
WITH n1, nv1, toLower(nv1.title) as title
RETURN n1, nv1
ORDER BY title ASC
SKIP 0 LIMIT 15
答案 1 :(得分:1)
我认为改进的良好开端是使用索引匹配节点,这样您就可以快速获得较小的相关节点子集进行搜索。你现在的方法必须每次检查你的所有:NODE以及它们之间的所有关系和模式,正如你所发现的那样,它们不会随着你的数据而扩展。
现在,图中唯一具有日期/时间属性的节点是您的:ITEM_VERSION节点,所以让我们从这些节点开始。您需要一个索引:ITEM_VERSION来自和直到快速查找的属性。
对于您的查找,空值将会出现问题,因为任何针对空值的不等式都会返回null,并且使用空值的大多数变通方法(使用COALESCE()或多个AND / OR用于空案例)似乎会阻止使用索引查找,这是我特别建议的一点。
我建议您使用min和max值替换from和until中的空值,这样可以让您利用索引查找来查找节点:
MATCH (version:ITEM_VERSION)
WHERE version.from <= {date} <= version.until
MATCH (version)<-[:VERSION_OF]-(node:NODE)
...
至少应该在开始时快速访问较小的节点子集以继续查询。