From 64942a89dfa8e930b93ce272b5c4bf8a249a9e14 Mon Sep 17 00:00:00 2001 From: Daniel Petranek Date: Fri, 17 May 2024 13:48:36 -0500 Subject: [PATCH] add support for VALUES patterns and VALUES clauses FQL now supports :values patterns so we can use that directly instead of translating to a :values clause. --- resources/sparql.bnf | 4 +- .../fluree/db/query/sparql/translator.cljc | 46 ++++++++------- test/fluree/db/query/sparql_test.cljc | 56 +++++++++++++++---- 3 files changed, 74 insertions(+), 32 deletions(-) diff --git a/resources/sparql.bnf b/resources/sparql.bnf index f6d69f8f5..38db8b7d0 100644 --- a/resources/sparql.bnf +++ b/resources/sparql.bnf @@ -62,7 +62,9 @@ Bind ::= <'BIND' WS '(' WS> Expression Var InlineData ::= <'VALUES'> WS DataBlock ::= InlineDataOneVar | InlineDataFull InlineDataOneVar ::= Var <'{'> WS DataBlockValue* <'}'> -InlineDataFull ::= ( NIL | '(' Var* ')' ) '{' ( '(' DataBlockValue* ')' | NIL )* '}' +InlineDataFull ::= ( NIL | VarList ) WS <'{'> WS ( ValueList WS | NIL )* <'}'> +VarList ::= ( <'('> Var* <')'> ) +ValueList ::= ( <'('> WS DataBlockValue* <')'> ) DataBlockValue ::= iri | RDFLiteral | NumericLiteral | BooleanLiteral | 'UNDEF' WS MinusGraphPattern ::= 'MINUS' GroupGraphPattern GroupOrUnionGraphPattern ::= GroupGraphPattern ( <'UNION'> GroupGraphPattern )* diff --git a/src/clj/fluree/db/query/sparql/translator.cljc b/src/clj/fluree/db/query/sparql/translator.cljc index d40157db4..1c5df6a61 100644 --- a/src/clj/fluree/db/query/sparql/translator.cljc +++ b/src/clj/fluree/db/query/sparql/translator.cljc @@ -274,16 +274,27 @@ (defmethod parse-term :DataBlockValue ;; DataBlockValue ::= iri | RDFLiteral | NumericLiteral | BooleanLiteral | 'UNDEF' WS - [[_ value]] - (if (= value "UNDEF") - nil - (parse-term value))) + [[_ [tag :as value]]] + (cond + ;; iri values need to be wrapped in a value-map + (= tag :iri) {const/iri-type const/iri-anyURI const/iri-value (parse-term value)} + (= value "UNDEF") nil + :else (parse-term value))) + +(defmethod parse-term :VarList + ;; VarList ::= ( <'('> Var* <')'> ) + [[_ & vars]] + (mapv parse-term vars)) + +(defmethod parse-term :ValueList + ;; ValueList ::= ( <'('> WS DataBlockValue* <')'> ) + [[_ & values]] + (mapv parse-term values)) (defmethod parse-term :InlineDataFull - ;; InlineDataFull ::= ( NIL | '(' Var* ')' ) '{' ( '(' DataBlockValue* ')' | NIL )* '}' - [_ data] - (throw (ex-info "Multiple inline data values not supported" - {:status 400 :error :db/invalid-query}))) + ;; InlineDataFull ::= ( NIL | VarList ) WS <'{'> WS ( ValueList WS | NIL )* <'}'> + [[_ vars & data]] + [:values [(parse-term vars)] (mapv parse-term data)]) (defmethod parse-term :InlineDataOneVar ;; InlineDataOneVar ::= Var <'{'> WS DataBlockValue* <'}'> @@ -299,7 +310,7 @@ (defmethod parse-term :GraphPatternNotTriples ;; GraphPatternNotTriples ::= GroupOrUnionGraphPattern | OptionalGraphPattern | MinusGraphPattern | GraphGraphPattern | ServiceGraphPattern | Filter | Bind | InlineData [[_ & non-triples]] - (into [] (mapv parse-term non-triples))) + (mapv parse-term non-triples)) (defmethod parse-term :PrefixedName [[_ & name]] @@ -410,6 +421,11 @@ [[_ & patterns]] [[:where (into [] (mapcat parse-term patterns))]]) +(defmethod parse-rule :ValuesClause + ;; ValuesClause ::= ( <'VALUES'> WS DataBlock )? WS + [[_ datablock]] + [(parse-term datablock)]) + (defmethod parse-rule :OffsetClause ;; OffsetClause ::= <'OFFSET'> WS INTEGER [[_ offset]] @@ -479,17 +495,7 @@ [[_ & modifiers]] (mapcat parse-rule modifiers)) -(defn format-values - [{:keys [where] :as fql}] - (let [{values true - patterns false} - (group-by (fn [pattern] (= (first pattern) :values)) where)] - (cond-> (assoc fql :where patterns) - (seq values) (assoc :values (->> (mapv second values) - (apply mapv (fn [& values] (vec values)))))))) - (defn translate [parsed] (->> parsed - (reduce (fn [fql rule] (into fql (parse-rule rule))) {}) - (format-values))) + (reduce (fn [fql rule] (into fql (parse-rule rule))) {}))) diff --git a/test/fluree/db/query/sparql_test.cljc b/test/fluree/db/query/sparql_test.cljc index 778294dc7..d7a265611 100644 --- a/test/fluree/db/query/sparql_test.cljc +++ b/test/fluree/db/query/sparql_test.cljc @@ -186,20 +186,54 @@ [:filter ["(> ?num 10)"]]]]] where))))) (testing "VALUES" - (let [query "SELECT ?handle + (testing "pattern" + (let [query "SELECT ?handle WHERE {VALUES ?handle { \"dsanchez\" } ?person person:handle ?handle.}"] - (is (= {:where [{"@id" "?person", "person:handle" "?handle"}], - :values [["?handle"] [["dsanchez"]]]} - (select-keys (sparql/->fql query) [:where :values])) - "single var, single val")) - (let [query "SELECT ?handle - WHERE {VALUES ?handle { \"dsanchez\" \"coolguy\"} + (is (= [[:values ["?handle" ["dsanchez"]]] + {"@id" "?person", "person:handle" "?handle"}] + (:where (sparql/->fql query))) + "where pattern: single var, single val")) + (let [query "SELECT ?handle + WHERE {VALUES ?person { :personA :personB } ?person person:handle ?handle.}"] - (is (= {:where [{"@id" "?person", "person:handle" "?handle"}], - :values [["?handle"] [["dsanchez" "coolguy"]]]} - (select-keys (sparql/->fql query) [:where :values])) - "single var, multiple values"))) + (is (= [[:values + ["?person" + [{"@type" "http://www.w3.org/2001/XMLSchema#anyURI", + "@value" ":personA"} + {"@type" "http://www.w3.org/2001/XMLSchema#anyURI", + "@value" ":personB"}]]] + {"@id" "?person", "person:handle" "?handle"}] + (:where (sparql/->fql query))) + "where pattern: single var, multiple values")) + (let [query "SELECT * WHERE { + VALUES (?color ?direction) { + ( dm:red \"north\" ) + ( dm:blue \"west\" ) + }}"] + (is (= [[:values + [["?color" "?direction"]] + [[{"@type" "http://www.w3.org/2001/XMLSchema#anyURI", + "@value" "dm:red"} + "north"] + [{"@type" "http://www.w3.org/2001/XMLSchema#anyURI", + "@value" "dm:blue"} + "west"]]]] + (:where (sparql/->fql query))) + "multiple vars, multiple values"))) + (testing "clause" + (let [query "SELECT ?handle + WHERE { ?person person:handle ?handle.} + VALUES ?person { :personA :personB }"] + (is (= {:where [{"@id" "?person", "person:handle" "?handle"}], + :values + ["?person" + [{"@type" "http://www.w3.org/2001/XMLSchema#anyURI", + "@value" ":personA"} + {"@type" "http://www.w3.org/2001/XMLSchema#anyURI", + "@value" ":personB"}]]} + (select-keys (sparql/->fql query) [:where :values])) + "where pattern: single var, multiple values")))) (testing "BIND" (let [query "SELECT ?person ?handle WHERE {BIND (\"dsanchez\" AS ?handle)