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..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 @@ -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,7 +15,7 @@ (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-default) @@ -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,37 +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 @@ -198,13 +188,9 @@ (def property-position-to-sparql property-path) +;; -------------------------------------------------- query construction --- ;; -;;; -------------------------------------------------------- -;;; query text -;;; -------------------------------------------------------- - -;;; query component construction -;;; -------------------------------------------------------- +;; --- blocks (declare sparql-query-body) @@ -243,11 +229,36 @@ (sparql-query-body optional-clauses) " }\n ")) -;; (def operator-bound [args] -;; (str " bound(" (filter-body (first args)) ") ")) +(defn graph-body [graph-clauses] + (str " GRAPH " (first graph-clauses) " { \n" + (sparql-query-body (rest graph-clauses)) + " }\n ")) + +;; --- operators (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 " \"#\" ")) + +(defn filter-body [filter-clauses] + (str " FILTER ( " + (operator-body filter-clauses) + " )\n ")) + +(defn empty-operator [op-name] + (str op-name)) + +(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)) ") "))) @@ -262,6 +273,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 " ( " @@ -269,10 +287,8 @@ (map operator-body args))) " ) \n "))) - (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 (defn safe-box-raw-string [arg] @@ -295,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") @@ -304,6 +327,16 @@ :str (unary-operator "str") :lang (unary-operator "lang") :datatype (unary-operator "datatype") + ;; duplicate bind + :pound pound-operator + :bind (naked-unary-operator "bind") + :filter (unary-operator "filter") + :strafter (binary-prefix-operator "strafter") + :strbefore (binary-prefix-operator "strbefore") + :concat (binary-prefix-operator "concat") + :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") @@ -318,6 +351,8 @@ "&&" (n-ary-operator "&&") "!" not-operator + := (binary-operator "=") + :!= (binary-operator "!=") "=" (binary-operator "=") "!=" (binary-operator "!=") "<" (binary-operator "<") @@ -328,7 +363,7 @@ "/" (binary-operator "/") "+" (binary-operator "+") "-" (binary-operator "-") - + :as (binary-operator "AS") ;; '|| (n-ary-operator "||") ;; '&& (n-ary-operator "&&") @@ -391,12 +426,10 @@ (item-to-sparql operator-expression))))) ;;(str operator-expression))))) -(defn filter-body [filter-clause] - (str " FILTER ( " (operator-body filter-clause) ") ")) - +;; -------------------------------------------------------------- SPARQL --- ;; (defn sparql-query-body [triple-pattern] - (cond + (cond ;; (not (seq? triple-pattern)) "" ;; (seq? (first triple-pattern)) (not (sequential? triple-pattern)) "" @@ -404,14 +437,15 @@ (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) + (= :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 -;;; -------------------------------------------------------- - (defn sparql-ask-query [triple-pattern & [options]] (let [vars (variables triple-pattern) non-vars (symbols-no-vars triple-pattern) @@ -424,8 +458,7 @@ "}"))) (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)) @@ -440,9 +473,7 @@ "}" (if *select-limit* (str " LIMIT " *select-limit* " ") - "") - ))) - + "")))) (defn sparql-construct-query [create-pattern triple-pattern & [options]] (let [;;vars (or (and options @@ -459,7 +490,7 @@ "\n" "WHERE { " (sparql-query-body triple-pattern) - "}" + " } " (if *select-limit* (str " LIMIT " *select-limit* " ") "") @@ -495,11 +526,7 @@ "") ))) -;;; -------------------------------------------------------- -;;; -------------------------------------------------------- -;;; query helpers -;;; -------------------------------------------------------- -;;; -------------------------------------------------------- +;; ------------------------------------------------------- query helpers --- ;; (defn ask ([pattern] (ask *kb* pattern)) @@ -507,9 +534,11 @@ (ask-pattern *kb* pattern options)))) (defn query - ([pattern] (query-pattern *kb* pattern)) - ([kb pattern & [options]] (binding [*kb* kb] - (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] @@ -527,7 +556,6 @@ (binding [*kb* kb] (visit-pattern kb visitor pattern options)))) - (defn count-query ;;(defn query-count ([pattern] (count-query *kb* pattern)) @@ -536,7 +564,6 @@ (def query-count count-query) - (defn construct ([create-pattern pattern] (construct-pattern *kb* create-pattern pattern)) @@ -555,8 +582,6 @@ pattern options)))) - - (defn sparql-ask ([sparql-string] (ask *kb* sparql-string)) ([kb sparql-string] (binding [*kb* kb] @@ -602,13 +627,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] @@ -628,7 +649,6 @@ l (range))) - (defn pmap-some [kb fcn list-list] (pmap (fn [l num] (with-new-connection kb conn @@ -638,11 +658,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..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 @@ -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,239 @@ (: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" + (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")]))))))) + +(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))) + (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" + (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))))