From 4c09fdde9286aed5a9255798f366d847e7722a26 Mon Sep 17 00:00:00 2001 From: Elizabeth White Date: Fri, 19 Feb 2016 14:51:25 -0700 Subject: [PATCH 1/6] added Bind, Strafter (buggy), changed Filter --- .../clojure/edu/ucdenver/ccp/kr/sparql.clj | 64 ++++++++++++++++--- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj b/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj index 35e9542..2f33fd6 100644 --- a/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj +++ b/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj @@ -15,6 +15,7 @@ (def select-default "") (def select-distinct "DISTINCT ") (def select-reduced "REDUCED ") +(def pound-sign "#") ;; use for strafter (def sparql-1-0 :sparql-1-0) (def sparql-1-1 :sparql-1-1) @@ -243,11 +244,43 @@ (sparql-query-body optional-clauses) " }\n ")) +(defn filter-body [filter-clauses] + (str " FILTER ( " + (operator-body filter-clauses) + " )\n ")) + +(defn bind-body [bind-clauses] + (str " BIND " + (operator-body bind-clauses) + " \n ")) + +(defn iri-body [iri-clauses] + (str " IRI " + (operator-body iri-clauses) + " \n ")) + +(defn concat-body [concat-clauses] + (str " CONCAT " + (operator-body concat-clauses) + " \n ")) + +(defn strafter-body [strafter-clauses] + (str " STRAFTER " + (operator-body strafter-clauses) + " \n ")) + + + + ;; (def operator-bound [args] ;; (str " bound(" (filter-body (first args)) ") ")) (declare operator-body) +(defn naked-unary-operator [op-name] + (fn [args] + (str " " op-name (operator-body (first args))))) + (defn unary-operator [op-name] (fn [args] (str " " op-name "(" (operator-body (first args)) ") "))) @@ -304,6 +337,12 @@ :str (unary-operator "str") :lang (unary-operator "lang") :datatype (unary-operator "datatype") + ;; duplicate bind + :bind (naked-unary-operator "bind") + :filter (naked-unary-operator "filter") + :strafter (binary-prefix-operator "strafter") + :concat (n-ary-operator "concat") + :iri (unary-operator "iri") ;;:isBLANK (unary-operator "isBlank") ;;:isLITERAL (unary-operator "isLiteral") @@ -328,7 +367,7 @@ "/" (binary-operator "/") "+" (binary-operator "+") "-" (binary-operator "-") - + :as (binary-operator "AS") ;; '|| (n-ary-operator "||") ;; '&& (n-ary-operator "&&") @@ -391,10 +430,6 @@ (item-to-sparql operator-expression))))) ;;(str operator-expression))))) -(defn filter-body [filter-clause] - (str " FILTER ( " (operator-body filter-clause) ") ")) - - (defn sparql-query-body [triple-pattern] (cond ;; (not (seq? triple-pattern)) "" @@ -404,7 +439,14 @@ (apply str (interleave (map sparql-query-body triple-pattern) ;;(repeat ". \n"))) (repeat " \n"))) - (sparql-operator? (first triple-pattern)) (filter-body triple-pattern) + ;; used as a catch-all; this routes anything unusual to a FILTER. + ;; (sparql-operator? (first triple-pattern)) (filter-body triple-pattern) + (sparql-operator? (first triple-pattern)) (operator-body triple-pattern) + (= :bind (first triple-pattern)) (bind-body triple-pattern) + (= :iri (first triple-pattern)) (iri-body triple-pattern) + (= :concat (first triple-pattern)) (concat-body triple-pattern) + (= :strafter (first triple-pattern)) (strafter-body triple-pattern) + (= :filter (first triple-pattern)) (filter-body triple-pattern) (= :union (first triple-pattern)) (union-body (rest triple-pattern)) (= :optional (first triple-pattern)) (optional-body (rest triple-pattern)) :default (sparql-statement triple-pattern))) @@ -412,6 +454,7 @@ ;;; full query bodies ;;; -------------------------------------------------------- + (defn sparql-ask-query [triple-pattern & [options]] (let [vars (variables triple-pattern) non-vars (symbols-no-vars triple-pattern) @@ -423,6 +466,7 @@ (sparql-query-body triple-pattern) "}"))) + (defn sparql-select-query [triple-pattern & [options]] (let [vars (or (and options (:select-vars options)) @@ -430,7 +474,11 @@ non-vars (symbols-no-vars triple-pattern) namespaces (distinct (map namespace non-vars)) prefixes (get-prefixes-from-namespaces namespaces)] - (str + (println "options" options) + (println "non-vars" non-vars) + (println "prefixes" prefixes) + (println "namespaces" namespaces) + (str (prefix-block prefixes) (apply str "SELECT " *select-type* (interleave (map sym-to-sparql vars) (repeat " "))) @@ -459,7 +507,7 @@ "\n" "WHERE { " (sparql-query-body triple-pattern) - "}" + " } " (if *select-limit* (str " LIMIT " *select-limit* " ") "") From 71e6a7965e6e2790f1f78d03131b22566f81e470 Mon Sep 17 00:00:00 2001 From: Elizabeth White Date: Fri, 1 Apr 2016 14:34:21 -0600 Subject: [PATCH 2/6] added more sparql keywords and explicit filter statements --- .../clojure/edu/ucdenver/ccp/kr/sparql.clj | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj b/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj index 2f33fd6..aea2c24 100644 --- a/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj +++ b/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj @@ -15,14 +15,13 @@ (def select-default "") (def select-distinct "DISTINCT ") (def select-reduced "REDUCED ") -(def pound-sign "#") ;; use for strafter (def sparql-1-0 :sparql-1-0) (def sparql-1-1 :sparql-1-1) ;;; dynamic variables to control function -(defonce ^:dynamic *select-type* select-default) +(defonce ^:dynamic *select-type* select-distinct) (defonce ^:dynamic *select-limit* nil) @@ -244,6 +243,15 @@ (sparql-query-body optional-clauses) " }\n ")) +(defn graph-body [graph-clauses] + (str " GRAPH " (first graph-clauses) " { \n" + (sparql-query-body (rest graph-clauses)) + " }\n ")) + +(defn pound-operator [args] + (str " \"#\" ")) + + (defn filter-body [filter-clauses] (str " FILTER ( " (operator-body filter-clauses) @@ -269,6 +277,11 @@ (operator-body strafter-clauses) " \n ")) +(defn strbefore-body [strbefore-clauses] + (str " STRBEFORE " + (operator-body strbefore-clauses) + " \n ")) + @@ -277,6 +290,9 @@ (declare operator-body) +(defn empty-operator [op-name] + (str op-name)) + (defn naked-unary-operator [op-name] (fn [args] (str " " op-name (operator-body (first args))))) @@ -295,6 +311,13 @@ (str " " op-name "(" (operator-body (first args)) ", " (operator-body (second args)) ") "))) +(defn prefix-n-ary-operator [op-name] + (fn [args] + (str op-name " ( " + (apply str (interpose (map operator-body args) + )) + " ) \n "))) + (defn n-ary-operator [op-name] (fn [args] (str " ( " @@ -304,7 +327,7 @@ (defn not-operator [args] - (str "!" (operator-body (first args)))) + (str " ! " (operator-body (first args)))) ;; if the arg is a raw string box it, otherwise leave it alone @@ -338,10 +361,12 @@ :lang (unary-operator "lang") :datatype (unary-operator "datatype") ;; duplicate bind + :pound pound-operator :bind (naked-unary-operator "bind") - :filter (naked-unary-operator "filter") + :filter (unary-operator "filter") :strafter (binary-prefix-operator "strafter") - :concat (n-ary-operator "concat") + :strbefore (binary-prefix-operator "strbefore") + :concat (binary-prefix-operator "concat") :iri (unary-operator "iri") ;;:isBLANK (unary-operator "isBlank") ;;:isLITERAL (unary-operator "isLiteral") @@ -357,6 +382,8 @@ "&&" (n-ary-operator "&&") "!" not-operator + := (binary-operator "=") + :!= (binary-operator "!=") "=" (binary-operator "=") "!=" (binary-operator "!=") "<" (binary-operator "<") @@ -449,6 +476,7 @@ (= :filter (first triple-pattern)) (filter-body triple-pattern) (= :union (first triple-pattern)) (union-body (rest triple-pattern)) (= :optional (first triple-pattern)) (optional-body (rest triple-pattern)) + (= :graph (first triple-pattern)) (graph-body (rest triple-pattern)) :default (sparql-statement triple-pattern))) ;;; full query bodies @@ -556,7 +584,7 @@ (defn query ([pattern] (query-pattern *kb* pattern)) - ([kb pattern & [options]] (binding [*kb* kb] + ([kb pattern & [options]] (binding [*kb* kb *select-type* select-distinct] (query-pattern *kb* pattern options)))) (defn query-template From 945fff3790db9414c1f8ad801cd10adb4cbc030a Mon Sep 17 00:00:00 2001 From: Marc Daya Date: Tue, 31 May 2016 15:34:32 -0600 Subject: [PATCH 3/6] Test case fixes needed for modified rule syntax. Because it no longer be assumed that all SPARQL functions and operators occur in a `filter` block, `filter` must now be explicitly specified. The change of default select-type default has been reverted, as this breaks the standard query pattern, and is in fact not needed (cf. https://github.com/UCDenver-ccp/kr/pull/4). --- .../clojure/edu/ucdenver/ccp/kr/sparql.clj | 94 +--- .../edu/ucdenver/ccp/test/kr/test_sparql.clj | 471 +++++++++--------- 2 files changed, 243 insertions(+), 322 deletions(-) diff --git a/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj b/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj index aea2c24..425ce25 100644 --- a/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj +++ b/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj @@ -6,11 +6,7 @@ (import java.util.Map ;is this actaully used? java.net.URI)) -;;; -------------------------------------------------------- -;;; constants and variables -;;; -------------------------------------------------------- - -;;; constants +;; ----------------------------------------------------------- constants --- ;; (def select-default "") (def select-distinct "DISTINCT ") @@ -19,9 +15,9 @@ (def sparql-1-0 :sparql-1-0) (def sparql-1-1 :sparql-1-1) -;;; dynamic variables to control function +;; ------------------------------- dynamic variables to control function --- ;; -(defonce ^:dynamic *select-type* select-distinct) +(defonce ^:dynamic *select-type* select-default) (defonce ^:dynamic *select-limit* nil) @@ -31,9 +27,7 @@ (defonce ^:dynamic *force-prefixes* nil) -;;; -------------------------------------------------------- -;;; protocol -;;; -------------------------------------------------------- +;; --------------------------------------------------------- KB protocol --- ;; (defprotocol sparqlKB (ask-pattern [kb pattern] [kb pattern options] @@ -56,10 +50,7 @@ (construct-sparql [kb sparql-string]) (construct-visit-sparql [kb visitor sparql-string])) - -;;; -------------------------------------------------------- -;;; helpers -;;; -------------------------------------------------------- +;; ---------------------------------------------------------- helper fns --- ;; ;; shouldn't these be calling the RDF based ones? @@ -80,8 +71,6 @@ (defn sparql-str-lang [s l] (str (pr-str s) "@" l)) - - (defn sparql-ify-uri [uri] (sparql-uri (.toString uri))) @@ -95,9 +84,6 @@ (defmethod sparql-ify String [s] (pr-str s)) (defmethod sparql-ify :default [s] (pr-str s)) - - - ;; it's possible this could be done faster with a call to rdf/object ;; and then re-serializing that (defn item-to-sparql [s] @@ -126,7 +112,6 @@ ;;:else (pr-str s))) :else (sparql-ify s))) - ;; :inverse ;; :or ;; default is just a sequence as with / @@ -198,13 +183,9 @@ (def property-position-to-sparql property-path) +;; -------------------------------------------------- query construction --- ;; -;;; -------------------------------------------------------- -;;; query text -;;; -------------------------------------------------------- - -;;; query component construction -;;; -------------------------------------------------------- +;; --- blocks (declare sparql-query-body) @@ -248,10 +229,13 @@ (sparql-query-body (rest graph-clauses)) " }\n ")) +;; --- operators + +(declare operator-body) + (defn pound-operator [args] (str " \"#\" ")) - (defn filter-body [filter-clauses] (str " FILTER ( " (operator-body filter-clauses) @@ -282,14 +266,9 @@ (operator-body strbefore-clauses) " \n ")) - - - ;; (def operator-bound [args] ;; (str " bound(" (filter-body (first args)) ") ")) -(declare operator-body) - (defn empty-operator [op-name] (str op-name)) @@ -325,11 +304,9 @@ (map operator-body args))) " ) \n "))) - (defn not-operator [args] (str " ! " (operator-body (first args)))) - ;; if the arg is a raw string box it, otherwise leave it alone (defn safe-box-raw-string [arg] (if (string? arg) @@ -457,6 +434,8 @@ (item-to-sparql operator-expression))))) ;;(str operator-expression))))) +;; -------------------------------------------------------------- SPARQL --- ;; + (defn sparql-query-body [triple-pattern] (cond ;; (not (seq? triple-pattern)) "" @@ -479,10 +458,6 @@ (= :graph (first triple-pattern)) (graph-body (rest triple-pattern)) :default (sparql-statement triple-pattern))) -;;; full query bodies -;;; -------------------------------------------------------- - - (defn sparql-ask-query [triple-pattern & [options]] (let [vars (variables triple-pattern) non-vars (symbols-no-vars triple-pattern) @@ -494,19 +469,13 @@ (sparql-query-body triple-pattern) "}"))) - (defn sparql-select-query [triple-pattern & [options]] - (let [vars (or (and options - (:select-vars options)) + (let [vars (or (and options (:select-vars options)) (variables triple-pattern)) non-vars (symbols-no-vars triple-pattern) namespaces (distinct (map namespace non-vars)) prefixes (get-prefixes-from-namespaces namespaces)] - (println "options" options) - (println "non-vars" non-vars) - (println "prefixes" prefixes) - (println "namespaces" namespaces) - (str + (str (prefix-block prefixes) (apply str "SELECT " *select-type* (interleave (map sym-to-sparql vars) (repeat " "))) @@ -516,9 +485,7 @@ "}" (if *select-limit* (str " LIMIT " *select-limit* " ") - "") - ))) - + "")))) (defn sparql-construct-query [create-pattern triple-pattern & [options]] (let [;;vars (or (and options @@ -571,11 +538,7 @@ "") ))) -;;; -------------------------------------------------------- -;;; -------------------------------------------------------- -;;; query helpers -;;; -------------------------------------------------------- -;;; -------------------------------------------------------- +;; ------------------------------------------------------- query helpers --- ;; (defn ask ([pattern] (ask *kb* pattern)) @@ -603,7 +566,6 @@ (binding [*kb* kb] (visit-pattern kb visitor pattern options)))) - (defn count-query ;;(defn query-count ([pattern] (count-query *kb* pattern)) @@ -612,7 +574,6 @@ (def query-count count-query) - (defn construct ([create-pattern pattern] (construct-pattern *kb* create-pattern pattern)) @@ -631,8 +592,6 @@ pattern options)))) - - (defn sparql-ask ([sparql-string] (ask *kb* sparql-string)) ([kb sparql-string] (binding [*kb* kb] @@ -678,13 +637,9 @@ (binding [*kb* kb] (construct-visit-sparql *kb* visitor sparql-string)))) - -;;; -------------------------------------------------------- -;;; -------------------------------------------------------- - -;;; -------------------------------------------------------- -;;; these are some crazy helpers that need to be revisited -;;; -------------------------------------------------------- +;; ------------------------------------------------------------------------- ;; +;; these are some crazy helpers that need to be revisited +;; ------------------------------------------------------------------------- ;; (defn pmap-query [kb fcn l] (pmap (fn [x num] @@ -704,7 +659,6 @@ l (range))) - (defn pmap-some [kb fcn list-list] (pmap (fn [l num] (with-new-connection kb conn @@ -714,11 +668,3 @@ l))) list-list (range))) - - - -;;; -------------------------------------------------------- -;;; END -;;; -------------------------------------------------------- - - diff --git a/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj b/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj index 737e8b0..8f30723 100644 --- a/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj +++ b/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj @@ -19,81 +19,67 @@ (def uri-b (URI. "http://www.example.org/b")) (def uri-x (URI. "http://www.example.org/x")) - (def test-triples-uri - '((ex/a rdf/type foaf/Person ) - (ex/a foaf/name "Alice" ) - (ex/a foaf/mbox "" ) - (ex/a foaf/mbox "" ) - (ex/a foaf/knows ex/b) - (ex/b rdf/type foaf/Person ) - (ex/b foaf/name "Bob" ))) + '((ex/a rdf/type foaf/Person) + (ex/a foaf/name "Alice") + (ex/a foaf/mbox "") + (ex/a foaf/mbox "") + (ex/a foaf/knows ex/b) + (ex/b rdf/type foaf/Person) + (ex/b foaf/name "Bob"))) (def test-triples-6-1 - '((ex/a rdf/type foaf/Person ) - (ex/a foaf/name "Alice" ) - (ex/a foaf/mbox "" ) - (ex/a foaf/mbox "" ) - - (ex/b rdf/type foaf/Person ) - (ex/b foaf/name "Bob" ))) + '((ex/a rdf/type foaf/Person) + (ex/a foaf/name "Alice") + (ex/a foaf/mbox "") + (ex/a foaf/mbox "") + (ex/b rdf/type foaf/Person) + (ex/b foaf/name "Bob"))) (def test-triples-6-3 - '((ex/a foaf/name "Alice" ) - (ex/a foaf/homepage "" ) - - (ex/b foaf/name "Bob" ) - (ex/b foaf/mbox "" ))) + '((ex/a foaf/name "Alice") + (ex/a foaf/homepage "") + (ex/b foaf/name "Bob") + (ex/b foaf/mbox ""))) (def test-triples-7 - '((ex/a dc10/title "SPARQL Query Language Tutorial" ) - (ex/a dc10/creator "Alice" ) - - (ex/b dc11/title "SPARQL Protocol Tutorial" ) - (ex/b dc11/creator "Bob" ) - - (ex/c dc10/title "SPARQL" ) - (ex/c dc11/title "SPARQL (updated)" ))) + '((ex/a dc10/title "SPARQL Query Language Tutorial") + (ex/a dc10/creator "Alice") + (ex/b dc11/title "SPARQL Protocol Tutorial") + (ex/b dc11/creator "Bob") + (ex/c dc10/title "SPARQL") + (ex/c dc11/title "SPARQL (updated)"))) (def test-triples-10-2-1 - '((ex/a foaf/givenname "Alice" ) - (ex/a foaf/family_name "Hacker" ) - - (ex/b foaf/firstname "Bob" ) - (ex/b foaf/surname "Hacker" ))) + '((ex/a foaf/givenname "Alice") + (ex/a foaf/family_name "Hacker") + (ex/b foaf/firstname "Bob") + (ex/b foaf/surname "Hacker"))) (def test-triples-numbers-equality - '((ex/a foaf/givenname "Alice" ) - (ex/a foaf/surname "Hacker" ) - (ex/a foaf/age [40 xsd/integer]) - - (ex/b foaf/firstname "Bob" ) - (ex/b foaf/surname "Hacker" ) - (ex/b foaf/age 40) ;the default should be xsd/integer - - (ex/c foaf/firstname "Fred" ) - (ex/c foaf/surname "Hacker" ) - (ex/c foaf/age [50 xsd/integer])));50))) + '((ex/a foaf/givenname "Alice") + (ex/a foaf/surname "Hacker") + (ex/a foaf/age [40 xsd/integer]) + (ex/b foaf/firstname "Bob") + (ex/b foaf/surname "Hacker") + (ex/b foaf/age 40) ;the default should be xsd/integer + (ex/c foaf/firstname "Fred") + (ex/c foaf/surname "Hacker") + (ex/c foaf/age [50 xsd/integer])));50))) (def test-triples-lang - '((ex/a foaf/firstname "Alice" ) - (ex/b foaf/firstname ["Bob" "en"]) - (ex/c foaf/firstname ["Bob"]))) + '((ex/a foaf/firstname "Alice") + (ex/b foaf/firstname ["Bob" "en"]) + (ex/c foaf/firstname ["Bob"]))) (def test-triples-custom-type - '((ex/a ex/p ["foo" ex/custom]) - (ex/b ex/p ["foo" ex/custom2]))) + '((ex/a ex/p ["foo" ex/custom]) + (ex/b ex/p ["foo" ex/custom2]))) (def test-triples-custom-type-uri - `((ex/a ex/p ["foo" ex/custom]) - (ex/b ex/p ["foo" ~(URI. "http://www.example.org/custom")]))) - - - -;;; -------------------------------------------------------- -;;; helpers -;;; -------------------------------------------------------- + `((ex/a ex/p ["foo" ex/custom]) + (ex/b ex/p ["foo" ~(URI. "http://www.example.org/custom")]))) ;;; -------------------------------------------------------- ;;; tests @@ -112,11 +98,11 @@ (_/person foaf/mbox ?/email))))))) (kb-test test-optional test-triples-6-1 - (is (= 3 - (count - (query '((?/person foaf/name ?/name) - (:optional - (?/person foaf/mbox ?/email)))))))) + (is (= 3 + (count + (query '((?/person foaf/name ?/name) + (:optional + (?/person foaf/mbox ?/email)))))))) (kb-test test-count-query test-triples-6-1 (is (= 3 @@ -124,225 +110,214 @@ (:optional (?/person foaf/mbox ?/email))))))) - (kb-test test-optional-select-6-3 test-triples-6-3 - (is (= 2 - (count - (query '((?/person foaf/name ?/name) - (:optional (?/person foaf/mbox ?/email)) - (:optional (?/person foaf/homepage ?/hpage)))))))) + (is (= 2 + (count + (query '((?/person foaf/name ?/name) + (:optional (?/person foaf/mbox ?/email)) + (:optional (?/person foaf/homepage ?/hpage)))))))) (kb-test test-union-select-7 test-triples-7 - (is (= 2 - (count - (query - '(:union - ((?/book dc10/title ?/title) - (?/book dc10/creator ?/author)) - ((?/book dc11/title ?/title) - (?/book dc11/creator ?/author)))))))) + (is (= 2 + (count + (query + '(:union + ((?/book dc10/title ?/title) + (?/book dc10/creator ?/author)) + ((?/book dc11/title ?/title) + (?/book dc11/creator ?/author)))))))) (kb-test test-union-select-10-2-1 test-triples-10-2-1 - (is (= 2 - (count - (query - '((:union ((?/x foaf/firstname ?/gname)) - ((?/x foaf/givenname ?/gname))) - (:union ((?/x foaf/surname ?/fname)) - ((?/x foaf/family_name ?/fname))))))))) + (is (= 2 + (count + (query + '((:union ((?/x foaf/firstname ?/gname)) + ((?/x foaf/givenname ?/gname))) + (:union ((?/x foaf/surname ?/fname)) + ((?/x foaf/family_name ?/fname))))))))) (kb-test test-bound-operator test-triples-6-1 - (is (= 2 ;this is 2 but should be one if ?/person doesn't capture - (count - (query '((?/person foaf/name ?/name) - (:optional - (?/person foaf/mbox ?/email)) - (:bound ?/email)))))) - (is (= 1 ;just bob - (count - (query '((?/person foaf/name ?/name) - (:optional - (?/person foaf/mbox ?/email)) - (:not (:bound ?/email)))))))) - + (is (= 2 ;this is 2 but should be one if ?/person doesn't capture + (count + (query '((?/person foaf/name ?/name) + (:optional (?/person foaf/mbox ?/email)) + (:filter (:bound ?/email))))))) + (is (= 1 ;just bob + (count + (query '((?/person foaf/name ?/name) + (:optional (?/person foaf/mbox ?/email)) + (:filter (:not (:bound ?/email))))))))) + (kb-test test-not-operator test-triples-6-1 - (is (= 1 ;just bob - (count - (query '((?/person foaf/name ?/name) - (:optional - (?/person foaf/mbox ?/email)) - (:not (:bound ?/email))))))) - (is (= 1 ;just bob - (count - (query '((?/person foaf/name ?/name) - (:optional - (?/person foaf/mbox ?/email)) - (! (:bound ?/email)))))))) + (is (= 1 ;just bob + (count + (query '((?/person foaf/name ?/name) + (:optional (?/person foaf/mbox ?/email)) + (:filter (:not (:bound ?/email)))))))) + (is (= 1 ;just bob + (count + (query '((?/person foaf/name ?/name) + (:optional (?/person foaf/mbox ?/email)) + (:filter (! (:bound ?/email))))))))) (kb-test test-numbers test-triples-numbers-equality - (is (= 2 ;two because of reflection - (count (query '((?/person foaf/surname ?/name) - (?/person foaf/age ?/age1) - (?/person2 foaf/surname ?/name) - (?/person2 foaf/age ?/age2) - (= ?/age1 ?/age2) - (!= ?/person ?/person2) - ))))) - (is (= 2 - (count (query '((?/person foaf/surname ?/name) - (?/person foaf/age ?/age1) - (?/person2 foaf/surname ?/name) - (?/person2 foaf/age ?/age2) - (> ?/age1 ?/age2) - )))))) + (is (= 2 ;two because of reflection + (count (query '((?/person foaf/surname ?/name) + (?/person foaf/age ?/age1) + (?/person2 foaf/surname ?/name) + (?/person2 foaf/age ?/age2) + (:filter (= ?/age1 ?/age2)) + (:filter (!= ?/person ?/person2))))))) + (is (= 2 + (count (query '((?/person foaf/surname ?/name) + (?/person foaf/age ?/age1) + (?/person2 foaf/surname ?/name) + (?/person2 foaf/age ?/age2) + (:filter (> ?/age1 ?/age2)))))))) (kb-test test-n-ary-or test-triples-numbers-equality - (is (= 3 - (count (query '((?/person foaf/surname ?/name) - (?/person foaf/age ?/age) - (:or (= ?/age 30) - (= ?/age 40) - (= ?/age 50)))))))) + (is (= 3 + (count (query '((?/person foaf/surname ?/name) + (?/person foaf/age ?/age) + (:filter (:or (= ?/age 30) + (= ?/age 40) + (= ?/age 50))))))))) + +(kb-test test-n-ary-and test-triples-numbers-equality + (is (= 2 ;two because of reflection + (count (query '((?/person foaf/surname ?/name) + (?/person foaf/age ?/age1) + (?/person2 foaf/surname ?/name) + (?/person2 foaf/age ?/age2) + (:filter (:and (= ?/age1 ?/age2) + (!= ?/person ?/person2))))))))) (kb-test test-boxed-number test-triples-numbers-equality - (is (= 2 - (count (query '((?/person foaf/surname ?/name) - (?/person foaf/age 40)))))) - (is (= 0 - (count (query '((?/person foaf/surname ?/name) - (?/person foaf/age [40])))))) - (is (= 2 - (count (query '((?/person foaf/surname ?/name) - (?/person foaf/age [40 xsd/integer])))))) - - (is (= 2 - (count (query '((?/person foaf/surname ?/name) - (?/person foaf/age ["40" xsd/integer]))))))) - - -(kb-test test-lang test-triples-lang - (is (= 3 - (count (query '((?/person foaf/firstname ?/x)))))) - ;;TODO this langague tag is wrong there shouldn't need to be - ;; nested escaped quotes (is (= 2 - (count (query '((?/person foaf/firstname ?/x) - (= (:lang ?/x) ["en"])))))) - ;; the next to are auto-languaged into "en" - (is (= 1 - (count (query '((?/person foaf/firstname "Bob")))))) - (is (= 1 - (count (query '((?/person foaf/firstname "Alice")))))) - ;; forced "en" - (is (= 1 - (count (query '((?/person foaf/firstname ["Alice" "en"])))))) - ;; boxed forcing off auto-language thus missing - (is (= 0 - (count (query '((?/person foaf/firstname ["Alice"])))))) - ;;note the lower case 'b' on the failure test + (count (query '((?/person foaf/surname ?/name) + (?/person foaf/age 40)))))) (is (= 0 - (count (query '((?/person foaf/firstname "bob"))))))) - + (count (query '((?/person foaf/surname ?/name) + (?/person foaf/age [40])))))) + (is (= 2 + (count (query '((?/person foaf/surname ?/name) + (?/person foaf/age [40 xsd/integer])))))) + (is (= 2 + (count (query '((?/person foaf/surname ?/name) + (?/person foaf/age ["40" xsd/integer]))))))) +(kb-test test-lang test-triples-lang + (testing "simple" + (is (= 3 + (count (query '((?/person foaf/firstname ?/x))))))) + (testing "nested quotes" + ;;TODO this langague tag is wrong there shouldn't need to be + ;; nested escaped quotes + (is (= 2 + (count (query '((?/person foaf/firstname ?/x) + (:filter (= (:lang ?/x) ["en"])))))))) + (testing "auto-magically set language as 'en'" + (is (= 1 + (count (query '((?/person foaf/firstname "Bob")))))) + (is (= 1 + (count (query '((?/person foaf/firstname "Alice"))))))) + (testing "force langugage as 'en'" + (is (= 1 + (count (query '((?/person foaf/firstname ["Alice" "en"]))))))) + (testing "boxed, unforced; language missing" + ;; FIXME: Why do we allow this? This leads to inconsistent + ;; representation of literals. Is "Alice" really not the same as + ;; ["Alice"]? + (is (= 0 + (count (query '((?/person foaf/firstname ["Alice"]))))))) + (testing "exact match; 'bob' isn't 'Bob'" + (is (= 0 + (count (query '((?/person foaf/firstname "bob")))))))) (kb-test test-query-visitor test-triples-lang - (let [count (atom 0)] - (query-visit (fn [bindings] (swap! count inc)) - '((?/person foaf/firstname ?/x))) - (is (= 3 @count))) - - (query-visit (fn [bindings] (is (= 2 (count bindings)))) - '((?/person foaf/firstname ?/x))) - - (query-visit (fn [bindings] - (let [key-set (set (map first bindings))] - (is (key-set '?/x)) - (is (key-set '?/person)))) - '((?/person foaf/firstname ?/x)))) + (let [count (atom 0)] + (query-visit (fn [bindings] (swap! count inc)) + '((?/person foaf/firstname ?/x))) + (is (= 3 @count))) + + (query-visit (fn [bindings] (is (= 2 (count bindings)))) + '((?/person foaf/firstname ?/x))) + + (query-visit (fn [bindings] + (let [key-set (set (map first bindings))] + (is (key-set '?/x)) + (is (key-set '?/person)))) + '((?/person foaf/firstname ?/x)))) ;; there is a bug where "3"^^ ;; was coming out as "3" instead of 3 (kb-test test-integer-clj-ify test-triples-numbers-equality - (is (= 40 - ('?/age (first (query '((?/person foaf/givenname "Alice") - (?/person foaf/age ?/age)))))))) - - + (is (= 40 + ('?/age (first (query '((?/person foaf/givenname "Alice") + (?/person foaf/age ?/age)))))))) (kb-test test-backquote-operators test-triples-numbers-equality - (is (= 2 ;two because of reflection - (count (query `((?/person foaf/surname ?/name) - (?/person foaf/age ?/age1) - (?/person2 foaf/surname ?/name) - (?/person2 foaf/age ?/age2) - (= ?/age1 ?/age2) - (!= ?/person ?/person2) - ))))) - (is (= 2 - (count (query `((?/person foaf/surname ?/name) - (?/person foaf/age ?/age1) - (?/person2 foaf/surname ?/name) - (?/person2 foaf/age ?/age2) - (> ?/age1 ?/age2) - )))))) - + (is (= 2 ;two because of reflection + (count (query `((?/person foaf/surname ?/name) + (?/person foaf/age ?/age1) + (?/person2 foaf/surname ?/name) + (?/person2 foaf/age ?/age2) + (:filter (= ?/age1 ?/age2)) + (:filter (!= ?/person ?/person2))))))) + (is (= 2 + (count (query `((?/person foaf/surname ?/name) + (?/person foaf/age ?/age1) + (?/person2 foaf/surname ?/name) + (?/person2 foaf/age ?/age2) + (:filter (> ?/age1 ?/age2)))))))) (kb-test test-strings-in-operators test-triples-6-3 - (is (= 2 - (count (query `((?/person foaf/name ?/name)))))) - (is (= 1 - (count (query `((?/person foaf/name ?/name) - (= "Bob" ?/name)))))) - ;; box to drop the language tag since none given - (is (= 0 - (count (query `((?/person foaf/name ?/name) - (= ["Bob"] ?/name))))))) + (is (= 2 + (count (query `((?/person foaf/name ?/name)))))) + (is (= 1 + (count (query `((?/person foaf/name ?/name) + (:filter (= "Bob" ?/name))))))) + ;; box to drop the language tag since none given + (is (= 0 + (count (query `((?/person foaf/name ?/name) + (:filter (= ["Bob"] ?/name)))))))) (kb-test test-regex-operator test-triples-6-3 - (is (= 2 - (count (query `((?/person foaf/name ?/name)))))) - (is (= 1 - (count (query `((?/person foaf/name ?/name) - (:regex ?/name "^ali" "i"))))))) - + (is (= 2 + (count (query `((?/person foaf/name ?/name)))))) + (is (= 1 + (count (query `((?/person foaf/name ?/name) + (:filter (:regex ?/name "^ali" "i")))))))) (kb-test test-uri-pat test-triples-uri - (is (= 1 - (count (query '((?/person1 foaf/knows ?/person2)))))) - (is (= 1 - (count (query '((ex/a foaf/knows ?/person2)))))) - (is (= 1 - (count (query '((?/person1 foaf/knows ex/b)))))) - (is (= 0 - (count (query '((ex/b foaf/knows ?/person2)))))) - (is (= 1 - (count (query `((?/person1 foaf/knows ~uri-b)))))) - (is (= 1 - (count (query `((~uri-a foaf/knows ?/person2)))))) - (is (ask `((~uri-a foaf/knows ~uri-b)))) - ) - + (is (= 1 + (count (query '((?/person1 foaf/knows ?/person2)))))) + (is (= 1 + (count (query '((ex/a foaf/knows ?/person2)))))) + (is (= 1 + (count (query '((?/person1 foaf/knows ex/b)))))) + (is (= 0 + (count (query '((ex/b foaf/knows ?/person2)))))) + (is (= 1 + (count (query `((?/person1 foaf/knows ~uri-b)))))) + (is (= 1 + (count (query `((~uri-a foaf/knows ?/person2)))))) + (is (ask `((~uri-a foaf/knows ~uri-b))))) (kb-test test-custom-type test-triples-custom-type - (is (= 2 - (count (query '((?/person1 ex/p ?/custom)))))) - (is (= 1 - (count (query '((?/a ex/p ["foo" ex/custom]))))))) + (is (= 2 + (count (query '((?/person1 ex/p ?/custom)))))) + (is (= 1 + (count (query '((?/a ex/p ["foo" ex/custom]))))))) (kb-test test-custom-type-uri test-triples-custom-type-uri - (is (= 2 - (count (query '((?/person1 ex/p ?/custom)))))) - (is (= 2 - (count (query '((?/a ?/p ["foo" ex/custom])))))) - (is (= 2 - (count - (query `((?/a - ?/p - ["foo" ~(URI. "http://www.example.org/custom")]))))))) - - - -;;; -------------------------------------------------------- -;;; END -;;; -------------------------------------------------------- + (is (= 2 + (count (query '((?/person1 ex/p ?/custom)))))) + (is (= 2 + (count (query '((?/a ?/p ["foo" ex/custom])))))) + (is (= 2 + (count + (query `((?/a + ?/p + ["foo" ~(URI. "http://www.example.org/custom")]))))))) From 50226092e8a74ae4495869c60ffd24a1769b94a1 Mon Sep 17 00:00:00 2001 From: Marc Daya Date: Wed, 1 Jun 2016 11:11:38 -0600 Subject: [PATCH 4/6] Test cases for new SPARQL operators. --- .../edu/ucdenver/ccp/test/kr/test_sparql.clj | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj b/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj index 8f30723..7a9670c 100644 --- a/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj +++ b/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj @@ -321,3 +321,29 @@ (query `((?/a ?/p ["foo" ~(URI. "http://www.example.org/custom")]))))))) + +(kb-test test-bind test-triples-uri + (is (= '("Alice" "Bob") + (sort (map #(get % '?/madeupname) + (query '((_/person foaf/name ?/name) + (:bind (:as ?/name ?/madeupname))))))))) + +(kb-test test-iri test-triples-uri + (is (= 'pro://dom.ext + (get (first (query '((:bind (:as (:iri ["pro://dom.ext"]) ?/name))))) + '?/name)))) + +(kb-test test-concat test-triples-uri + (is (= "foobar" + (get (first (query '((:bind (:as (:concat "foo" "bar") ?/x))))) + '?/x)))) + +(kb-test test-strbefore test-triples-uri + (is (= "foo" + (get (first (query '((:bind (:as (:strbefore "foobar" "bar") ?/x))))) + '?/x)))) + +(kb-test test-strafter test-triples-uri + (is (= "bar" + (get (first (query '((:bind (:as (:strafter "foobar" "foo") ?/x))))) + '?/x)))) From 01b32f75ff0f5d2305f8b301e4ee5329bf0776eb Mon Sep 17 00:00:00 2001 From: Marc Daya Date: Wed, 1 Jun 2016 13:02:19 -0600 Subject: [PATCH 5/6] Remove defuct SPARQL body parse clauses. A collection of SPARQL keywords were added both to the body parsing condition, and to the `sparql-operators` map. The latter actually takes precendence; thus the clauses in the condition would never have been hit. --- .../clojure/edu/ucdenver/ccp/kr/sparql.clj | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj b/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj index 425ce25..2646d33 100644 --- a/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj +++ b/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj @@ -241,34 +241,6 @@ (operator-body filter-clauses) " )\n ")) -(defn bind-body [bind-clauses] - (str " BIND " - (operator-body bind-clauses) - " \n ")) - -(defn iri-body [iri-clauses] - (str " IRI " - (operator-body iri-clauses) - " \n ")) - -(defn concat-body [concat-clauses] - (str " CONCAT " - (operator-body concat-clauses) - " \n ")) - -(defn strafter-body [strafter-clauses] - (str " STRAFTER " - (operator-body strafter-clauses) - " \n ")) - -(defn strbefore-body [strbefore-clauses] - (str " STRBEFORE " - (operator-body strbefore-clauses) - " \n ")) - -;; (def operator-bound [args] -;; (str " bound(" (filter-body (first args)) ") ")) - (defn empty-operator [op-name] (str op-name)) @@ -448,10 +420,6 @@ ;; used as a catch-all; this routes anything unusual to a FILTER. ;; (sparql-operator? (first triple-pattern)) (filter-body triple-pattern) (sparql-operator? (first triple-pattern)) (operator-body triple-pattern) - (= :bind (first triple-pattern)) (bind-body triple-pattern) - (= :iri (first triple-pattern)) (iri-body triple-pattern) - (= :concat (first triple-pattern)) (concat-body triple-pattern) - (= :strafter (first triple-pattern)) (strafter-body triple-pattern) (= :filter (first triple-pattern)) (filter-body triple-pattern) (= :union (first triple-pattern)) (union-body (rest triple-pattern)) (= :optional (first triple-pattern)) (optional-body (rest triple-pattern)) From ad40727f1fbcd84121c8f6dd01803030e8e40ca0 Mon Sep 17 00:00:00 2001 From: Marc Daya Date: Wed, 1 Jun 2016 12:41:05 -0600 Subject: [PATCH 6/6] Add support for operator validation. It may be desirable in some instances to pre-validate the arguments that are passed to an operator, rather than only relying on the backend parsing of the generated SPARQL. One example of this arises from how operators are implemented across different backends. For example, the IRI function across the various platforms does not consistent handle language-tagged strings; some (like Sesame) will do the conversion, while others (like Allegrograph) will not. This also provides a hook to insert validatation to help users with the not-quite-SPARQL pattern syntax. --- .../clojure/edu/ucdenver/ccp/kr/sparql.clj | 78 ++++++++++++------- .../edu/ucdenver/ccp/test/kr/test_sparql.clj | 7 +- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj b/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj index 2646d33..a3d83c1 100644 --- a/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj +++ b/kr-core/src/main/clojure/edu/ucdenver/ccp/kr/sparql.clj @@ -84,33 +84,38 @@ (defmethod sparql-ify String [s] (pr-str s)) (defmethod sparql-ify :default [s] (pr-str s)) +(defn boxed? + [s] + (and (sequential? s) + (and (> (count s) 0) (< (count s) 3)))) + ;; it's possible this could be done faster with a call to rdf/object ;; and then re-serializing that (defn item-to-sparql [s] (cond - ;; symbol - (symbol? s) (sym-to-sparql s) - ;; no inference allowd - ;;(not *infer-literal-type*) (pr-str s) - (not *infer-literal-type*) (sparql-ify s) - ;; boxed - (sequential? s) (let [[x type] s] - (cond - ;; boxed no type - (nil? type) (pr-str (str x)) ; why is a non-string boxed? - ;; lang tagged - (and (string? x) - (string? type)) (sparql-str-lang x type) - ;; typed - :else (str (pr-str (str x)) "^^" - (str (sparql-ify type))))) - ;; plain string default language - (and (string? s) - *use-default-language* - *string-literal-language*) (sparql-str-lang s *string-literal-language*) - ;;plain literal including plain string - ;;:else (pr-str s))) - :else (sparql-ify s))) + ;; symbol + (symbol? s) (sym-to-sparql s) + ;; no inference allowd + ;;(not *infer-literal-type*) (pr-str s) + (not *infer-literal-type*) (sparql-ify s) + ;; boxed + (boxed? s) (let [[x type] s] + (cond + ;; boxed no type + (nil? type) (pr-str (str x)) ; why is a non-string boxed? + ;; lang tagged + (and (string? x) + (string? type)) (sparql-str-lang x type) + ;; typed + :else (str (pr-str (str x)) "^^" + (str (sparql-ify type))))) + ;; plain string default language + (and (string? s) + *use-default-language* + *string-literal-language*) (sparql-str-lang s *string-literal-language*) + ;;plain literal including plain string + ;;:else (pr-str s))) + :else (sparql-ify s))) ;; :inverse ;; :or @@ -233,6 +238,12 @@ (declare operator-body) +(defn- validate + [validator input msg] + (if (validator input) + input + (throw (ex-info msg {:validator validator :input input})))) + (defn pound-operator [args] (str " \"#\" ")) @@ -300,6 +311,13 @@ ;; (str ", " (operator-body (first (rest (rest args)))) ")") ;; ") "))) +(defn- validated + [op-name op-fn validator msg] + (fn [args] + (if (validator args) + ((op-fn op-name) args) + (throw (ex-info msg {:operator op-name :arguments args}))))) + (def sparql-operators {:bound (unary-operator "bound") :isIRI (unary-operator "isIRI") @@ -316,7 +334,9 @@ :strafter (binary-prefix-operator "strafter") :strbefore (binary-prefix-operator "strbefore") :concat (binary-prefix-operator "concat") - :iri (unary-operator "iri") + :iri (validated "iri" unary-operator + #(and (= 1 (count %)) (boxed? (first %))) + "The argument to IRI() must be a boxed string literal without a language tag; the use of language tags in IRIs is not supported by all stores.") ;;:isBLANK (unary-operator "isBlank") ;;:isLITERAL (unary-operator "isLiteral") @@ -409,7 +429,7 @@ ;; -------------------------------------------------------------- SPARQL --- ;; (defn sparql-query-body [triple-pattern] - (cond + (cond ;; (not (seq? triple-pattern)) "" ;; (seq? (first triple-pattern)) (not (sequential? triple-pattern)) "" @@ -514,9 +534,11 @@ (ask-pattern *kb* pattern options)))) (defn query - ([pattern] (query-pattern *kb* pattern)) - ([kb pattern & [options]] (binding [*kb* kb *select-type* select-distinct] - (query-pattern *kb* pattern options)))) + ([pattern] + (query-pattern *kb* pattern)) + ([kb pattern & [options]] + (binding [*kb* kb *select-type* select-distinct] + (query-pattern *kb* pattern options)))) (defn query-template ([result-template pattern] diff --git a/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj b/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj index 7a9670c..32ff2f8 100644 --- a/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj +++ b/kr-core/src/test/clojure/edu/ucdenver/ccp/test/kr/test_sparql.clj @@ -226,9 +226,6 @@ (is (= 1 (count (query '((?/person foaf/firstname ["Alice" "en"]))))))) (testing "boxed, unforced; language missing" - ;; FIXME: Why do we allow this? This leads to inconsistent - ;; representation of literals. Is "Alice" really not the same as - ;; ["Alice"]? (is (= 0 (count (query '((?/person foaf/firstname ["Alice"]))))))) (testing "exact match; 'bob' isn't 'Bob'" @@ -331,7 +328,9 @@ (kb-test test-iri test-triples-uri (is (= 'pro://dom.ext (get (first (query '((:bind (:as (:iri ["pro://dom.ext"]) ?/name))))) - '?/name)))) + '?/name))) + (is (thrown-with-msg? clojure.lang.ExceptionInfo #"must be a boxed string" + (query '((:bind (:as (:iri "pro://dom.ext") ?/name))))))) (kb-test test-concat test-triples-uri (is (= "foobar"