我想编写一个函数来扩展从给定的Datomic Entity可以访问的所有内容。我知道如果有循环,这可能会有问题,假设实体不是循环的。
(defn touch-all
"Touches `entity and all other reachable Entities of `entity"
[entity]
(let [entity (d/touch entity)]
(doseq [[k v] entity]
(when (instance? datomic.query.EntityMap v)
(touch-all v)))
entity))
d/touch
扩展实体中的所有“键”,并将其值显示为字面值或另一个(未触及的)实体。
上面的代码确实遍历了实体的所有子项(递归)。因此,触及了entity
可到达的所有实体。当我输入(touch-all entity)
时,为什么我没有看到完全展开子形式的实体?
我的困惑很大一部分与我在repl中看到的有关:
sample.datomic> (def root (d/entity db 44))
#'sample.datomic/root
sample.datomic> root
{:db/id 17592186045421}
sample.datomic> (:answer-2-card root)
{:db/id 17592186045423}
sample.datomic> root
{:answer-2-card {:db/id 17592186045423}, :db/id 17592186045421}
sample.datomic> (-> root :answer-2-card :text)
"Card 2"
sample.datomic> (-> root :answer-2-card)
{:text "Card 2", :db/id 17592186045423}
sample.datomic> root
{:answer-2-card {:text "Card 2", :db/id 17592186045423}, :db/id 17592186045421}
所以,实际上看起来实体是可变的和粘性的。当我访问包含实体的实体时,它似乎也影响包含属性的字符串表示。如果这在repl中“递归地”手动工作,为什么当我在函数中执行它时它不起作用?我只是感到困惑,因为我似乎无法重复我在函数中的repl中手动执行的操作。看起来很奇怪。
答案 0 :(得分:1)
你的问题的答案是不变性。
entity
绑定到第一个被参数传递的触摸实体,但当递归触及其他实体时,该值不会改变。
如果您想要包含触摸实体树的单个值,您应该执行以下操作:
(defn touch-all
"Touches `entity and all other reachable Entities of `entity"
[e]
(if (instance? datomic.query.EntityMap e)
(reduce (fn [m [k v]]
(assoc m k (touch-all v)))
{}
(d/touch e))
e))
考虑以下因素:
assoc
在实体上失败。ref
且基数为many
的属性,此解决方案不完整(在这种情况下,当e
为set
时,应将其视为一组实体)