无题



入门

核心概念

  • 节点 (Node): 代表实体,就像关系型数据库中的一行数据。例如:一个人、一部电影、一个公司。节点可以有标签 (Label) 来分类,比如 :Person, :Movie
  • 关系 (Relationship): 代表节点之间的连接,这是图数据库的精髓。例如:[:ACTED_IN] (出演), [:DIRECTED] (导演)。关系必须有方向类型
  • 属性 (Property): 节点和关系都可以有属性,用来存储键值对数据。例如:一个 :Person 节点可以有 name: 'Keanu Reeves' 属性。

语句:

  • MERGE: 一个非常强大的命令,可以理解为 “匹配或创建”。如果节点/关系已存在,就匹配它;如果不存在,就创建它。常用来避免创建重复数据。
  • WHERE: 过滤条件,用法和 SQL 非常相似。
  • SET: 修改节点或关系的属性。
  • DELETE / DETACH DELETE: 删除节点和关系。DETACH DELETE 会在删除节点的同时,删除与它相连的所有关系。

  • 可变长度路径: 查询两个节点之间任意长度的路径,例如 (a)-[*2..5]->(b) 表示查找 a 和 b 之间长度为 2 到 5 的路径。

  • 聚合 (Aggregation): 类似于 SQL 的 GROUP BY,例如 count(), sum(), avg(), collect()
  • WITH: 管道命令。将一个查询的结果,作为下一个查询的输入,构建复杂的查询逻辑。

技巧

高级 Cypher 查询技巧

  1. 列表推导 (List Comprehension): 类似于 Python,可以快速生成列表。

找到一部电影的所有演员,并返回他们的出生年份列表。

1
2
MATCH (p:Person)-[:ACTED_IN]->(m:Movie {title: 'The Matrix'})
RETURN m.title, [person IN collect(p) | person.born] AS birthYears
  1. 字符串函数: split(), substring(), toLower(), replace() 等。

设人名存的是 “Reeves, Keanu”,我们想把它规范化。

1
2
3
4
5
6
MATCH (p:Person)
WHERE p.name CONTAINS ','
WITH p, split(p.name, ', ') AS names
SET p.firstName = names[1], p.lastName = names[0]
RETURN p.firstName, p.lastName

  1. UNWIND,它可以将一个列表展开成多行数据,方便后续处理。
1
2
3
4
5
WITH ['Laurence Fishburne', 'Carrie-Anne Moss', 'Hugo Weaving'] AS actorNames
UNWIND actorNames AS name
MATCH (m:Movie {title: 'The Matrix'})
MERGE (p:Person {name: name})
MERGE (p)-[:ACTED_IN]->(m)
  • 这个查询会把3个演员名字的列表展开成3行,然后对每一行执行 MERGE 操作,非常高效。
  1. OPTIONAL MATCH: 类似 SQL 的 LEFT JOIN

MATCH 必须匹配到完整的模式,否则返回空。而 OPTIONAL MATCH 即使模式的某个部分不存在,也依然会返回结果,缺失的部分用 null 表示。

1
2
3
MATCH (m:Movie)
OPTIONAL MATCH (d:Person)-[:DIRECTED]->(m)
RETURN m.title, d.name

5.Case 条件逻辑

根据电影的上映年份,给电影打上“经典”或“现代”的标签

1
2
3
4
5
6
7
8
9
MATCH (m:Movie)
RETURN
m.title,
m.released,
CASE
WHEN m.released < 2000 THEN 'Classic'
ELSE 'Modern'
END AS era

图建模的构建

核心原则:

  1. 将实体建模为节点,将关系建模为关系: 这是最基本的原则。

  2. 细化标签: 不要只有一个 :User 标签,可以有 :User, :Customer, :Admin 等,方便查询。

  3. 关系的方向和类型至关重要: (a)-[:FRIEND_OF]->(b)(b)-[:FRIEND_OF]->(a) 可能意义不同(比如 Twitter 的关注)。

  4. 中间节点 (Intermediate Nodes)

    : 当一个关系本身需要有很多属性,或者需要被其他节点连接时,可以把它变成一个节点。

    • 坏模型: (Person)-[:PURCHASED {price: 100, date: '...', quantity: 2}]->(Product)。如果购买这个行为还需要关联一个 :Store 节点,就很难办。
    • 好模型: (Person)-[:MADE_PURCHASE]->(purchase:Order)-[:INCLUDES_PRODUCT]->(Product)。现在,:Order 节点可以拥有所有购买相关的属性,并且可以轻松地与 :Store 节点建立关系。

示例:社交网络

  • 需求: 用户可以发帖子,可以评论帖子,可以给帖子点赞,用户之间可以互相关注。
  • 模型设计:
    • 节点: (:User {userId, name}), (:Post {postId, content, timestamp})
    • 关系:
      • (u:User)-[:POSTED]->(p:Post)
      • (u:User)-[:COMMENTED_ON {text, timestamp}]->(p:Post)
      • (u:User)-[:LIKED]->(p:Post)
      • (u1:User)-[:FOLLOWS]->(u2:User)

比如:

  • 查找我关注的人最新发布的帖子:
1
2
3
4
MATCH (me:User {name: 'Alice'})-[:FOLLOWS]->(friend:User)-[:POSTED]->(post:Post)
RETURN friend.name, post.content, post.timestamp
ORDER BY post.timestamp DESC
LIMIT 10

索引

Neo4j 支持多种索引,最常用的是针对节点标签和属性的 RANGE 索引。

1
CREATE RANGE INDEX person_name_index FOR (p:Person) ON (p.name)
  • 当你执行 MATCH (p:Person {name: 'Tom Hanks'}) 这样的查询时,Neo4j 会利用这个索引快速定位到节点,而不是扫描所有的 :Person 节点。这会将查询速度从 O(N) 提升到 O(logN)。
  • 何时创建: 在你经常用于 MATCHWHERE 子句中进行查找的属性上创建索引。

EXPLAINPROFILE

EXPLAIN: 估算查询的执行计划,但不实际执行查询。

PROFILE: 实际执行查询,并返回详细的执行报告,包括每个操作实际命中了多少数据(DB Hits)、耗时多久等。这是性能调优最常用的工具。

查看 PROFILE 的输出,找到 DB Hits 最高的步骤。通常这就是性能瓶颈所在。如果某个 NodeByLabelScan 操作的 DB Hits 很高,就说明你可能需要为那个标签和属性添加一个索引

六度分隔理论

如果把全世界的每一个人看作一个节点 (Node),人与人之间的认识关系看作一条边 (Edge/Relationship),那么这个理论断言:在这个巨大的人际关系图中,任意两个节点之间的最短路径 (Shortest Path) 的长度平均约为 6。