2.1 为漫游建模:属性图设计的三个步骤


文档摘要

2.1 为漫游建模:属性图设计 本节摘要:查询的质量上限由建模决定。本节给出一个可复用的建模三步法——列实体、定关系、挑属性——并用电商场景从零建出"用户-订单-商品"图,途中处理"关系挂属性""一对多用关系还是用属性"两个高频决策点。读完后你能把一段业务描述系统地翻译成属性图。 第 1 章认识了四件积木,本节解决"怎么搭":同样的业务,建模方案不同,查询难度可能天差地别。漫游之前,先把地图画好。 一、三步法:实体、关系、属性 第一步,列实体。 把需求文档里的名词圈出来,问两个问题:它有没有独立的生命周期?会不会被多个其他实体关联?两个都答"是",它就该是节点。用户、商品、订单都通过这道检验;而"收货地址"若只属于单个用户、不被别的实体引用,先做成属性,等它需要被多处关联时再升级为节点。

2.1 为漫游建模:属性图设计

本节摘要:查询的质量上限由建模决定。本节给出一个可复用的建模三步法——列实体、定关系、挑属性——并用电商场景从零建出"用户-订单-商品"图,途中处理"关系挂属性""一对多用关系还是用属性"两个高频决策点。读完后你能把一段业务描述系统地翻译成属性图。

第 1 章认识了四件积木,本节解决"怎么搭":同样的业务,建模方案不同,查询难度可能天差地别。漫游之前,先把地图画好。

一、三步法:实体、关系、属性

第一步,列实体。 把需求文档里的名词圈出来,问两个问题:它有没有独立的生命周期?会不会被多个其他实体关联?两个都答"是",它就该是节点。用户、商品、订单都通过这道检验;而"收货地址"若只属于单个用户、不被别的实体引用,先做成属性,等它需要被多处关联时再升级为节点。

第二步,定关系。 圈动词和从属:下单、评价、收藏、属于。每个关系问一句"未来要不要沿它遍历"——要,就建关系;不要,可以考虑属性。这一问在 1.1 的判据里出现过,这里是它的战术应用。

第三步,挑属性。 每个节点保留"查得着"的属性(会被 WHERE 用到的),以及"看得懂"的属性(展示需要的)。既不查询也不展示的属性先不进图。

二、实战:从需求到图

需求原文:"买家下单购买商品,每笔订单包含多个商品与数量;买家可以对商品发表评价;平台需要按类目统计销量。"

第一步圈出实体:买家、商品、订单、类目。第二步定关系:买家下单订单、订单包含商品、买家评价商品、商品属于类目。第三步挂属性。得到的建模草图:

图:电商订单域的属性图设计

图:电商订单域的属性图设计

把草图写成 Cypher:

// 建模落库:节点先行,关系随后 CREATE (b:Buyer {name: '林晓', level: 'gold'}) CREATE (o:Order {orderNo: 'SO-2024-0912', total: 359.00}) CREATE (p:Product {sku: 'KB-87', title: '机械键盘 87键', price: 299.00}) CREATE (c:Category {name: '外设'}) CREATE (b)-[:PLACED {at: datetime('2024-09-12T10:03:00'), amount: 359.00}]->(o) CREATE (o)-[:CONTAINS {qty: 1, priceSnapshot: 299.00}]->(p) CREATE (p)-[:IN_CATEGORY]->(c)

注意两个设计决策:数量与成交价快照挂在 CONTAINS 关系上而不是商品节点——同一商品在不同订单里数量与当时的价格不同,这是典型的"关系级事实";买家与商品之间不建直接购买关系——购买事实经由订单表达,否则每笔多商品订单要复制多条直连边,统计口径也会乱。

三、建模验证:用未来的查询倒推

图建好不是终点,用三个预期问题验证它好不好走:

// 问题1:买家最近下过哪些单(PLACED 上有 at,可直接按时间筛) MATCH (b:Buyer {name: '林晓'})-[pl:PLACED]->(o:Order) WHERE pl.at >= datetime('2024-09-01') RETURN o.orderNo, o.total
o.orderNo | o.total ----------------|-------- "SO-2024-0912" | 359.0
// 问题2:某类目下被包含在多少笔订单里(沿 CONTAINS 与 IN_CATEGORY 两程) MATCH (c:Category {name: '外设'})<-[:IN_CATEGORY]-(p:Product) <-[ct:CONTAINS]-(o:Order) RETURN count(DISTINCT o) AS 订单数, sum(ct.qty) AS 件数
订单数 | 件数 -------|----- 2 | 3
// 问题3:买家评价过的商品属于哪些类目(验证 REVIEWED 边的价值) MATCH (b:Buyer {name: '林晓'})-[:REVIEWED]->(p:Product)-[:IN_CATEGORY]->(c:Category) RETURN DISTINCT c.name

三个查询都不需要复杂的中间构造,说明建模的形状与业务问题对齐了。若某个预期问题写出来要绕好几道弯,通常不是查询的错,是边建少了或建错了方向。

⚠️ 逆向可读的边值得预埋:Cypher 支持无方向匹配,但若某条边天然只该单向建(如 IN_CATEGORY),预先约定方向能省掉后续大量歧义。

四、常见建模误区清单

评审别人(或半年后的自己)的建模时,这几类问题出现频率最高:

误区 症状 纠正
万物皆属性 用户画像塞进一个 JSON 字符串属性 会查询的字段拆成属性,会关联的拆成节点
万物皆节点 "点击""浏览"也建成节点 无后续关联的纯事件用加权边
方向混乱 同语义两种方向的边并存 建模规约写死方向
复制狂 同一事实写在多个节点上 事实只存一处,靠关系取回
深层滥用 变长路径无上界遍地开花 给上界,深遍历交给算法(5.3)

其中"复制狂"最隐蔽:价格快照既写在商品节点又写在关系上,两处迟早不一致。原则重申一遍——每条事实只存一处,其余用关系取

五、FAQ

问:模型错了,改起来贵吗?
图模型没有固定表结构,加标签、加关系类型是运行时操作。代价主要在数据回填(给存量节点补边),用 apoc.periodic.iterate(6.1)分批处理即可。

问:要不要先设计好全部 schema 再动手?
抓大放小:核心实体与关系的骨架先定,属性可以渐进补充。约束(唯一性等)从一开始就建——它们改起来最贵。

本节要点回顾

  • 建模三步法:列实体 → 定关系 → 挑属性,用"独立生命周期 + 多主体关联"检验节点资格;
  • 关系级事实(数量、快照价、评分时间)挂关系,不复制进节点;
  • 购买这类"经由中介的事件",让中介当节点,避免直连边爆炸;
  • 建模完成后用预期查询倒推验证,写不顺的查询是建模的报警器。

地图画好了,下一节正式起步:MATCH 的基础语法与最小闭环。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U