跳到主要内容

数据建模

图数据建模是将业务领域转换为图结构的过程。良好的数据建模能够提高查询性能、简化查询逻辑,并更好地表达业务语义。

建模核心原则

1. 识别实体和关系

在开始建模前,先分析业务场景中的核心概念:

  • 实体(节点):人、地点、物品、事件等名词
  • 关系:实体之间的连接,通常是动词或动词短语
  • 属性:描述实体或关系的特征
-- 示例:社交网络建模
-- 实体:Person(人)
-- 关系:FRIEND(朋友)、FOLLOWS(关注)
-- 属性:name, age, since 等

CREATE (zhangsan:Person {name: '张三', age: 30})
CREATE (lisi:Person {name: '李四', age: 28})
CREATE (zhangsan)-[:FRIEND {since: '2020-01-01'}]->(lisi)

2. 粒度选择

决定数据应该建模为节点还是属性:

-- 作为属性:当地址不需要单独查询时
CREATE (p:Person {name: '张三', address: '北京市海淀区'})

-- 作为节点:当需要查询住在同一地区的人时
CREATE (p:Person {name: '张三'})-[:LIVES_IN]->(d:District {name: '海淀区'})

选择原则

  • 如果数据需要被独立查询 → 建模为节点
  • 如果数据只是描述信息 → 建模为属性
  • 如果数据需要建立关系 → 建模为节点

3. 关系方向

虽然 Neo4j 的关系是有方向的,但在查询时可以忽略方向:

-- 创建双向关系(如果需要)
CREATE (a)-[:FRIEND]->(b)
CREATE (b)-[:FRIEND]->(a)

-- 查询时忽略方向
MATCH (a:Person)-[:FRIEND]-(b:Person)
RETURN a.name, b.name

常见建模模式

社交网络模型

-- 用户节点
CREATE (user1:User {userId: 'u001', name: '张三', createdAt: datetime()})
CREATE (user2:User {userId: 'u002', name: '李四', createdAt: datetime()})

-- 关注关系
CREATE (user1)-[:FOLLOWS {createdAt: datetime()}]->(user2)

-- 好友关系(双向)
CREATE (user1)-[:FRIEND {since: date('2020-01-01')}]->(user2)
CREATE (user2)-[:FRIEND {since: date('2020-01-01')}]->(user1)

-- 发布内容
CREATE (post1:Post {postId: 'p001', content: '今天天气不错', createdAt: datetime()})
CREATE (user1)-[:PUBLISHED]->(post1)

-- 点赞
CREATE (user2)-[:LIKED {createdAt: datetime()}]->(post1)

-- 评论
CREATE (comment1:Comment {commentId: 'c001', content: '确实很好!'})
CREATE (user2)-[:COMMENTED]->(comment1)
CREATE (comment1)-[:ON]->(post1)

电商系统模型

-- 用户
CREATE (user:User {userId: 'u001', name: '张三', email: '[email protected]'})

-- 商品分类
CREATE (electronics:Category {name: '电子产品'})
CREATE (phones:Category {name: '手机'})
CREATE (phones)-[:SUBCATEGORY_OF]->(electronics)

-- 商品
CREATE (product:Product {sku: 'P001', name: 'iPhone 15', price: 5999})
CREATE (product)-[:BELONGS_TO]->(phones)

-- 订单
CREATE (order:Order {orderId: 'O001', totalAmount: 5999, status: 'PAID'})
CREATE (user)-[:PLACED {createdAt: datetime()}]->(order)
CREATE (order)-[:CONTAINS {quantity: 1, price: 5999}]->(product)

-- 评价
CREATE (review:Review {rating: 5, comment: '非常好用!'})
CREATE (user)-[:REVIEWED]->(review)
CREATE (review)-[:FOR]->(product)

-- 购物车
CREATE (cart:Cart {cartId: 'C001'})
CREATE (user)-[:HAS_CART]->(cart)
CREATE (cart)-[:CONTAINS {quantity: 2, addedAt: datetime()}]->(product)

知识图谱模型

-- 概念/实体
CREATE (china:Country {name: '中国'})
CREATE (beijing:City {name: '北京'})
CREATE (tsinghua:University {name: '清华大学'})

-- 关系
CREATE (beijing)-[:CAPITAL_OF]->(china)
CREATE (beijing)-[:LOCATED_IN]->(china)
CREATE (tsinghua)-[:LOCATED_IN]->(beijing)

-- 人物
CREATE (person:Person {name: '张三', profession: '教授'})
CREATE (person)-[:WORKS_AT]->(tsinghua)
CREATE (person)-[:BORN_IN]->(beijing)

-- 时间事件
CREATE (event:Event {name: '建校', date: date('1911-04-29')})
CREATE (tsinghua)-[:FOUNDED_ON]->(event)

高级建模技巧

1. 时间维度建模

-- 方法1:关系属性
CREATE (user)-[:WORKS_AT {from: date('2020-01-01'), to: date('2023-12-31')}]->(company)

-- 方法2:时间树(适合复杂时间查询)
CREATE (y2023:Year {value: 2023})
CREATE (m01:Month {value: 1})-[:OF_YEAR]->(y2023)
CREATE (d15:Day {value: 15})-[:OF_MONTH]->(m01)

-- 关联事件到时间点
CREATE (event:Event {name: '入职'})
CREATE (event)-[:OCCURRED_ON]->(d15)
CREATE (user)-[:PARTICIPATED_IN]->(event)

2. 版本控制建模

-- 文档版本
CREATE (doc:Document {docId: 'd001', title: '项目计划'})
CREATE (v1:Version {version: '1.0', content: '初始版本', createdAt: datetime()})
CREATE (v2:Version {version: '2.0', content: '修订版本', createdAt: datetime()})

CREATE (v2)-[:PREVIOUS_VERSION]->(v1)
CREATE (doc)-[:CURRENT_VERSION]->(v2)
CREATE (doc)-[:HAS_VERSION]->(v1)
CREATE (doc)-[:HAS_VERSION]->(v2)

3. 权限模型

-- RBAC 模型
CREATE (user:User {name: '张三'})
CREATE (role:Role {name: '管理员'})
CREATE (resource:Resource {name: '用户管理', type: '模块'})
CREATE (permission:Permission {action: 'WRITE'})

CREATE (user)-[:HAS_ROLE]->(role)
CREATE (role)-[:HAS_PERMISSION]->(permission)
CREATE (permission)-[:ON]->(resource)

4. 多语言支持

-- 产品多语言
CREATE (product:Product {sku: 'P001'})
CREATE (name_zh:ProductName {language: 'zh', value: '苹果手机'})
CREATE (name_en:ProductName {language: 'en', value: 'Apple iPhone'})

CREATE (product)-[:HAS_NAME]->(name_zh)
CREATE (product)-[:HAS_NAME]->(name_en)

-- 查询中文名称
MATCH (p:Product)-[:HAS_NAME]->(n:ProductName {language: 'zh'})
RETURN p.sku, n.value

反模式与避免

1. 过度使用属性

-- 不好的设计:所有信息都在属性中
CREATE (p:Person {
name: '张三',
friend1: '李四',
friend2: '王五',
friend3: '赵六'
})

-- 好的设计:关系表示连接
CREATE (zhangsan:Person {name: '张三'})
CREATE (lisi:Person {name: '李四'})
CREATE (wangwu:Person {name: '王五'})
CREATE (zhaoliu:Person {name: '赵六'})
CREATE (zhangsan)-[:FRIEND]->(lisi)
CREATE (zhangsan)-[:FRIEND]->(wangwu)
CREATE (zhangsan)-[:FRIEND]->(zhaoliu)

2. 过度规范化

-- 不好的设计:过度拆分
CREATE (user:User {id: 'u001'})
CREATE (name:Name {first: '张', last: '三'})
CREATE (age:Age {value: 30})
CREATE (user)-[:HAS_NAME]->(name)
CREATE (user)-[:HAS_AGE]->(age)

-- 好的设计:合理平衡
CREATE (user:User {id: 'u001', firstName: '张', lastName: '三', age: 30})

3. 忽略关系属性

-- 不好的设计:关系没有属性
CREATE (a)-[:RATED]->(b)

-- 好的设计:评分和时间
CREATE (a)-[:RATED {score: 5, comment: '非常好', at: datetime()}]->(b)

性能考虑

1. 索引策略

-- 为查询入口点创建索引
CREATE CONSTRAINT user_id_unique FOR (u:User) REQUIRE u.userId IS UNIQUE
CREATE INDEX user_name FOR (u:User) ON (u.name)
CREATE INDEX product_category FOR (p:Product) ON (p.category)

2. 查询模式优化

-- 高效:从有索引的节点开始
MATCH (u:User {userId: 'u001'})-[:FOLLOWS]->(followed)
RETURN followed

-- 低效:从关系开始
MATCH ()-[:FOLLOWS]->(followed)
WHERE followed.userId = 'u001'
RETURN followed

3. 超级节点处理

-- 超级节点:拥有大量关系的节点
-- 解决方案1:关系分片
CREATE (user)-[:FOLLOWS_2024]->(followed)

-- 解决方案2:双向关系
CREATE (user)-[:FOLLOWS]->(followed)
CREATE (followed)-[:FOLLOWED_BY]->(user)

-- 解决方案3:使用中间节点
CREATE (user)-[:HAS_FOLLOW_LIST]->(list:FollowList {year: 2024})
CREATE (list)-[:CONTAINS]->(followed)

实际案例

电影推荐系统

-- 创建电影数据
CREATE (inception:Movie {title: '盗梦空间', year: 2010, genre: '科幻'})
CREATE (nolan:Director {name: '克里斯托弗·诺兰'})
CREATE (dicaprio:Actor {name: '莱昂纳多·迪卡普里奥'})

CREATE (nolan)-[:DIRECTED]->(inception)
CREATE (dicaprio)-[:ACTED_IN {role: 'Cobb'}]->(inception)

-- 用户评分
CREATE (user:User {userId: 'u001'})
CREATE (user)-[:RATED {score: 5, at: datetime()}]->(inception)

-- 推荐查询:找到看过相同电影的用户推荐的其他电影
MATCH (user:User {userId: 'u001'})-[:RATED]->(movie)<-[:RATED]-(similar:User)
MATCH (similar)-[:RATED]->(recommendation:Movie)
WHERE NOT (user)-[:RATED]->(recommendation)
RETURN recommendation.title, avg(r.score) AS avgScore
ORDER BY avgScore DESC
LIMIT 10

建模检查清单

  • 识别了所有核心实体和关系
  • 选择了合适的粒度级别
  • 为常用查询入口创建了索引
  • 考虑了数据增长和超级节点问题
  • 验证了关键查询的性能
  • 处理了时间维度和版本控制需求
  • 考虑了多语言和国际化

下一步

掌握了数据建模后,继续学习 应用开发,了解如何在实际应用中使用 Neo4j。