Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PS-455] Implement collections syntactic sugar #36

Merged
merged 34 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1939e32
Add list specs
kelvinqian00 Feb 1, 2024
3300d2f
Add new triple/object and triple/list tags
kelvinqian00 Feb 1, 2024
016c663
Distinguish spo-list from spo
kelvinqian00 Feb 1, 2024
0e380bd
Move all triple and quad specs to triple ns
kelvinqian00 Feb 2, 2024
5064279
Add :where/triple tag as a parallel to :where/special
kelvinqian00 Feb 3, 2024
7386127
Rework blank node validation
kelvinqian00 Feb 5, 2024
ad5b86f
Extract out utility function and simplify bnode path construction
kelvinqian00 Feb 5, 2024
2ef7aaf
Add nform-list spec
kelvinqian00 Feb 6, 2024
0b5b78f
Fix nform specs and remove :triple/object
kelvinqian00 Feb 6, 2024
8604bcc
Remove :triple/object from format.triple tests
kelvinqian00 Feb 6, 2024
f96cc07
Implement triple-vec-no-po and nform-no-po specs
kelvinqian00 Feb 6, 2024
d88c5a5
Remove :triple/object multimethod calls
kelvinqian00 Feb 6, 2024
db6c170
Remove redundant :triple/nform kword
kelvinqian00 Feb 6, 2024
d1c35b2
Replace :triple/vec with :triple.vec/spo
kelvinqian00 Feb 6, 2024
7288e5b
Replace :triple/quad keywords
kelvinqian00 Feb 6, 2024
239cf97
Add bracketted blank node blocks
kelvinqian00 Feb 6, 2024
abab618
Allow bnode ves to be nested in lists
kelvinqian00 Feb 6, 2024
a391461
Add new macro to require-macros
kelvinqian00 Feb 6, 2024
0659153
Use s/defs to avoid undefined function errors
kelvinqian00 Feb 6, 2024
64a0443
Add bnode-pair AST node and use objects as list entry specs
kelvinqian00 Feb 6, 2024
54e62ba
Undo the addition of :triple/bnode-pair
kelvinqian00 Feb 6, 2024
1b440eb
Add tests
kelvinqian00 Feb 6, 2024
9a24a5e
Special case when po-pair vec is empty
kelvinqian00 Feb 6, 2024
f2f2ecd
Add tests and fix bnode bugs during validation
kelvinqian00 Feb 6, 2024
8600e7b
Add end-to-end tests for new features
kelvinqian00 Feb 6, 2024
a0310a3
Update docs with new features
kelvinqian00 Feb 6, 2024
bd5d75b
Special case for empty list
kelvinqian00 Feb 6, 2024
1c36963
Allow combinations of empty and no-empty po-maps
kelvinqian00 Feb 7, 2024
864e473
Update comments
kelvinqian00 Feb 7, 2024
62b2cef
Use subject-coll-nopath spec
kelvinqian00 Feb 7, 2024
79aefe7
Add nopath, novar, and noblank tests for colls
kelvinqian00 Feb 7, 2024
8fe0ff1
Update CHANGELOG and version number
kelvinqian00 Feb 7, 2024
e85ea39
Fix cljs test failures
kelvinqian00 Feb 7, 2024
572555b
Remove outdated line in the doc
kelvinqian00 Feb 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## v0.3.0

- Add support for RDF List syntactic sugar.
- Add support for blank node vector syntactic sugar.
- Modify the AST tree for triples to support the new features and to remove redundant nodes in the tree.
- Rework blank node validation to make the implementation simpler (this results in minor changes to the error output).

## v0.2.1

- Update GitHub Actions CI and CD to remove deprecation warnings.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ If you are using Apache Jena, check out the [flint-jena](https://github.com/yeta
Add the following to your `deps.edn` map.

```clojure
com.yetanalytics/flint {:mvn/version "0.2.1"
com.yetanalytics/flint {:mvn/version "0.3.0"
:exclusions [org.clojure/clojure
org.clojure/clojurescript]}
```
Expand Down
3 changes: 3 additions & 0 deletions dev-resources/test-fixtures/inputs/query/select/select-12.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
;; SELECT with lists
{:select [?x]
:where [[?s ?p (1 ?x 3 4)]]}
7 changes: 7 additions & 0 deletions dev-resources/test-fixtures/inputs/query/select/select-13.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
;; SELECT with blank node vectors
{:prefixes {:$ "<http://foo.org/>"
:foaf "<http://xmlns.com/foaf/0.1/>"}
:select [?x ?name]
:where [[[:p1 "v"] :q1 "w"]
[:x :q2 [:p2 "v"]]
[[:foaf/name ?name :foaf/mbox "<mailto:[email protected]>"]]]}
5 changes: 5 additions & 0 deletions dev-resources/test-fixtures/inputs/query/select/select-14.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
;; SELECT with both lists and blank node vectors
{:prefixes {:$ "<http://foo.org/>"}
:select [?x]
:where [{(1 [:p :q] (2 ?x)) {}
() {:r #{[]}}}]}
4 changes: 4 additions & 0 deletions dev-resources/test-fixtures/outputs/query/select/select-12.rq
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SELECT ?x
WHERE {
?s ?p ( 1 ?x 3 4 ) .
}
9 changes: 9 additions & 0 deletions dev-resources/test-fixtures/outputs/query/select/select-13.rq
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
PREFIX : <http://foo.org/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?x ?name
WHERE {
[ :p1 "v" ] :q1 "w" .
:x :q2 [ :p2 "v" ] .
[ foaf:name ?name ;
foaf:mbox <mailto:[email protected]> ] .
}
6 changes: 6 additions & 0 deletions dev-resources/test-fixtures/outputs/query/select/select-14.rq
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
PREFIX : <http://foo.org/>
SELECT ?x
WHERE {
( 1 [ :p :q ] ( 2 ?x ) ) .
() :r [] .
}
60 changes: 58 additions & 2 deletions doc/triple.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,65 @@ Objects can be one of the following:

**NOTE:** Variables are not allowed in triples in `DELETE DATA` OR `INSERT DATA` clauses.

**NOTE:** Technically literals are allowed in subject position according to the SPARQL spec, but no RDF implementation accepts that, so Flint does not allow for subject literals either.
**NOTE:** Technically literals are allowed in subject position according to the SPARQL spec, but no RDF implementation accepts that, so Flint does not allow for subject literals either (unless they are in an RDF list as described below).

**NOTE:** SPARQL has [syntactic sugar](https://www.w3.org/TR/sparql11-query/#collections) for easy writing of RDF lists, but for simplicity that is not implemented in Flint.
## RDF Lists

Flint supports SPARQL's [syntactic sugar](https://www.w3.org/TR/sparql11-query/#collections) for easy writing of RDF lists. For example:
```clojure
{:select [?x]
:where [[(1 ?x 3 4) ?p ?o]]}
```

becomes
```sparql
SELECT ?x
WHERE {
(1 ?x 3 4) ?p ?o
}
```
which should then expanded out into an RDF list by your SPARQL query engine.

RDF lists be placed in both subject and object position. If they are placed in subject position, then predicates and objects are optional (which is not usually the case). The predicate-object map can be left empty in normal form representation:
```clojure
{:select [?x]
:where [{(1 ?x 3 4) {}}]}
```

and can be left out entirely in triple representation:
```clojure
{:select [?x]
:where [[(1 ?x 3 4)]]}
```

RDF lists can also be nested, both with themselves and with blank node vectors (see below):
```clojure
{:select [?x]
:where [[(1 [:p :q] (2))]]}
```

## Blank Node Vectors

Flint also has support for SPARQL's [blank node syntactic sugar](https://www.w3.org/TR/sparql11-query/#QSynBlankNodes), which will be expanded out by the SPARQL engine. For example:

```clojure
{:prefixes {:foaf "<http://xmlns.com/foaf/0.1/>"
:select [?name]
:where [{[:foaf/name ?name
:foaf/mbox "<mailto:[email protected]>"] {}}]}}
```

becomes:
```sparql
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name
WHERE {
[ foaf:name ?name
foaf:mbox <mailto:[email protected]> ] .
}
```

Note that like with RDF lists, the predicate and object may be omitted here.

## Property Paths

Expand Down
30 changes: 30 additions & 0 deletions src/dev/com/yetanalytics/flint/sparql.clj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,36 @@

;; Query/Update writing sandbox

(comment
(QueryFactory/create
"CONSTRUCT { _:b0 ?pred _:b0 }
WHERE { _:b0 ?pred _:b0 .
OPTIONAL {
?y ?pred2 _:b1
}
_:b0 ?question _:b0 . }")

(QueryFactory/create
"SELECT ?x
WHERE {
{ ?x <http://foo.org> _:1 . }
{ ?y <http://bar.org> _:1 . }
}")

(QueryFactory/create
"PREFIX foo: <http://foo.org>
SELECT ?x WHERE {
?x foo:bar _:1 .
FILTER NOT EXISTS { ?z foo:baz ?w . }
?y foo:qux _:1 .
}")

(QueryFactory/create
"SELECT ?x WHERE {
[<http://bar.org#1> [<http://bar.org#2> ?x]] <http://foo.org> \"w\"
}")
)

(comment
(QueryFactory/create
"SELECT ((AVG(MAX(?x)) + 1) AS ?avg)
Expand Down
77 changes: 54 additions & 23 deletions src/main/com/yetanalytics/flint/format/triple.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,63 @@
(defmethod f/format-ast-node :triple/path [_ [_ path]]
path)

(defmethod f/format-ast-node :triple/vec [_ [_ [s p o]]]
(str s " " p " " o " ."))

(defmethod f/format-ast-node :triple/nform [_ [_ nform]]
nform)

(defmethod f/format-ast-node :triple/spo [{:keys [pretty?]} [_ spo]]
(if pretty?
(str (->> spo
(map (fn [[s po]]
(let [indent (->> (repeat (inc (count s)) " ")
(cstr/join "")
(str "\n"))]
(str s " " (cstr/replace po #"\n" indent)))))
(cstr/join " .\n"))
" .")
(str (->> spo
(map (fn [[s po]] (str s " " po)))
(cstr/join " . "))
(defmethod f/format-ast-node :triple/list [_ [_ list]]
(if (empty? list)
"()" ; Special case for empty lists
(str "( " (cstr/join " " list) " )")))

(defmethod f/format-ast-node :triple/bnodes [{:keys [pretty?]} [_ po-pairs]]
(if (empty? po-pairs)
"[]" ; Treat as a scalar blank node
(let [join-sep (if pretty? " ;\n " " ; ")
po-strs (mapv (fn [[p-str o-str]] (str p-str " " o-str)) po-pairs)]
(str "[ " (cstr/join join-sep po-strs) " ]"))))

(defmethod f/format-ast-node :triple.vec/spo [_ [_ [s-str p-str o-str]]]
(str s-str " " p-str " " o-str " ."))

(defmethod f/format-ast-node :triple.vec/s [_ [_ [s-str]]]
(str s-str " ."))

(defn- format-spo-pretty [s-str po-str]
(let [indent (->> (repeat (inc (count s-str)) " ")
(cstr/join "")
(str "\n"))]
(str s-str " " (cstr/replace po-str #"\n" indent))))

(defn- format-spo [s-str po-str]
(str s-str " " po-str))

(defmethod f/format-ast-node :triple.nform/spo [{:keys [pretty?]} [_ spo-pairs]]
(let [format-spo (if pretty? format-spo-pretty format-spo)
join-sep (if pretty? " .\n" " . ")]
(str (->> spo-pairs
(map (fn [[s-str po-str]]
(if (empty? po-str) s-str (format-spo s-str po-str))))
(cstr/join join-sep))
" .")))

(defmethod f/format-ast-node :triple/po [{:keys [pretty?]} [_ po]]
(defmethod f/format-ast-node :triple.nform/po [{:keys [pretty?]} [_ po-strs]]
(let [join-sep (if pretty? " ;\n" " ; ")]
(->> po
(->> po-strs
(map (fn [[p o]] (str p " " o)))
(cstr/join join-sep))))

(defmethod f/format-ast-node :triple/o [_ [_ o]]
(->> o (cstr/join " , ")))
(defmethod f/format-ast-node :triple.nform/po-empty [_ _]
"")

(defmethod f/format-ast-node :triple.nform/o [_ [_ o-strs]]
(->> o-strs (cstr/join " , ")))

(defn format-quads [quads pretty?]
(-> quads
(f/join-clauses pretty?)
(f/wrap-in-braces pretty?)))

(defmethod f/format-ast-node :triple.quad/gspo
[_ [_ [graph-str spo-str]]]
(str "GRAPH " graph-str " " spo-str))

(defmethod f/format-ast-node :triple.quad/spo
[{:keys [pretty?]} [_ spo-strs]]
(format-quads spo-strs pretty?))
29 changes: 6 additions & 23 deletions src/main/com/yetanalytics/flint/format/update.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,9 @@
[com.yetanalytics.flint.format :as f]
[com.yetanalytics.flint.format.axiom]
[com.yetanalytics.flint.format.prologue]
[com.yetanalytics.flint.format.triple]
[com.yetanalytics.flint.format.triple :as tf]
[com.yetanalytics.flint.format.where]))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Quads
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defn- format-quads [quads pretty?]
(-> quads
(f/join-clauses pretty?)
(f/wrap-in-braces pretty?)))

(defmethod f/format-ast-node :triple/quads
[_ [_ [var-or-iri triples-str]]]
(str "GRAPH " var-or-iri " " triples-str))

(defmethod f/format-ast-node :triple/quad-triples
[{:keys [pretty?]} [_ triples]]
(format-quads triples pretty?))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Graph Management
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down Expand Up @@ -128,19 +111,19 @@
(str "WITH " with))

(defmethod f/format-ast-node :insert-data [{:keys [pretty?]} [_ insert-data]]
(str "INSERT DATA " (format-quads insert-data pretty?)))
(str "INSERT DATA " (tf/format-quads insert-data pretty?)))

(defmethod f/format-ast-node :delete-data [{:keys [pretty?]} [_ delete-data]]
(str "DELETE DATA " (format-quads delete-data pretty?)))
(str "DELETE DATA " (tf/format-quads delete-data pretty?)))

(defmethod f/format-ast-node :delete-where [{:keys [pretty?]} [_ delete-where]]
(str "DELETE WHERE " (format-quads delete-where pretty?)))
(str "DELETE WHERE " (tf/format-quads delete-where pretty?)))

(defmethod f/format-ast-node :delete [{:keys [pretty?]} [_ delete]]
(str "DELETE " (format-quads delete pretty?)))
(str "DELETE " (tf/format-quads delete pretty?)))

(defmethod f/format-ast-node :insert [{:keys [pretty?]} [_ insert]]
(str "INSERT " (format-quads insert pretty?)))
(str "INSERT " (tf/format-quads insert pretty?)))

(defmethod f/format-ast-node :update/insert-data [{:keys [pretty?]} [_ id-update]]
(f/join-clauses id-update pretty?))
Expand Down
3 changes: 3 additions & 0 deletions src/main/com/yetanalytics/flint/format/where.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,8 @@
(defmethod f/format-ast-node :where/special [_ [_ where-form]]
where-form)

(defmethod f/format-ast-node :where/triple [_ [_ triples]]
triples)

(defmethod f/format-ast-node :where [_ [_ where]]
(str "WHERE " where))
8 changes: 1 addition & 7 deletions src/main/com/yetanalytics/flint/spec/query.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,7 @@
::vs/values]
:key-comp-fn key-comp))

(def triples-spec
(s/coll-of (s/or :triple/vec ts/triple-vec-nopath-spec
:triple/nform ts/normal-form-nopath-spec)
:min-count 0
:kind vector?))

(s/def ::construct triples-spec)
(s/def ::construct ts/triple-coll-nopath-spec)

(def construct-query-spec
(sparql-keys :req-un [::construct ::ws/where]
Expand Down
Loading
Loading