Другой способ (или вы называете это «косвенным» способом) выполнения этого типа запроса - через Datalog , где вы сначала превращаете вложенную карту в факты:
(def friendship (mapcat (fn [[p {xs :friends}]]
(for [f xs]
[p :person/friend f]))
{:james {:friends [:lucy :john :daisy]},
:lucy {:friends [:james :daisy]},
:daisy {:friends [:james :lucy]},
:john {:friends [:james]}}))
;; =>
([:james :person/friend :lucy]
[:james :person/friend :john]
[:james :person/friend :daisy]
[:lucy :person/friend :james]
[:lucy :person/friend :daisy]
[:daisy :person/friend :james]
[:daisy :person/friend :lucy]
[:john :person/friend :james])
затем выполните запрос данных по фактам с помощью пользовательских правил, таких как friend
и friend-of-friend
. Например. найти друга друга :james
:
(d/q '[:find [?f ...]
:where (friend-of-friend ?p ?f)
:in $ ?p %]
friendship
:james
'[[(friend ?p ?f)
[?p :person/friend ?f]]
[(friend-of-friend ?p ?f)
(friend ?p ?x)
(friend ?x ?f)
[(not= ?p ?f)]]])
;; => [:daisy :lucy]
, где
[:find [?f ...]
:where (friend-of-friend ?p ?f)
:in $ ?p %]
является запросом, friendship
является фактом и сопоставляется с $
, :james
является предметом запроса (сопоставляется с аргументом ?p
), а %
является правилами, определенными как:
[[(friend ?p ?f) ; 1. What is a friend?
[?p :person/friend ?f]] ; ?p has an attribute :person/friend defined with ?f
[(friend-of-friend ?p ?f) ; 2. Friend of friend
(friend ?p ?x) ; ?p and ?x are friends (base on #1)
(friend ?x ?f) ; ?x and ?f are friends
[(not= ?p ?f)]]] ; ?p and ?f not same person
Примечание: приведенный выше пример использует datascript