2

我的 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)
4

2 回答 2

1

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

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

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

于 2014-05-13T01:02:35.930 回答
0

我对 Datomic 有点n00b,并且确信必须有一种更惯用的方式来做@arthur-ulfeldt 上面建议的事情,但万一其他人正在寻找关于如何将 Datomic EntityMaps 序列化为 json 的快速指针在存在自引用 ref 的地方,这是解决我的问题的代码:

(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)
于 2014-05-15T14:36:45.813 回答