2014-05-12 2 views
2

У меня есть тип пользовательского объекта в базе данных Datomic, который может следовать за другими типами пользователей. Моя проблема возникает, когда один пользователь следует другому пользователю, который уже следующий их:Сериализация рекурсивных ссылок в Datomic

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

Когда я пытаюсь сериализации (используя Чешир) Я получаю StackOverflowError из-за (я предполагаю, что) зацикливание на атрибут :user/follows-users.

Как бы я стал сериализовать (для json для API) два объекта 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]}] 

И когда база данных созданы и т.д. на РЕПЛ:

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) 

ответ

1

нетерпеливого расширение связанных структур данных только безопасно на любом языке, если они свободны от цикла. Api, который обещает «с нетерпением расширить данные только до тех пор, пока не будет найден цикл, а затем переключится на привязку (по идентификатору пользователя)», может быть труднее потреблять надежно, чем тот, который никогда не расширялся и всегда возвращал достаточное количество пользователей, чтобы следить за всеми ссылками в ответе , Например запрос выше, может вернуть JSON:

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

Если список выбранных пользователей найден путем уменьшения походки пользователей графа в набор.

0

Я немного от n00b до 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) 
Смежные вопросы