neo4j 关系属性
为了在Neo4j查询中获得最佳的遍历速度,我们应该使关系类型尽可能具体 。
让我们看一下几周前我在Skillsmatter上发表的“ 建模建议引擎建模 ”演讲中的一个例子。
我需要决定如何为成员和事件之间的“ RSVP”关系建模。 一个人可以对某个事件表示“是”或“否”,我想同时捕获这两个响应。
即我们可以选择:
和:
在确定模型时,我们主要需要考虑我们要编写的查询的类型。 我们不应该忘记更新模型,但以我的经验,查询图形要比更新图形花费更多的时间。
让我们依次看一下其中的每个:
我们要写什么查询?
第一个查询将使用以前的“是” RSVP作为将来事件的关注指标。 我们对此查询的“否” RSVP不感兴趣。
我从具有“ response”属性的通用RSVP关系类型开始,以区分“是”和“否”:
MATCH (member:Member {name: "Mark Needham"})
MATCH (futureEvent:Event) WHERE futureEvent.time >= timestamp()
MATCH (futureEvent)<-[:HOSTED_EVENT]-(group)OPTIONAL MATCH (member)-[rsvp:RSVPD {response: "yes"}]->(pastEvent)<-[:HOSTED_EVENT]-(group)
WHERE pastEvent.time < timestamp()RETURN group.name, futureEvent.name, COUNT(rsvp) AS previousEvents
ORDER BY previousEvents DESC
这运行得相当快,但是我很好奇是否可以通过更改为更具体的模型来使查询更快地运行。 使用更具体的关系类型,我们的查询显示为:
MATCH (member:Member {name: "Mark Needham"})
MATCH (futureEvent:Event) WHERE futureEvent.time >= timestamp()
MATCH (futureEvent)<-[:HOSTED_EVENT]-(group)OPTIONAL MATCH (member)-[rsvp:RSVP_YES]->(pastEvent)<-[:HOSTED_EVENT]-(group)
WHERE pastEvent.time < timestamp()RETURN group.name, futureEvent.name, COUNT(rsvp) AS previousEvents
ORDER BY previousEvents DESC
现在,我们可以分析查询并比较两种解决方案的数据库命中率:
RSVPD {response: "yes"}
Cypher version: CYPHER 2.3, planner: COST. 688635 total db hits in 232 ms.RSVP_YES
Cypher version: CYPHER 2.3, planner: COST. 559866 total db hits in 207 ms.
因此,通过使用更具体的关系类型,我们会获得一点收益。 数据库命中率较低的原因部分是因为我们不再需要在每个“ RSVP”属性上查找“ response”属性并检查其是否与“ yes”匹配。 我们还评估了较少的关系,因为我们只查看正向RSVP,负向被忽略。
我们的下一个查询可能是捕获成员发出的所有RSVP,并在事件旁边列出它们:
MATCH (member:Member {name: "Mark Needham"})-[rsvp:RSVPD]->(event)
WHERE event.time < timestamp()
RETURN event.name, event.time, rsvp.response
ORDER BY event.time DESC
MATCH (member:Member {name: "Mark Needham"})-[rsvp:RSVP_YES|:RSVP_NO]->(event)
WHERE event.time < timestamp()
RETURN event.name, event.time, CASE TYPE(rsvp) WHEN "RSVP_YES" THEN "yes" ELSE "no" END AS response
ORDER BY event.time DESC
再次,我们看到边际数据库命中率赢得了更具体的关系类型:
RSVPD {response: "yes"} / RSVPD {response: "no"}
Cypher version: CYPHER 2.3, planner: COST. 684 total db hits in 37 ms.RSVP_YES / RSVP_NO
Cypher version: CYPHER 2.3, planner: COST. 541 total db hits in 24 ms.
但是,查询非常麻烦,除非我们将响应存储为关系的属性,否则返回“ yes”或“ no”的代码会有些尴尬。 如果引入了我们选择排除的“ waitlist” RSVP,则更具体的方法查询将变得更加痛苦。
我们需要更新关系吗?
是! 在事件发生之前,用户可以更改其RSVP,因此我们需要能够进行处理。
让我们看一下使用两种模型处理RSVP更改时必须编写的查询:
通用关系类型
MATCH (event:Event {id: {event_id}})
MATCH (member:Member {id: {member_id}})
MERGE (member)-[rsvpRel:RSVPD {id: {rsvp_id}}]->(event)
ON CREATE SET rsvpRel.created = toint({mtime})
ON MATCH SET rsvpRel.lastModified = toint({mtime})
SET rsvpRel.response = {response}
具体关系类型
MATCH (event:Event {id: {event_id}})
MATCH (member:Member {id: {member_id}})FOREACH(ignoreMe IN CASE WHEN {response} = "yes" THEN [1] ELSE [] END |MERGE (member)-[rsvpYes:RSVP_YES {id: {rsvp_id}}]->(event)ON CREATE SET rsvpYes.created = toint({mtime})ON MATCH SET rsvpYes.lastModified = toint({mtime})MERGE (member)-[oldRSVP:RSVP_NO]->(event)DELETE oldRSVP
)FOREACH(ignoreMe IN CASE WHEN {response} = "no" THEN [1] ELSE [] END |MERGE (member)-[rsvpNo:RSVP_NO {id: {rsvp_id}}]->(event)ON CREATE SET rsvpNo.created = toint({mtime})ON MATCH SET rsvpNo.lastModified = toint({mtime})MERGE (member)-[oldRSVP:RSVP_YES]->(event)DELETE oldRSVP
)
如您所见,使用特定的关系类型时,更新RSVP的代码更加复杂,部分原因是Cypher还没有对条件的一流支持。
总而言之,对于我们的metup.com模型,我们通过使用更具体的关系类型获得了速度上的提高,但是却以一些更复杂的读取查询和更为复杂的更新查询为代价。
根据模型中关系的基数,您的里程可能会有所不同,但是值得进行一些分析以比较所有选项。
翻译自: https://www.javacodegeeks.com/2015/12/neo4j-specific-relationship-vs-generic-relationship-property.html
neo4j 关系属性