如何在Datomic中为已有实体自动填充复合属性元组?
Datomic 元组属性自动填充与已有实体更新问题
问题描述
现有Datomic数据库,实体包含:user/first和:user/last属性。添加基于这两个属性的元组属性:user/first+last后,出现以下问题:
- 已存在的实体未自动填充该元组属性
- 由于旧实体没有元组属性,Datomic允许创建重复的
:user/first+:user/last组合的新实体
需要明确:
- 是否可让Datomic自动填充已有实体的元组属性?
- 若必须手动操作,有无简便的查询/事务方案?
复现代码
(ns test-tuple (:require [processdb.ion.db :as db] [datomic.client.api :as d])) (def schema [{:db/ident :user/first :db/valueType :db.type/string :db/cardinality :db.cardinality/one} {:db/ident :user/last :db/valueType :db.type/string :db/cardinality :db.cardinality/one}]) (def user-data [{:user/first "Joe" :user/last "Smith"} {:user/first "Jane" :user/last "Doe"}]) (def tuple-schema [{:db/ident :user/first+last :db/valueType :db.type/tuple :db/tupleAttrs [:user/first :user/last] :db/cardinality :db.cardinality/one}]) (defn load-schema [] (d/transact (db/get-connection) {:tx-data schema})) (defn load-tuple-schema [] (d/transact (db/get-connection) {:tx-data tuple-schema})) (defn load-data [] (d/transact (db/get-connection) {:tx-data user-data})) (defn query-names [] (d/q '[:find (pull ?e [:user/first :user/last :user/first+last]) :in $ :where [?e :user/first]] (db/get-db)))
REPL执行步骤
> (require '[test-tuple :as tt] :reload) > (tt/load-schema) > (tt/load-data) > (tt/load-tuple-schema) > (tt/load-data)
执行结果
查询返回重复实体,部分含元组属性,部分不含:
> (tt/query-names) [[#:user{:first "Joe", :last "Smith"}] [#:user{:first "Jane", :last "Doe"}] [#:user{:first "Joe", :last "Smith", :first+last ["Joe" "Smith"]}] [#:user{:first "Jane", :last "Doe", :first+last ["Jane" "Doe"]}]]
注:新创建的实体元组会自动填充,符合预期。
解答
1. 自动填充已有实体的元组属性?
不行。Datomic不会自动回溯填充新增的派生元组属性到已有实体。元组属性的自动计算仅适用于新增实体,或更新了元组依赖属性的现有实体。对于添加元组属性前就存在的实体,必须手动更新。
2. 手动更新已有实体的简便方案
通过Datalog查询找出所有缺少元组属性的实体,生成事务数据批量填充:
步骤1:查询缺少元组的实体
(defn find-users-missing-tuple [] (d/q '[:find ?e ?first ?last :in $ :where [?e :user/first ?first] [?e :user/last ?last] (not [?e :user/first+last])] (db/get-db)))
步骤2:生成事务并执行更新
(defn fill-missing-tuples [] (let [missing-users (find-users-missing-tuple) tx-data (map (fn [[e first last]] {:db/id e :user/first+last [first last]}) missing-users)] (d/transact (db/get-connection) {:tx-data tx-data})))
执行(fill-missing-tuples)后,所有旧实体都会被填充元组属性。
3. 补充:添加唯一约束
当前的tuple-schema缺少唯一约束,需添加:db/unique来阻止重复实体:
(def tuple-schema [{:db/ident :user/first+last :db/valueType :db.type/tuple :db/tupleAttrs [:user/first :user/last] :db/cardinality :db.cardinality/one :db/unique :db.unique/identity}])
- 使用
:db.unique/identity:后续可通过元组属性查找实体 - 使用
:db.unique/value:仅阻止重复值,无法通过元组直接定位实体
验证
执行填充事务后,再次查询(tt/query-names)会看到所有实体都包含:user/first+last属性,此时添加重复用户数据会触发唯一约束异常,符合预期。
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

