知识库

使用计数存储进行快速计数

Neo4j 维护一个事务性计数存储,用于保存各种事物的计数元数据。

计数存储用于通知查询规划器,以便它能就如何规划查询做出明智的选择。

从计数存储获取计数是常数时间操作,因此如果您想获取可以从计数存储中获取的计数,可以快速查询。

您可以通过查看查询的 EXPLAIN 的查询计划来判断查询中是否使用了计数存储。您应该会看到 NodeCountFromCountStoreRelationshipCountFromCountStore 运算符。

计数存储查询的限制

根据定义,获取计数的查询必须包含 count() 聚合。

不能存在 WHERE 子句,匹配模式中也不能有任何内联属性。

由于查询规划器的限制,只有当 count() 聚合单独出现在 WITH 或 RETURN 子句中时,才会利用计数存储。如果任何其他变量与 count() 聚合同时在作用域内,计数存储将不会被使用。

如前所述,您可以通过检查查询计划中是否存在 NodeCountFromCountStoreRelationshipCountFromCountStore 来判断是否使用了计数存储。

在同一查询中需要多个计数时的解决方案将在文章末尾讨论。

节点计数

您可以使用计数存储来获取数据库中所有节点的计数

MATCH (n)
RETURN count(n) as count

您也可以获取给定标签的所有节点的计数

MATCH (n:Person)
RETURN count(n) as count

变量对于这类查询是可选的,因此您可以省略它们并改用 count(*),结果相同

MATCH ()
RETURN count(*) as count

以及

MATCH (:Person)
RETURN count(*) as count

限制 - 不能使用计数存储查询具有多个标签的节点

当查询具有多个标签的节点时,将不使用计数存储,因为这些计数未在计数存储中进行跟踪。

以下情况将使用计数存储

MATCH (n:Person:Director)
RETURN count(n) as count

关系计数

计数存储还保存关系计数元数据,此处使用的模式必须描述单个关系模式。请注意,查询必须在匹配模式中使用有方向的关系才能使用计数存储,不要省略方向。

无论是否存在关系类型,都可以使用计数存储

MATCH ()-[r]->()
RETURN count(r) as count
MATCH ()-[r:ACTED_IN]->()
RETURN count(r) as count

当查询多种类型的关系时,计数存储也将被使用,这只会将每种类型的计数相加

MATCH ()-[r:ACTED_IN|DIRECTED]->()
RETURN count(r) as count

与节点一样,此处变量是可选的,可以改用 count(*)

MATCH ()-[:ACTED_IN]->()
RETURN count(*) as count

到/来自单个标签节点的关系统计

计数存储还保留了关于单个末端节点标签的关系计数。以下查询将从计数存储中获取其计数。

MATCH ()-[r:ACTED_IN]->(:Movie)
RETURN count(r) as count
MATCH (:Person)-[r:ACTED_IN]->()
RETURN count(r) as count
MATCH ()-[r]->(:Movie)
RETURN count(r) as count

限制 - 不能在起始节点和结束节点上同时存在标签时使用计数存储

计数存储不保留关于起始节点和结束节点上标签的元数据。

以下情况将使用计数存储

MATCH (:Person)-[r:ACTED_IN]->(:Movie)
RETURN count(r) as count

在单个查询中获取多个计数

在您希望在单个查询中从计数存储中获取多个计数的情况下,您可能会遇到本文开头提到的限制:count() 聚合必须单独出现在 WITH 或 RETURN 行中才能使用计数存储。

对于此限制,有两种值得注意的解决方案。

使用 UNION ALL 查询获取计数

如果我们使用计数存储分别查询计数,然后将它们 UNION 到一起,只需额外做一点工作即可获得所需的计数

MATCH (n:Person)
WITH count(n) as count
RETURN 'Person' as label, count
UNION ALL
MATCH (n:Movie)
WITH count(n) as count
RETURN 'Movie' as label, count

请注意,我们需要存在另一个变量来提供上下文,但我们必须在获取 count() 之后才引入该变量,因为在聚合点存在额外变量否则会阻止使用计数存储。

或者,我们可以返回一个映射结构,其中包含标签类型及其关联的计数

MATCH (n:Person)
RETURN {label:'Person', count: count(n)} as info
UNION ALL
MATCH (n:Movie)
RETURN {label:'Movie', count: count(n)} as info

使用 apoc.cypher.run() 动态获取每个标签/类型的计数

apoc.cypher.run() 可以用于每行执行单个 Cypher 查询,这使您能够每行从计数存储中获取计数。

结合获取节点标签或关系类型的调用,这可以是一种有效的方法,同时自动快速地获取多个计数

对于标签

CALL db.labels() YIELD label
CALL apoc.cypher.run('MATCH (:`'+label+'`) RETURN count(*) as count',{}) YIELD value
RETURN label, value.count

对于关系

CALL db.relationshipTypes() YIELD relationshipType as type
CALL apoc.cypher.run('MATCH ()-[:`'+type+'`]->() RETURN count(*) as count',{}) YIELD value
RETURN type, value.count

使用 APOC Procedures 中的 apoc.meta.stats()

APOC Procedures 包含 元数据过程,可用于一次性访问几乎所有计数存储数据。

您需要选择并显示 apoc.meta.stats() 调用中您想要的数据。

内容

apoc.meta.stats() 调用将 YIELD 以下值

labelCount - 图中标签的数量。

relTypeCount - 图中关系类型的数量。

propertyKeyCount - 图中属性键的数量。

nodeCount - 图中节点总数。

relCount - 图中关系总数。

labels - 每个标签及其该标签节点计数的映射。

relTypes - 每个关系模式(仅限有类型关系,包括一端带有标签的模式)及其关联计数的映射。

relTypesCount - 每个关系类型及其该类型计数的映射。

stats - 包含上述所有计数数据的映射。

用法

labels 计数本身通常最有用,但类似的方法也可用于其他计数

CALL apoc.meta.stats() YIELD labels
RETURN labels

这可能会返回一个类似以下内容的映射

{
  "Movie": 38,
  "Word": 12,
  "News": 2,
  "Director": 28,
  "Reviewer": 3,
  "Person": 133,
  "Sentence": 17
}

获取其中一个值就像使用点表示法获取键值一样简单。

CALL apoc.meta.stats() YIELD labels
RETURN labels.Person as personCount

如果需要多个值,我们可以使用映射投影来获取仅包含我们所需计数的映射

CALL apoc.meta.stats() YIELD labels
RETURN labels {.Person, .Movie, .Director} as counts