2

Datomic データベースに、他のユーザー タイプに従うことができるユーザー エンティティ タイプがあります。私の問題は、あるユーザーがすでにフォローしている別のユーザーをフォローしているときに発生します。

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

(Cheshire を使用して) シリアル化しようとすると、属性の無限再帰が原因で (私が推測している) StackOverflowError が発生し:user/follows-usersます。

このような方法で相互に参照する 2 つの Datomic エンティティを (API の json に) シリアル化するにはどうすればよいですか?

基本的なスキーマは次のとおりです。

; 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 に少し無頓着であり、@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)
于 2014-05-15T14:36:45.813 に答える