【问题标题】:Serializing recursive refs in Datomic在 Datomic 中序列化递归引用
【发布时间】:2014-06-30 10:39:41
【问题描述】:

我的 Datomic 数据库中有一个用户实体类型,它可以跟随其他用户类型。当一个用户关注另一个已经关注他们的用户时,我的问题就出现了:

User A follows user B and also User B follows user A

当我尝试序列化(使用 Cheshire)时,由于(我猜):user/follows-users 属性上的无限递归,我得到了 StackOverflowError。

我将如何序列化(对于 API 的 json)两个以这种方式相互引用的 Datomic 实体?

这是一个基本架构:

; schema
[{:db/id #db/id[:db.part/db]
:db/ident :user/username
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity
:db.install/_attribute :db.part/db}

{:db/id #db/id[:db.part/db]
:db/ident :user/follows-users
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many
:db.install/_attribute :db.part/db}

; create users  
{:db/id #db/id[:db.part/user -100000]
 :user/username "Cheech"} 
{:db/id #db/id[:db.part/user -200000]
 :user/username "Chong"}

; create follow relationships
{:db/id #db/id[:db.part/user -100000]
 :user/follows-users  #db/id[:db.part/user -200000]}
{:db/id #db/id[:db.part/user -200000]
 :user/follows-users  #db/id[:db.part/user -100000]}]

一旦在 repl 上设置了数据库等:

user=> (use '[cheshire.core :refer :all])
nil

user=> (generate-string (d/touch (d/entity (d/db conn) [:user/username "Cheech"]))) 
StackOverflowError   clojure.lang.RestFn.invoke (RestFn.java:433)

【问题讨论】:

    标签: json recursion clojure datomic


    【解决方案1】:

    链接数据结构的急切扩展只有在无循环的情况下才能在任何语言中安全。承诺“只在找到一个循环之前急切扩展数据,然后切换到链接(通过用户 ID)”的 api 可能比从未扩展并始终返回足够用户以关注响应中的所有链接的 API 更难可靠地使用.例如上面的请求可以返回 JSON:

    [{"id": -100000,
      "username": "Cheech",
      "follows-users": [-200000]}
     {"id": -200000,
      "username": "Chong",
      "follows-users": [-100000]}] 
    

    通过将用户图的步行减少到一个集合来找到选定用户的列表。

    【讨论】:

      【解决方案2】:

      我对 Datomic 有点不了解,并且确信必须有一种更惯用的方式来执行上面@arthur-ulfeldt 建议的操作,但以防万一其他人正在寻找有关如何序列化 Datomic 的快速指针EntityMaps 到存在自引用引用的 json 中,这是解决我的问题的代码:

      (defn should-pack?
        "Returns true if the attribute is type 
        ref with a cardinality of many"
        [attr]
        (->>
          (d/q '[:find ?attr
                 :in $ ?attr
                 :where
                 [?attr :db/valueType ?type]
                 [?type :db/ident :db.type/ref]
                 [?attr :db/cardinality ?card]
                 [?card :db/ident :db.cardinality/many]]
               (d/db CONN) attr)
          first
          empty?
          not))
      
      (defn make-serializable 
        "Stop infinite loops on recursive refs" 
        [entity]
        (def ent (into {} entity))
        (doseq [attr ent]
          (if (should-pack? (first attr))
            (def ent (assoc ent 
                            (first attr) 
                            (map #(get-entity-id %) (first (rest attr)))))))
        ent)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-22
        • 1970-01-01
        • 2016-02-26
        • 1970-01-01
        • 2021-08-28
        • 1970-01-01
        • 2021-06-24
        • 2010-12-01
        相关资源
        最近更新 更多