如何在Datomic中添加关系属性(如两人相识时间)?需转换思路吗?
在Datomic中实现类似Neo4j关系属性的方案
Datomic的模型和Neo4j的图模型核心差异在于:Datomic中没有“一等公民”的关系概念,所有可被描述的事物都是实体(Entity),关系是通过实体间的引用属性来表达的。要实现类似Neo4j的关系属性功能,你需要转换思路,将原本的“关系”建模为独立实体,具体方案如下:
核心方法:把关系建模为实体
在Neo4j中你可能会写这样的图关系:
(:Person)-[:FRIENDS_WITH {since: #inst "2020-01-01"}]->(:Person)
在Datomic中,你可以将FRIENDS_WITH这个关系本身定义为一个实体,给它添加连接两个Person的引用属性,以及你需要的关系属性(比如since)。
1. 定义Schema
首先创建对应的实体属性:
(def schema [{:db/ident :person/name :db/valueType :db.type/string :db/cardinality :db.cardinality/one} {:db/ident :friendship/person1 :db/valueType :db.type/ref :db/cardinality :db.cardinality/one} {:db/ident :friendship/person2 :db/valueType :db.type/ref :db/cardinality :db.cardinality/one} {:db/ident :friendship/since :db/valueType :db.type/instant :db/cardinality :db.cardinality/one}])
2. 插入数据
添加人物实体和代表关系的friendship实体:
(d/transact conn [{:person/name "Alice"} {:person/name "Bob"} {:friendship/person1 [:person/name "Alice"] :friendship/person2 [:person/name "Bob"] :friendship/since #inst "2020-01-01"}])
3. 查询关系及属性
用Datalog查询Alice的所有朋友及成为朋友的时间:
(d/q '[:find ?friend-name ?since :where [?alice :person/name "Alice"] [?friendship :friendship/person1 ?alice] [?friendship :friendship/person2 ?friend] [?friend :person/name ?friend-name] [?friendship :friendship/since ?since]] (d/db conn))
如果需要支持双向查询(比如找所有和Alice是朋友的人),可以扩展查询条件:
(d/q '[:find ?friend-name ?since :where [?alice :person/name "Alice"] (or [?friendship :friendship/person1 ?alice] [?friendship :friendship/person2 ?alice]) (not= ?alice ?friend) (if [?friendship :friendship/person1 ?alice] [?friendship :friendship/person2 ?friend] [?friendship :friendship/person1 ?friend]) [?friend :person/name ?friend-name] [?friendship :friendship/since ?since]] (d/db conn))
思路转换要点
- Neo4j中关系是独立的一等元素,而Datomic中所有“需要被描述的事物”都应该是实体——包括原本的关系。
- 这种建模方式完全适配Datomic的事实模型,你可以像操作普通实体一样更新、查询关系的属性,也能利用Datomic的时间回溯特性查看关系属性的历史变化。
内容的提问来源于stack exchange,提问作者Mohammer
相关产品推荐
相关产品推荐

