Skip to content

Commit

Permalink
Merge pull request #769 from fluree/feature/sparql-values
Browse files Browse the repository at this point in the history
SPARQL VALUES patterns
  • Loading branch information
dpetran committed Jun 17, 2024
2 parents b39f11c + dee6c88 commit a74defb
Show file tree
Hide file tree
Showing 14 changed files with 788 additions and 106 deletions.
42 changes: 23 additions & 19 deletions resources/sparql.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ UpdateUnit ::= Update
Prologue ::= ( BaseDecl | PrefixDecl )*
BaseDecl ::= <'BASE'> WS IRIREF
PrefixDecl ::= <'PREFIX'> WS PNAME_NS WS IRIREF
SelectQuery ::= WS SelectClause WS DatasetClause* WS WhereClause WS SolutionModifier WS
SelectQuery ::= WS SelectClause WS DatasetClause WS WhereClause WS SolutionModifier WS
SubSelect ::= SelectClause WhereClause SolutionModifier ValuesClause
SelectClause ::= WS <'SELECT'> WS ( 'DISTINCT' | 'REDUCED')? ( ( WS ( Var | ( <'('> Expression WS 'AS' WS Var <')'> ) ) )+ | ( WS '*' ) )
ConstructQuery ::= 'CONSTRUCT' ( ConstructTemplate DatasetClause* WhereClause SolutionModifier | DatasetClause* 'WHERE' '{' TriplesTemplate? '}' SolutionModifier )
DescribeQuery ::= 'DESCRIBE' ( VarOrIri+ | '*' ) DatasetClause* WhereClause? SolutionModifier
AskQuery ::= 'ASK' DatasetClause* WhereClause SolutionModifier
Modifiers ::= (ValuesClause? | PrettyPrint? ) (PrettyPrint? | ValuesClause? )
DatasetClause ::= <'FROM'> WS ( DefaultGraphClause | NamedGraphClause )
ConstructQuery ::= 'CONSTRUCT' ( ConstructTemplate DatasetClause WhereClause SolutionModifier | DatasetClause 'WHERE' '{' TriplesTemplate? '}' SolutionModifier )
DescribeQuery ::= 'DESCRIBE' ( VarOrIri+ | '*' ) DatasetClause WhereClause? SolutionModifier
AskQuery ::= 'ASK' DatasetClause WhereClause SolutionModifier
Modifiers ::= (ValuesClause? | PrettyPrint? ) (PrettyPrint? | ValuesClause? )
DatasetClause ::= FromClause*
<FromClause> ::= <'FROM'> WS ( DefaultGraphClause | NamedGraphClause )
DefaultGraphClause ::= SourceSelector
NamedGraphClause ::= <'NAMED'> SourceSelector
NamedGraphClause ::= <'NAMED'> WS SourceSelector
<SourceSelector> ::= iri
WhereClause ::= <'WHERE'?> WS GroupGraphPattern WS
SolutionModifier ::= GroupClause? HavingClause? OrderClause? LimitOffsetClauses?
Expand Down Expand Up @@ -56,13 +57,15 @@ GroupGraphPatternSub ::= WS TriplesBlock? ( GraphPatternNotTriples WS <'.'?>
TriplesBlock ::= WS TriplesSameSubjectPath WS ( <'.'> TriplesBlock? WS )?
GraphPatternNotTriples ::= GroupOrUnionGraphPattern | OptionalGraphPattern | MinusGraphPattern | GraphGraphPattern | ServiceGraphPattern | Filter | Bind | InlineData
OptionalGraphPattern ::= <'OPTIONAL'> GroupGraphPattern
GraphGraphPattern ::= 'GRAPH' VarOrIri GroupGraphPattern
ServiceGraphPattern ::= 'SERVICE' 'SILENT'? VarOrIri GroupGraphPattern
GraphGraphPattern ::= <'GRAPH'> WS VarOrIri WS GroupGraphPattern
ServiceGraphPattern ::= <'SERVICE'> WS 'SILENT'? WS VarOrIri GroupGraphPattern
Bind ::= <'BIND' WS '(' WS> Expression <WS 'AS' WS> Var <WS ')' WS>
InlineData ::= <'VALUES'> WS DataBlock
<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 )*
Expand All @@ -76,7 +79,7 @@ ConstructTriples ::= TriplesSameSubject ( '.' ConstructTriples? )?
TriplesSameSubject ::= VarOrTerm PropertyListNotEmpty | TriplesNode PropertyList
PropertyList ::= PropertyListNotEmpty?
PropertyListNotEmpty ::= Verb ObjectList ( <';'> WS ( Verb ObjectList )? )*
Verb ::= VarOrIri | 'a'
Verb ::= VarOrIri | Type
ObjectList ::= Object ( <','> WS Object )*
Object ::= GraphNode
TriplesSameSubjectPath ::= VarOrTerm PropertyListPathNotEmpty | TriplesNodePath PropertyListPath WS
Expand All @@ -92,9 +95,9 @@ PathSequence ::= PathEltOrInverse ( <'/'> PathEltOrInverse )*
<PathElt> ::= PathPrimary PathMod?
<PathEltOrInverse> ::= PathElt | <'^'> PathElt
PathMod ::= '?' | '*' | ('+' INTEGER?) WS
PathPrimary ::= iri | 'a' | '!' PathNegatedPropertySet | '(' Path ')'
PathPrimary ::= iri | Type | '!' PathNegatedPropertySet | '(' Path ')'
PathNegatedPropertySet ::= PathOneInPropertySet | '(' ( PathOneInPropertySet ( '|' PathOneInPropertySet )* )? ')'
PathOneInPropertySet ::= iri | 'a' | '^' ( iri | 'a' )
PathOneInPropertySet ::= iri | Type | '^' ( iri | Type )
Integer ::= INTEGER
TriplesNode ::= Collection | BlankNodePropertyList
BlankNodePropertyList ::= '[' PropertyListNotEmpty ']'
Expand All @@ -105,14 +108,15 @@ CollectionPath ::= '(' GraphNodePath+ ')'
<GraphNode> ::= VarOrTerm | TriplesNode
<GraphNodePath> ::= VarOrTerm | TriplesNodePath
<VarOrTerm> ::= Var | GraphTerm WS
VarOrIri ::= Var | iri WS
<VarOrIri> ::= Var | iri WS
Var ::= VAR1 WS | VAR2 WS
<Type> ::= (WS 'a' WS)
<GraphTerm> ::= iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL
Expression ::= WS ConditionalOrExpression WS
<ConditionalOrExpression> ::= ConditionalAndExpression ( <'||'> ConditionalAndExpression )*
<ConditionalAndExpression> ::= ValueLogical ( <'&&'> ValueLogical )*
ConditionalOrExpression ::= ConditionalAndExpression ( <'||'> ConditionalAndExpression )*
ConditionalAndExpression ::= ValueLogical ( <'&&'> ValueLogical )*
<ValueLogical> ::= RelationalExpression
RelationalExpression ::= NumericExpression WS ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression | 'IN' ExpressionList | 'NOT' 'IN' ExpressionList )?
RelationalExpression ::= NumericExpression WS ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression | 'IN' WS ExpressionList | 'NOT' WS 'IN' WS ExpressionList )?
NumericExpression ::= WS AdditiveExpression WS

<AdditiveExpression> ::= MultiplicativeExpression ( '+' MultiplicativeExpression | '-' MultiplicativeExpression | ( NumericLiteralPositive | NumericLiteralNegative ) ( ( '*' UnaryExpression ) | ( '/' UnaryExpression ) )* )*
Expand Down Expand Up @@ -188,7 +192,7 @@ RegexExpression ::= <'REGEX'> <'('> Expression <','> Expression ( <','> Expr
SubstringExpression ::= <'SUBSTR'> <'('> Expression <','> Expression ( <','> Expression )? <')'>
StrReplaceExpression ::= <'REPLACE'> <'('> Expression <','> Expression <','> Expression ( <','> Expression )? <')'>
ExistsFunc ::= <'EXISTS'> GroupGraphPattern
NotExistsFunc ::= <'NOT'> <'EXISTS'> GroupGraphPattern
NotExistsFunc ::= <'NOT'> WS <'EXISTS'> GroupGraphPattern
Aggregate ::= 'COUNT' WS <'('> WS 'DISTINCT'? WS ( '*' | Expression ) WS <')'> WS
| 'SUM' WS <'('> WS 'DISTINCT'? Expression <')'>
| 'MIN' <'('> WS 'DISTINCT'? Expression <')'>
Expand All @@ -214,7 +218,7 @@ IRIREF ::= #"<[^<>\"{}|^`\x00-\x20]*>" WS
BLANK_NODE_LABEL ::= '_:' ( PN_CHARS_U | #"[0-9]" ) ((PN_CHARS|'.')* PN_CHARS)?
<VAR1> ::= <'?'> VARNAME
<VAR2> ::= <'$'> VARNAME
LANGTAG ::= #"@[a-zA-Z]+-[a-zA-Z0-9]*" WS
LANGTAG ::= #"@[a-zA-Z]+(-[a-zA-Z0-9]+)*" WS
<INTEGER> ::= #"[0-9]+"
<DECIMAL> ::= #"[0-9]*\.[0-9]*"
<DOUBLE> ::= #"[0-9]+\.[0-9]*|(\.[0-9]+)|([0-9]+)" EXPONENT
Expand Down
1 change: 1 addition & 0 deletions src/clj/fluree/db/constants.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
(def ^:const iri-rdf-type "http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
(def ^:const iri-class "http://www.w3.org/2000/01/rdf-schema#Class")
(def ^:const iri-lang-string "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString")
(def ^:const iri-string "http://www.w3.org/2001/XMLSchema#string")

;; rdfs
(def ^:const iri-rdfs:Class "http://www.w3.org/2000/01/rdf-schema#Class")
Expand Down
7 changes: 6 additions & 1 deletion src/clj/fluree/db/query/exec/eval.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,13 @@
[x]
(str x))

(defn in
[term expressions]
(contains? (set expressions) term))

(def allowed-scalar-fns
'#{&& || ! > < >= <= = + - * / quot and bound coalesce if lang nil? as
not not= or re-find re-pattern
not not= or re-find re-pattern in
;; string fns
strStarts strEnds subStr strLen ucase lcase contains strBefore strAfter concat regex replace
;; numeric fns
Expand Down Expand Up @@ -320,6 +324,7 @@
count clojure.core/count
floor fluree.db.query.exec.eval/floor
groupconcat fluree.db.query.exec.eval/groupconcat
in fluree.db.query.exec.eval/in
lang fluree.db.query.exec.eval/lang
lcase fluree.db.query.exec.eval/lcase
median fluree.db.query.exec.eval/median
Expand Down
2 changes: 1 addition & 1 deletion src/clj/fluree/db/query/exec/update.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
where/get-datatype-iri
(->> (generate-sid! db-vol)))
(dbproto/-p-prop @db-vol :datatype p-iri)
(datatype/infer v))
(datatype/infer v (:lang m)))
v* (datatype/coerce-value v dt)]
(flake/create sid pid v* dt t true m)))

Expand Down
27 changes: 27 additions & 0 deletions src/clj/fluree/db/query/exec/where.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,20 @@
(dataset/activate alias)
(match-clause fuel-tracker solution clause error-ch)))

(defmethod match-pattern :exists
[ds fuel-tracker solution pattern error-ch]
(let [clause (pattern-data pattern)]
(go
(when (async/<! (match-clause ds fuel-tracker solution clause error-ch))
solution))))

(defmethod match-pattern :not-exists
[ds fuel-tracker solution pattern error-ch]
(let [clause (pattern-data pattern)]
(go
(when-not (async/<! (match-clause ds fuel-tracker solution clause error-ch))
solution))))

(defmethod match-pattern :graph
[ds fuel-tracker solution pattern error-ch]
(let [[g clause] (pattern-data pattern)]
Expand Down Expand Up @@ -566,6 +580,19 @@
clause-ch)
out-ch))

(defmethod match-pattern :values
[db fuel-tracker solution pattern error-ch]
(let [inline-solutions (pattern-data pattern)
;; transform a match into its identity for equality checks
match-identity (juxt get-iri get-value get-datatype-iri (comp get-meta :lang))
solution* (update-vals solution match-identity)]
;; filter out any inline solutions whose matches don't match the solution's matches
(->> inline-solutions
(filterv (fn [inline-solution] (= (select-keys solution* (keys inline-solution))
(update-vals inline-solution match-identity))))
(mapv (partial merge solution) inline-solutions)
(async/to-chan!))))

(defn with-default
"Return a transducer that transforms an input stream of solutions to include the
`default-solution` if and only if the stream was empty."
Expand Down
37 changes: 27 additions & 10 deletions src/clj/fluree/db/query/fql/parse.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@
(json-ld/expand-iri context)
(where/anonymous-value dt-iri))
(where/anonymous-value v dt-iri))
(where/anonymous-value v)))
(if-let [lang (get attrs const/iri-language)]
(where/anonymous-value v const/iri-lang-string {:lang lang})
(where/anonymous-value v))))

(defn parse-value-attributes
[v attrs context]
Expand All @@ -79,8 +81,10 @@
(let [expanded (json-ld/expand-iri val context)]
(where/match-iri var-match expanded))
(where/match-value var-match val dt-iri))
(let [dt (datatype/infer-iri val)]
(where/match-value var-match val dt)))))
(if-let [lang (get attrs const/iri-language)]
(where/match-value var-match val const/iri-lang-string {:lang lang})
(let [dt (datatype/infer-iri val)]
(where/match-value var-match val dt))))))

(defn match-value-binding
[var-match value context]
Expand All @@ -98,11 +102,11 @@
(zipmap vars binding)))

(defn parse-values
[q context]
(when-let [values (:values q)]
[values context]
(when values
(let [[vars vals] values
vars* (keep parse-var-name (util/sequential vars))
vals* (mapv util/sequential vals)
vars* (keep parse-var-name (util/sequential vars))
vals* (mapv util/sequential vals)
var-count (count vars*)]
(if (every? (fn [binding]
(= (count binding) var-count))
Expand Down Expand Up @@ -408,6 +412,19 @@
(let [parsed (parse-bind-map binds)]
[(where/->pattern :bind parsed)]))

(defmethod parse-pattern :values
[[_ values] vars context]
(let [[_vars solutions] (parse-values values context)]
[(where/->pattern :values solutions)]))

(defmethod parse-pattern :exists
[[_ patterns] vars context]
[(where/->pattern :exists (parse-where-clause patterns vars context))])

(defmethod parse-pattern :not-exists
[[_ patterns] vars context]
[(where/->pattern :not-exists (parse-where-clause patterns vars context))])

(defmethod parse-pattern :graph
[[_ graph where] vars context]
(let [graph* (or (parse-variable graph)
Expand Down Expand Up @@ -570,7 +587,7 @@
(defn parse-analytical-query
[q]
(let [context (context/extract q)
[vars values] (parse-values q context)
[vars values] (parse-values (:values q) context)
where (parse-where q vars context)
grouping (parse-grouping q)
ordering (parse-ordering q)]
Expand Down Expand Up @@ -684,8 +701,8 @@

(defn parse-txn
[txn context]
(let [vals-map {:values (util/get-first-value txn const/iri-values)}
[vars values] (parse-values vals-map context)
(let [values (util/get-first-value txn const/iri-values)
[vars values] (parse-values values context)
where-map {:where (util/get-first-value txn const/iri-where)}
where (parse-where where-map vars context)
bound-vars (-> where where/bound-variables (into vars))
Expand Down
Loading

0 comments on commit a74defb

Please sign in to comment.