如何在Datomic中编写touch-all(触摸实体中所有可访问的实体)?

时间:2014-11-22 00:54:43

标签: clojure datomic

我想编写一个函数来扩展从给定的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中手动执行的操作。看起来很奇怪。

1 个答案:

答案 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的属性,此解决方案不完整(在这种情况下,当eset时,应将其视为一组实体)