@@ -59,7 +59,7 @@ import Factory from './factory';
5959import Util from './util' ;
6060
6161const types = Algebra . Types ;
62- type mapAggregateType = Wildcard | Expression | Ordering | PatternBind ;
62+ type MapAggregateType = Wildcard | Expression | Ordering | PatternBind ;
6363// Type TempDelIns = (PatternBgp | GraphQuads)[];
6464
6565export type TransformedNamed < T extends object > = AlterNodeOutput < T , SubTyped < 'term' , 'namedNode' > , RDF . Term > ;
@@ -188,35 +188,8 @@ class QueryTranslator {
188188
189189 // 18.2.1
190190 private inScopeVariables ( thingy : SparqlQuery | TripleNesting | TripleCollection | Path | Term ) : Set < string > {
191- const F = this . astFactory ;
192191 const vars = new Set < string > ( ) ;
193- if ( F . isQuery ( thingy ) || F . isUpdate ( thingy ) ) {
194- if ( F . isQuerySelect ( thingy ) ) {
195- if ( thingy . where && thingy . variables . some ( Util . isWildcard ) ) {
196- findPatternBoundedVars ( thingy . where , vars ) ;
197- } else {
198- for ( const v of thingy . variables ) {
199- findPatternBoundedVars ( v , vars ) ;
200- }
201- }
202- if ( thingy . solutionModifiers . group ) {
203- const grouping = thingy . solutionModifiers . group ;
204- for ( const g of grouping . groupings ) {
205- if ( 'variable' in g ) {
206- findPatternBoundedVars ( g . variable , vars ) ;
207- }
208- }
209- }
210- if ( thingy . values ?. values && thingy . values . values . length > 0 ) {
211- const values = thingy . values . values ;
212- for ( const v of Object . keys ( values [ 0 ] ) ) {
213- vars . add ( v ) ;
214- }
215- }
216- }
217- } else {
218- findPatternBoundedVars ( thingy , vars ) ;
219- }
192+ findPatternBoundedVars ( thingy , vars ) ;
220193 return vars ;
221194
222195 // Const inScope: Record<string, RDF.Variable> = {};
@@ -428,6 +401,10 @@ class QueryTranslator {
428401 if ( F . isTermVariable ( term ) ) {
429402 return < AstToRdfTerm < T > > dataFact . variable ( term . value ) ;
430403 }
404+ if ( F . isTermLiteral ( term ) ) {
405+ const langOrIri = typeof term . langOrIri === 'object' ? this . translateTerm ( term . langOrIri ) : term . langOrIri ;
406+ return < AstToRdfTerm < T > > dataFact . literal ( term . value , langOrIri ) ;
407+ }
431408 throw new Error ( `Unexpected term: ${ JSON . stringify ( term ) } ` ) ;
432409 }
433410
@@ -467,6 +444,9 @@ class QueryTranslator {
467444 } ) ;
468445 }
469446
447+ /**
448+ * When flattening, nested subject triples first, followed by nested object triples, lastly the current tripple.
449+ */
470450 private translateBasicGraphPattern ( triples : BasicGraphPattern , result : FlattenedTriple [ ] ) : void {
471451 const F = this . astFactory ;
472452 for ( const triple of triples ) {
@@ -535,7 +515,10 @@ class QueryTranslator {
535515 /**
536516 * 18.2.2.3 Translate Property Path Expressions
537517 */
538- private translatePathPredicate ( predicate : RDF . NamedNode | PathPure ) : Algebra . PropertyPathSymbol {
518+ private translatePathPredicate ( predicate : RDF . NamedNode | Path ) : Algebra . PropertyPathSymbol {
519+ if ( this . astFactory . isTerm ( predicate ) ) {
520+ return this . translatePathPredicate ( this . translateTerm ( predicate ) ) ;
521+ }
539522 // Iri -> link(iri)
540523 if ( this . isTerm ( predicate ) ) {
541524 return this . factory . createLink ( predicate ) ;
@@ -548,30 +531,30 @@ class QueryTranslator {
548531
549532 if ( predicate . subType === '!' ) {
550533 // Negation is either over a single predicate or a list of disjuncted properties - that can only have modifier '^'
551- const normals : RDF . NamedNode [ ] = [ ] ;
552- const inverted : RDF . NamedNode [ ] = [ ] ;
534+ const normals : TermIri [ ] = [ ] ;
535+ const inverted : TermIri [ ] = [ ] ;
553536 // Either the item of this one is an `|`, `^` or `iri`
554537 const contained = predicate . items [ 0 ] ;
555- let items : ( RDF . NamedNode | PathNegatedElt ) [ ] ;
538+ let items : ( TermIri | PathNegatedElt ) [ ] ;
556539 if ( this . astFactory . isPathPure ( contained ) && contained . subType === '|' ) {
557- items = < ( RDF . NamedNode | PathNegatedElt ) [ ] > contained . items ;
540+ items = < ( TermIri | PathNegatedElt ) [ ] > contained . items ;
558541 } else {
559- items = [ < RDF . NamedNode | PathNegatedElt > contained ] ;
542+ items = [ contained ] ;
560543 }
561544
562545 for ( const item of items ) {
563- if ( this . isTerm ( item ) ) {
546+ if ( this . astFactory . isTerm ( item ) ) {
564547 normals . push ( item ) ;
565548 } else if ( item . subType === '^' ) {
566- inverted . push ( < RDF . NamedNode > < unknown > item . items [ 0 ] ) ;
549+ inverted . push ( item . items [ 0 ] ) ;
567550 } else {
568551 throw new Error ( `Unexpected item: ${ JSON . stringify ( item ) } ` ) ;
569552 }
570553 }
571554
572555 // NPS elements do not have the LINK function
573- const normalElement = this . factory . createNps ( normals ) ;
574- const invertedElement = this . factory . createInv ( this . factory . createNps ( inverted ) ) ;
556+ const normalElement = this . factory . createNps ( normals . map ( x => this . translateTerm ( x ) ) ) ;
557+ const invertedElement = this . factory . createInv ( this . factory . createNps ( inverted . map ( x => this . translateTerm ( x ) ) ) ) ;
575558
576559 // !(:iri1|...|:irin) -> NPS({:iri1 ... :irin})
577560 if ( inverted . length === 0 ) {
@@ -824,21 +807,15 @@ class QueryTranslator {
824807 const bindPatterns : PatternBind [ ] = [ ] ;
825808
826809 const varAggrMap : Record < string , ExpressionAggregate > = { } ;
827- if ( F . isQuerySelect ( query ) ) {
828- for ( const val of query . variables ) {
829- this . mapAggregate ( val , varAggrMap ) ;
830- }
831- }
832- if ( query . solutionModifiers . having ) {
833- for ( const val of query . solutionModifiers . having . having ) {
834- this . mapAggregate ( val , varAggrMap ) ;
835- }
836- }
837- if ( query . solutionModifiers . order ) {
838- for ( const val of query . solutionModifiers . order . orderDefs ) {
839- this . mapAggregate ( val , varAggrMap ) ;
840- }
841- }
810+ const variables = F . isQuerySelect ( query ) || F . isQueryDescribe ( query ) ?
811+ query . variables . map ( x => this . mapAggregate ( x , varAggrMap ) ) :
812+ undefined ;
813+ const having = query . solutionModifiers . having ?
814+ query . solutionModifiers . having . having . map ( x => this . mapAggregate ( x , varAggrMap ) ) :
815+ undefined ;
816+ const order = query . solutionModifiers . order ?
817+ query . solutionModifiers . order . orderDefs . map ( x => this . mapAggregate ( x , varAggrMap ) ) :
818+ undefined ;
842819
843820 // Step: GROUP BY - If we found an aggregate, in group by or implicitly, do Group function.
844821 // 18.2.4.1 Grouping and Aggregation
@@ -852,12 +829,17 @@ class QueryTranslator {
852829 if ( F . isTerm ( expression ) ) {
853830 // This will always be a var, otherwise sparql would be invalid
854831 vars . push ( this . translateTerm ( < TermVariable > expression ) ) ;
855- } else if ( 'variable' in expression ) {
856- const var_ = this . translateTerm ( expression . variable ) ;
857- res = this . factory . createExtend ( res , var_ , this . translateExpression ( expression . value ) ) ;
858832 } else {
859- const var_ = this . generateFreshVar ( ) ;
860- res = this . factory . createExtend ( res , var_ , this . translateExpression ( expression ) ) ;
833+ let var_ : RDF . Variable ;
834+ let expr : Expression ;
835+ if ( 'variable' in expression ) {
836+ var_ = this . translateTerm ( expression . variable ) ;
837+ expr = expression . value ;
838+ } else {
839+ var_ = this . generateFreshVar ( ) ;
840+ expr = expression ;
841+ }
842+ res = this . factory . createExtend ( res , var_ , this . translateExpression ( expr ) ) ;
861843 vars . push ( var_ ) ;
862844 }
863845 }
@@ -866,8 +848,8 @@ class QueryTranslator {
866848 }
867849
868850 // 18.2.4.2
869- if ( query . solutionModifiers . having ) {
870- for ( const filter of query . solutionModifiers . having . having ) {
851+ if ( having ) {
852+ for ( const filter of having ) {
871853 res = this . factory . createFilter ( res , this . translateExpression ( filter ) ) ;
872854 }
873855 }
@@ -880,14 +862,14 @@ class QueryTranslator {
880862 // 18.2.4.4
881863 let PatternValues : ( RDF . Variable | RDF . NamedNode ) [ ] = [ ] ;
882864
883- if ( F . isQuerySelect ( query ) || F . isQueryDescribe ( query ) ) {
865+ if ( variables ) {
884866 // Sort variables for consistent output
885- if ( query . variables . some ( wild => F . isWildcard ( wild ) ) ) {
886- PatternValues = Object . values ( this . inScopeVariables ( query ) )
867+ if ( variables . some ( wild => F . isWildcard ( wild ) ) ) {
868+ PatternValues = [ ... this . inScopeVariables ( query ) . values ( ) ] . map ( x => this . dataFactory . variable ( x ) )
887869 . sort ( ( left , right ) => left . value . localeCompare ( right . value ) ) ;
888870 } else {
889871 // Wildcard has been filtered out above
890- for ( const var_ of < Exclude < typeof query . variables , [ Wildcard ] > > query . variables ) {
872+ for ( const var_ of < ( TermVariable | TermIri | PatternBind ) [ ] > variables ) {
891873 // Can have non-variables with DESCRIBE
892874 if ( F . isTerm ( var_ ) ) {
893875 PatternValues . push ( this . translateTerm ( var_ ) ) ;
@@ -913,8 +895,8 @@ class QueryTranslator {
913895 // not using toList and toMultiset
914896
915897 // 18.2.5.1
916- if ( query . solutionModifiers . order ) {
917- res = this . factory . createOrderBy ( res , query . solutionModifiers . order . orderDefs . map ( ( expr ) => {
898+ if ( order ) {
899+ res = this . factory . createOrderBy ( res , order . map ( ( expr ) => {
918900 let result = this . translateExpression ( expr . expression ) ;
919901 if ( expr . descending ) {
920902 result = this . factory . createOperatorExpression ( 'desc' , [ result ] ) ;
@@ -965,37 +947,42 @@ class QueryTranslator {
965947 return res ;
966948 }
967949
968- // Rewrites some of the input sparql object to make use of aggregate variables
969- private mapAggregate ( thingy : mapAggregateType , aggregates : Record < string , ExpressionAggregate > ) : void {
950+ /**
951+ * Rewrites some of the input sparql object to make use of aggregate variables
952+ * It thus replaces aggregates by their representative variable and registers the mapping.
953+ *
954+ * DEBUG NOTE: The type is tricky since it does replace aggregates by variables, but it works for the most part
955+ */
956+ private mapAggregate < T extends MapAggregateType > ( thingy : T , aggregates : Record < string , ExpressionAggregate > ) : T {
970957 const F = this . astFactory ;
971958
972959 if ( F . isExpressionAggregate ( thingy ) ) {
973- let val : RDF . Variable | undefined ;
960+ // Needed to take away the difference in the various `loc` descriptions
961+ const canonicalAggregate = this . astFactory . forcedAutoGenTree < ExpressionAggregate > ( thingy ) ;
962+ let val : TermVariable | undefined ;
963+ // Look for the matching aggregate
974964 for ( const [ key , aggregate ] of Object . entries ( aggregates ) ) {
975- if ( equal ( aggregate , thingy ) ) {
976- val = this . dataFactory . variable ( key ) ;
965+ if ( equal ( aggregate , canonicalAggregate ) ) {
966+ val = F . variable ( key , F . sourceLocation ( ) ) ;
977967 break ;
978968 }
979969 }
980970 if ( val !== undefined ) {
981- return ;
971+ return < T > val ;
982972 }
983973 const freshVar = this . generateFreshVar ( ) ;
984- aggregates [ freshVar . value ] = thingy ;
985- return ;
974+ aggregates [ freshVar . value ] = canonicalAggregate ;
975+ return < T > F . variable ( freshVar . value , F . sourceLocation ( ) ) ;
986976 }
987977
988978 if ( F . isExpressionPure ( thingy ) && ! F . isExpressionPatternOperation ( thingy ) ) {
989- for ( const expr of thingy . args ) {
990- this . mapAggregate ( expr , aggregates ) ;
991- }
992- return ;
979+ return { ...thingy , args : thingy . args . map ( x => this . mapAggregate ( x , aggregates ) ) } ;
993980 }
994-
995981 // Non-aggregate expression
996982 if ( 'expression' in thingy && thingy . expression ) {
997- this . mapAggregate ( thingy . expression , aggregates ) ;
983+ return { ... thingy , expression : this . mapAggregate ( thingy . expression , aggregates ) } ;
998984 }
985+ return thingy ;
999986 }
1000987
1001988 private translateBoundAggregate ( thingy : ExpressionAggregate , variable : RDF . Variable ) : Algebra . BoundAggregate {
@@ -1008,6 +995,9 @@ class QueryTranslator {
1008995 this . registerContextDefinitions ( update . context ) ;
1009996 return update . operation ? [ this . translateSingleUpdate ( update . operation ) ] : [ ] ;
1010997 } ) ;
998+ if ( updates . length === 0 ) {
999+ return this . factory . createNop ( ) ;
1000+ }
10111001 if ( updates . length === 1 ) {
10121002 return updates [ 0 ] ;
10131003 }
@@ -1076,8 +1066,10 @@ class QueryTranslator {
10761066 } else if ( F . isUpdateOperationInsertData ( op ) ) {
10771067 insertTriples . push ( ...op . data . flatMap ( quad => this . translateUpdateTriplesBlock ( quad ) ) ) ;
10781068 } else {
1079- deleteTriples . push ( ...op . delete . flatMap ( quad => this . translateUpdateTriplesBlock ( quad ) ) ) ;
1080- insertTriples . push ( ...op . insert . flatMap ( quad => this . translateUpdateTriplesBlock ( quad ) ) ) ;
1069+ deleteTriples . push ( ...op . delete . flatMap ( quad =>
1070+ this . translateUpdateTriplesBlock ( quad , op . graph ? this . translateTerm ( op . graph ) : op . graph ) ) ) ;
1071+ insertTriples . push ( ...op . insert . flatMap ( quad =>
1072+ this . translateUpdateTriplesBlock ( quad , op . graph ? this . translateTerm ( op . graph ) : op . graph ) ) ) ;
10811073 if ( op . where . length > 0 ) {
10821074 where = this . translateGraphPattern ( F . patternGroup ( op . where , F . sourceLocation ( ...op . where ) ) ) ;
10831075 const use : { default : RDF . NamedNode [ ] ; named : RDF . NamedNode [ ] } = this . translateDatasetClause ( op . from ) ;
@@ -1105,20 +1097,21 @@ class QueryTranslator {
11051097 }
11061098
11071099 // UPDATE parsing will always return quads and have no GRAPH elements
1108- private translateUpdateTriplesBlock ( thingy : PatternBgp | GraphQuads ) : Algebra . Pattern [ ] {
1100+ private translateUpdateTriplesBlock ( thingy : PatternBgp | GraphQuads , graph : RDF . NamedNode | undefined = undefined ) :
1101+ Algebra . Pattern [ ] {
11091102 const F = this . astFactory ;
1110- let graph : TermIri | TermVariable | undefined ;
1103+ let currentGraph : RDF . NamedNode | RDF . Variable | undefined = graph ;
11111104 let patternBgp : PatternBgp ;
11121105 if ( F . isGraphQuads ( thingy ) ) {
1113- graph = thingy . graph ;
1106+ currentGraph = this . translateTerm ( thingy . graph ) ;
11141107 patternBgp = thingy . triples ;
11151108 } else {
11161109 patternBgp = thingy ;
11171110 }
11181111 let triples : FlattenedTriple [ ] = [ ] ;
11191112 this . translateBasicGraphPattern ( patternBgp . triples , triples ) ;
1120- if ( graph ) {
1121- triples = triples . map ( triple => Object . assign ( triple , { graph } ) ) ;
1113+ if ( currentGraph ) {
1114+ triples = triples . map ( triple => Object . assign ( triple , { graph : currentGraph } ) ) ;
11221115 }
11231116 return triples . map ( triple => this . translateQuad ( triple ) ) ;
11241117 }
0 commit comments