问题:
创建以下MATCH
语句的最佳性能是什么?为什么?
详细问题:
我们假设我们有一个Place
节点,其中包含可变数量的属性,需要根据其类别从可能数十亿个节点中查找节点。我试图围绕每个查询的表现进行总结,这证明是非常困难的。
可能的查询:
使用Place
查找匹配property
节点:
MATCH (entity:Place { category: "Food" })
将Place
节点与isCategory
节点的Food
关系匹配:
MATCH (entity:Place)-[:isCategory]->(category:Food)
将Place
节点与Food
节点的Category
关系匹配:
MATCH (entity)-[category:Food]->(:Category)
将Food
节点与isCategoryFor
节点的Place
关系匹配:
MATCH (category:Food)-[:isCategoryFor]->(entity:place)
显然所有的变化都介于两者之间。关系方向也是相反的。
更复杂:
让我们更复杂一点,并说我们现在需要使用多个类别查找所有Place
个节点。例如:查找类别为Place
或Food
的所有Bar
个节点
我们是否只会谈论另一个MATCH
声明?如果没有,那么最有效的路线是什么?
额外:
是否有工具可以帮助我描述遍历过程并告诉我最佳选择方法?
答案 0 :(得分:6)
如果我正确理解您的域名,我建议您自己将Category
转换为节点。
MERGE (:Category {name:"Food"})
MERGE (:Category {name:"Bar"})
MERGE (:Category {name:"Park"})
将每个Place
节点连接到它所属的Category
。
MERGE (:Place {name:"Central Park"})-[:IS_A]->(:Category {name:"Park"})
MERGE (:Place {name:"Joe's Diner"})-[:IS_A]->(:Category {name:"Food"})
MERGE (:Place {name:"Joe's Diner"})-[:IS_A]->(:Category {name:"Bar"})
然后,如果您想查找属于Place
的{{1}},那么它可以非常快。首先匹配类别,然后分支到与类别相关的位置。
Category
您的类别数量相对有限,因此匹配该类别会很快。然后,由于Neo4j实际存储数据的方式,可以快速找到与该类别相关的所有地点。
更复杂
在多个类别中查找地点也很容易。
MATCH (c:Category {name:"Bar"}), (c)<-[:IS_A]-(p:Place)
RETURN p
同样,您只是先匹配类别(快速,因为它们不多),然后分支到连接的位置。
使用索引
如果你想要快速,你需要在有意义的地方使用索引。在这个例子中,我将在类别的MATCH (c:Category) WHERE c.name = "Bar" OR c.name = "Food", (c)<-[:IS_A]-(p:Place)
RETURN p
属性上使用索引。
name
或者更好的是,对类别名称使用唯一性约束,这将对它们编制索引并防止重复。
CREATE INDEX ON :Category(name)
索引(和唯一性)会使查询速度产生大差异。
为什么这是最快的
Neo4j以非常紧凑,快速访问的格式存储节点和关系。拥有节点或关系后,获取相邻关系或节点的速度非常快。但是,它分别存储每个节点(和关系)的属性,这意味着查看属性相对较慢。
目标是尽快到达起始节点。在那里,遍历相关实体很快。如果您只有1,000个类别,但是您有10亿个位置,那么选择单个CREATE CONSTRAINT ON (c:Category) ASSERT c.name IS UNIQUE
比单个Category
更快。一旦有了这个起始节点,进入相关节点将非常有效。
其他选项
只是为了强化,这就是让你的其他选择更慢或更糟的原因。
在第一个示例中,您将查看每个节点上的属性以查找匹配项。属性查找很慢,你做了十亿次。索引可以帮助解决这个问题,但它仍然需要做很多工作。此外,您实际上是在每个十亿个地方复制类别数据,而不是利用Neo4j的优势。
在所有其他示例中,您的数据模型看起来很奇怪。 “食物”,“酒吧”,“公园”等都是类别的所有实例,而不是单独的类型。它们应该都是自己的节点,但它们都应该有Place
标签,因为它们就是这样。此外,类别是的东西,因此它们应该是节点。关系描述了事物之间的连接。以这种方式使用类别是没有意义的。
我希望这有帮助!