-
Notifications
You must be signed in to change notification settings - Fork 29
/
multi_graph.cljc
106 lines (98 loc) · 5.37 KB
/
multi_graph.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
(ns ^{:doc "A multi-graph implementation.
Resolution counting ignores multiple edges connecting nodes, so as to
allow rules to successfully use this graph type."
:author "Paula Gearon"}
asami.multi-graph
(:require [asami.graph :refer [Graph graph-add graph-delete graph-diff resolve-triple count-triple]]
[asami.index :refer [? simplify count-from-index]]
#?(:clj [schema.core :as s]
:cljs [schema.core :as s :include-macros true])))
(def ^:dynamic *insert-op* #(if (zero? %) 1 %))
(s/defn multi-add :- {s/Any {s/Any {s/Any s/Num}}}
"Add elements to a 4-level index"
[idx :- {s/Any {s/Any {s/Any s/Num}}}
a :- s/Any
b :- s/Any
c :- s/Any]
(update-in idx [a b c] (fn [v] (*insert-op* (or v 0)))))
(s/defn multi-delete :- {s/Any {s/Any {s/Any s/Num}}}
"Remove elements from a 3-level index. Returns the new index, or nil if there is no change."
[idx :- {s/Any {s/Any {s/Any s/Num}}}
a :- s/Any
b :- s/Any
c :- s/Any]
(if-let [idx2 (idx a)]
(if-let [idx3 (idx2 b)]
(if-let [c4 (idx3 c)]
(if (= 1 c4)
(let [new-idx3 (dissoc idx3 c)
new-idx2 (if (seq new-idx3) (assoc idx2 b new-idx3) (dissoc idx2 b))
new-idx (if (seq new-idx2) (assoc idx a new-idx2) (dissoc idx a))]
new-idx)
(update-in idx [a b c] dec))))))
(defmulti get-from-multi-index
"Lookup an index in the graph for the requested data.
Returns a sequence of unlabelled bindings. Each binding is a vector of binding values."
simplify)
;; Extracts the required index (idx), and looks up the requested fields.
;; If an embedded index is pulled out, then this is referred to as edx.
(defmethod get-from-multi-index [:v :v :v] [{idx :spo} s p o] (let [n (get-in idx [s p o])]
(if (and (number? n) (> n 0))
(repeat n [])
[])))
(defmethod get-from-multi-index [:v :v ?] [{idx :spo} s p o] (for [[o c] (get-in idx [s p])
_ (range c)]
[o]))
(defmethod get-from-multi-index [:v ? :v] [{idx :osp} s p o] (for [[p c] (get-in idx [o s])
_ (range c)]
[p]))
(defmethod get-from-multi-index [:v ? ?] [{idx :spo} s p o] (let [edx (idx s)]
(for [p (keys edx)
[o c] (edx p)
_ (range c)]
[p o])))
(defmethod get-from-multi-index [ ? :v :v] [{idx :pos} s p o] (for [[s c] (get-in idx [p o])
_ (range c)]
[s]))
(defmethod get-from-multi-index [ ? :v ?] [{idx :pos} s p o] (let [edx (idx p)]
(for [o (keys edx)
[s c] (edx o)
_ (range c)]
[s o])))
(defmethod get-from-multi-index [ ? ? :v] [{idx :osp} s p o] (let [edx (idx o)]
(for [s (keys edx)
[p c] (edx s)
_ (range c)]
[s p])))
(defmethod get-from-multi-index [ ? ? ?] [{idx :spo} s p o] (for [s (keys idx)
p (keys (idx s))
[o c] ((idx s) p)
_ (range c)]
[s p o]))
(defrecord MultiGraph [spo pos osp]
Graph
(graph-add [this subj pred obj]
(assoc this
:spo (multi-add spo subj pred obj)
:pos (multi-add pos pred obj subj)
:osp (multi-add osp obj subj pred)))
(graph-delete [this subj pred obj]
(if-let [idx (multi-delete spo subj pred obj)]
(assoc this :spo idx :pos (multi-delete pos pred obj subj) :osp (multi-delete osp obj subj pred))
this))
(graph-diff [this other]
(let [s-po (remove (fn [[s po]] (= po (get (:spo other) s)))
spo)]
(map first s-po)))
(resolve-triple [this subj pred obj]
(get-from-multi-index this subj pred obj))
(count-triple [this subj pred obj] ;; This intentionally ignores multi-edges, and is used for Naga
(count-from-index this subj pred obj)))
(defn multi-graph-add
([graph subj pred obj n]
(binding [*insert-op* (partial + n)]
(graph-add graph subj pred obj)))
([graph subj pred obj]
(binding [*insert-op* inc]
(graph-add graph subj pred obj))))
(def empty-multi-graph (->MultiGraph {} {} {}))