Skip to content

Commit 8e710d3

Browse files
authored
Merge pull request #7 from yetanalytics/collections
[PS-455] Implement RDF List and blank node collection syntax
2 parents 97e0046 + c0de13c commit 8e710d3

File tree

11 files changed

+499
-130
lines changed

11 files changed

+499
-130
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## 0.1.3
4+
- Support RDF List and blank node collection syntactic sugar.
5+
36
## 0.1.2
47
- Update Jena version to 4.8.0 to address [CVE-2023-22665](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-22665).
58

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88

99
A companion library to [Flint](https://github.com/yetanalytics/flint) for the direct compilation of Flint data into [Apache Jena](https://jena.apache.org/)-based SPARQL queries and updates.
1010

11-
Uses Flint version [v0.2.1](https://github.com/yetanalytics/flint/releases/tag/v0.2.1).
11+
Uses Flint version [v0.3.0](https://github.com/yetanalytics/flint/releases/tag/v0.3.0).
1212

1313
## Installation
1414

1515
```clojure
16-
{com.yetanalytics/flint-jena {:mvn/version "0.1.2"
16+
{com.yetanalytics/flint-jena {:mvn/version "0.1.3"
1717
:exclusions [org.clojure/clojure]}}
1818
```
1919

@@ -72,4 +72,3 @@ Which then outputs the following to `target/bench/query.txt`:
7272
Copyright © 2022-2024 Yet Analytics, Inc.
7373

7474
Distributed under the Apache License version 2.0.
75-

deps.edn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
org.apache.jena/jena-arq {:mvn/version "4.8.0"
66
:exclusions [org.slf4j/slf4j-api]}
77
org.slf4j/slf4j-api {:mvn/version "1.7.36"}
8-
com.yetanalytics/flint {:mvn/version "0.2.1"
8+
com.yetanalytics/flint {:mvn/version "0.3.0"
99
:exclusions [org.clojure/clojure
1010
org.clojure/clojurescript]}}
1111
:aliases
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
;; SELECT with lists
2+
{:select [?x]
3+
:where [[?s ?p (1 ?x 3 4)]]}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
;; SELECT with blank node vectors
2+
{:prefixes {:$ "<http://foo.org/>"
3+
:foaf "<http://xmlns.com/foaf/0.1/>"}
4+
:select [?x ?name]
5+
:where [{[:p1 "v"] {:q1 #{"w"}}
6+
:xxxxxxxx {:q2 #{[:p2 "v"]}}
7+
[:foaf/name ?name :foaf/mbox "<mailto:bar@example.com>"] {}}]}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
;; SELECT with empty lists and blank node vectors
2+
{:select [?x ?y]
3+
:where [[() ?x []]
4+
[[] ?y ()]]}
Lines changed: 196 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
(ns com.yetanalytics.flint-jena.triple
22
(:require [com.yetanalytics.flint-jena.ast :as ast])
33
(:import [org.apache.jena.graph Node Triple]
4+
[org.apache.jena.sparql.graph NodeConst]
5+
[org.apache.jena.sparql.lang LabelToNodeMap]
46
[org.apache.jena.sparql.path Path]
57
[org.apache.jena.sparql.core
68
BasicPattern
@@ -10,72 +12,7 @@
1012
[org.apache.jena.sparql.syntax
1113
ElementPathBlock
1214
ElementTriplesBlock
13-
TripleCollector
14-
TripleCollectorMark]))
15-
16-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
17-
;; AST Methods
18-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
19-
20-
;; These multimethod dispatches (specifically `:triple/vec` and `triple/nform`)
21-
;; return ElementPathBlocks since that is the most general syntax Element that
22-
;; can be used in all clauses (WHERE, CONSTRUCT, DELETE, INSERT, etc).
23-
24-
(defmethod ast/ast-node->jena :triple/path [_ [_ path]]
25-
path)
26-
27-
(defprotocol Predicate
28-
(-create-triple
29-
[pred subj obj])
30-
(-add-triple!
31-
[pred subj obj triples]
32-
[pred subj obj triples idx]))
33-
34-
(extend-protocol Predicate
35-
Node
36-
(-create-triple
37-
[p s o]
38-
(Triple. s p o))
39-
(-add-triple!
40-
([p s o ^TripleCollector coll]
41-
(.addTriple coll ^Triple (-create-triple p s o)))
42-
([p s o ^TripleCollectorMark coll idx]
43-
(.addTriple coll idx ^Triple (-create-triple p s o))))
44-
45-
Path
46-
(-create-triple
47-
[p s o]
48-
(TriplePath. s p o))
49-
(-add-triple!
50-
([p s o ^TripleCollector coll]
51-
(.addTriplePath coll ^TriplePath (-create-triple p s o)))
52-
([p s o ^TripleCollectorMark coll idx]
53-
(.addTriplePath coll idx ^TriplePath (-create-triple p s o)))))
54-
55-
(defmethod ast/ast-node->jena :triple/vec [_ [_ [s p o]]]
56-
(let [triple-block (ElementPathBlock.)]
57-
(-add-triple! p s o triple-block)
58-
triple-block))
59-
60-
(defmethod ast/ast-node->jena :triple/nform [_ [_ nform]]
61-
(let [triple-block (ElementPathBlock.)]
62-
(dorun (map-indexed
63-
(fn [idx [s p o]] (-add-triple! p s o triple-block idx))
64-
nform))
65-
triple-block))
66-
67-
(defmethod ast/ast-node->jena :triple/spo [_ [_ spo]]
68-
(mapcat (fn [[s po-coll]]
69-
(map (fn [[p o]] [s p o]) po-coll))
70-
spo))
71-
72-
(defmethod ast/ast-node->jena :triple/po [_ [_ po]]
73-
(mapcat (fn [[p o-coll]]
74-
(map (fn [o] [p o]) o-coll))
75-
po))
76-
77-
(defmethod ast/ast-node->jena :triple/o [_ [_ o]]
78-
o)
15+
TripleCollector]))
7916

8017
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
8118
;; Triple Conversion
@@ -146,3 +83,196 @@
14683
:let [quad (Quad. graph-node triple)]]
14784
(.add pattern quad)))
14885
pattern))
86+
87+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
88+
;; Protocols
89+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
90+
91+
(defprotocol SubjectObject
92+
"Protocol for triple subjects and objects. This covers RDF Lists,
93+
blank node collections, and regular nodes."
94+
(-head-node [subject-or-object]
95+
"Return the Node that external Triples should point to..")
96+
(-nested-triples [subject-or-object]
97+
"Return any Triples that are nested in the subject or object."))
98+
99+
(extend-protocol SubjectObject
100+
Node
101+
(-head-node [node] node)
102+
(-nested-triples [_] []))
103+
104+
(defrecord RDFList [first-node triples]
105+
SubjectObject
106+
(-head-node [_] first-node)
107+
(-nested-triples [_] (triple-element->seq triples)))
108+
109+
(defrecord BlankNodeColl [subject-node triples]
110+
SubjectObject
111+
(-head-node [_] subject-node)
112+
(-nested-triples [_] (triple-element->seq triples)))
113+
114+
(defprotocol Predicate
115+
"Protocol for triple predicates. This covers paths and regular nodes."
116+
(-create-triple [predicate subject object]
117+
"Create a triple from `subject`, `predicate`, and `object`."))
118+
119+
(extend-protocol Predicate
120+
Node
121+
(-create-triple [pred subj obj]
122+
(Triple. subj pred obj))
123+
124+
Path
125+
(-create-triple [pred subj obj]
126+
(TriplePath. subj pred obj)))
127+
128+
(defprotocol ASTTriple
129+
"Protocol for triples (we can't call this \"Triple\" since that would clash
130+
with Jena's Triple class)."
131+
(-add-triple! [triple triple-acc]
132+
"Add `triple` to `triple-acc` (which should be an instance of
133+
TripleCollector)."))
134+
135+
(extend-protocol ASTTriple
136+
Triple
137+
(-add-triple! [triple ^TripleCollector triple-acc]
138+
(.addTriple triple-acc triple))
139+
140+
TriplePath
141+
(-add-triple! [triple-path ^TripleCollector triple-acc]
142+
(.addTriplePath triple-acc triple-path)))
143+
144+
(defn- create-triple [subj pred obj]
145+
(-create-triple pred subj obj))
146+
147+
(defn- add-triple! [triple-acc triple]
148+
(-add-triple! triple triple-acc))
149+
150+
(defn- add-triple-coll! [triple-acc triples]
151+
(dorun (map (fn [triple] (add-triple! triple-acc triple))
152+
triples)))
153+
154+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
155+
;; Blank Node + Triple Helpers
156+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
157+
158+
(def rdf-first NodeConst/nodeFirst)
159+
(def rdf-rest NodeConst/nodeRest)
160+
(def rdf-nil NodeConst/nodeNil)
161+
162+
(defn- new-bnode! ^Node [^LabelToNodeMap bnode-m]
163+
(.allocNode bnode-m))
164+
165+
(defn- s->triples [s]
166+
(or (not-empty (-nested-triples s))
167+
(throw (ex-info "Subject without predicates or objects is not an RDF list or blank node collecton!"
168+
{:kind ::illegal-subject
169+
:subject s}))))
170+
171+
(defn- spo->triples [s p o]
172+
(let [s-node (-head-node s)
173+
o-node (-head-node o)
174+
s-triples (-nested-triples s)
175+
o-triples (-nested-triples o)
176+
triple (create-triple s-node p o-node)]
177+
(concat s-triples [triple] o-triples)))
178+
179+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
180+
;; AST Methods
181+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
182+
183+
;; These multimethod dispatches (specifically `:triple.vec/spo` and
184+
;; `triple.nform/spo`) return ElementPathBlocks since that is the most general
185+
;; syntax Element that can be used in all clauses (WHERE, CONSTRUCT, DELETE,
186+
;; INSERT, etc).
187+
188+
(defmethod ast/ast-node->jena :triple/path [_ [_ path]]
189+
path)
190+
191+
(defn- make-rdf-list [bnode-m list]
192+
(let [triple-block (ElementPathBlock.)
193+
init-node (new-bnode! bnode-m)]
194+
(loop [curr-bnode init-node
195+
next-bnode (new-bnode! bnode-m)
196+
list list]
197+
(if-some [list-entry (first list)]
198+
(let [node (-head-node list-entry)
199+
triples (-nested-triples list-entry)
200+
first-triple (create-triple curr-bnode rdf-first node)
201+
rest-triple (if (some? (second list))
202+
(create-triple curr-bnode rdf-rest next-bnode)
203+
(create-triple curr-bnode rdf-rest rdf-nil))]
204+
(add-triple! triple-block first-triple)
205+
(add-triple-coll! triple-block triples)
206+
(add-triple! triple-block rest-triple)
207+
(recur next-bnode (new-bnode! bnode-m) (rest list)))
208+
(->RDFList init-node triple-block)))))
209+
210+
(defmethod ast/ast-node->jena :triple/list
211+
[{:keys [active-bnode-map] :as opts} [_ list]]
212+
(if (empty? list)
213+
rdf-nil
214+
(make-rdf-list (get opts @active-bnode-map) list)))
215+
216+
(defn- make-bnode-coll [bnode-m po-pairs]
217+
(let [triple-block (ElementPathBlock.)
218+
subj-node (new-bnode! bnode-m)]
219+
(dorun
220+
(for [[p o] po-pairs
221+
:let [o-node (-head-node o)
222+
o-triples (-nested-triples o)
223+
triple (create-triple subj-node p o-node)]]
224+
(do
225+
(add-triple! triple-block triple)
226+
(add-triple-coll! triple-block o-triples))))
227+
(->BlankNodeColl subj-node triple-block)))
228+
229+
(defmethod ast/ast-node->jena :triple/bnodes
230+
[{:keys [active-bnode-map] :as opts} [_ po-pairs]]
231+
(make-bnode-coll (get opts @active-bnode-map) po-pairs))
232+
233+
;; Vectors
234+
235+
(defmethod ast/ast-node->jena :triple.vec/spo [_ [_ [s p o]]]
236+
(let [triple-block (ElementPathBlock.)
237+
triples (spo->triples s p o)]
238+
(add-triple-coll! triple-block triples)
239+
triple-block))
240+
241+
(defmethod ast/ast-node->jena :triple.vec/s [_ [_ [s]]]
242+
(let [triple-block (ElementPathBlock.)
243+
triples (s->triples s)]
244+
(add-triple-coll! triple-block triples)
245+
triple-block))
246+
247+
;; Normal Forms
248+
249+
(defmethod ast/ast-node->jena :triple.nform/spo [_ [_ spo]]
250+
(let [triple-block (ElementPathBlock.)
251+
triples (mapcat
252+
(fn [[s po-coll]]
253+
(if (empty? po-coll)
254+
(s->triples s)
255+
(mapcat (fn [[p o]] (spo->triples s p o)) po-coll)))
256+
spo)]
257+
(add-triple-coll! triple-block triples)
258+
triple-block))
259+
260+
(defmethod ast/ast-node->jena :triple.nform/po [_ [_ po]]
261+
(mapcat (fn [[p o-coll]] (map (fn [o] [p o]) o-coll))
262+
po))
263+
264+
(defmethod ast/ast-node->jena :triple.nform/po-empty [_ _]
265+
[])
266+
267+
(defmethod ast/ast-node->jena :triple.nform/o [_ [_ o]]
268+
o)
269+
270+
;; Quads
271+
272+
(defmethod ast/ast-node->jena :triple.quad/spo
273+
[_opts [_ triple-elements]]
274+
(triple-elements->basic-pattern triple-elements))
275+
276+
(defmethod ast/ast-node->jena :triple.quad/gspo
277+
[_opts [_ [graph-node triple-bgp]]]
278+
(basic-pattern->quad-pattern triple-bgp graph-node))

src/main/com/yetanalytics/flint_jena/update.clj

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,6 @@
123123
;; Graph Update
124124
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
125125

126-
(defmethod ast/ast-node->jena :triple/quad-triples
127-
[_opts [_ triple-elements]]
128-
(t/triple-elements->basic-pattern triple-elements))
129-
130-
(defmethod ast/ast-node->jena :triple/quads
131-
[_opts [_ [graph-node triple-bgp]]]
132-
(t/basic-pattern->quad-pattern triple-bgp graph-node))
133-
134126
;; Construct quad accumulators (i.e. QuadBlocks -> QuadAcc)
135127
;; These are defined in this namespace since QuadAcc/QuadDataAcc are found
136128
;; in the sparql.modify.request package, i.e. they're specific to updates.

src/main/com/yetanalytics/flint_jena/where.clj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,7 @@
186186
(defmethod ast/ast-node->jena :where/special
187187
[_ [_ element]]
188188
element)
189+
190+
(defmethod ast/ast-node->jena :where/triple
191+
[_ [_ element]]
192+
element)

0 commit comments

Comments
 (0)