sparql 1.1.6 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -6
- data/VERSION +1 -1
- data/lib/sparql/algebra/expression.rb +28 -2
- data/lib/sparql/algebra/extensions.rb +73 -0
- data/lib/sparql/algebra/operator.rb +73 -9
- data/lib/sparql/algebra/operator/alt.rb +58 -0
- data/lib/sparql/algebra/operator/extend.rb +15 -3
- data/lib/sparql/algebra/operator/filter.rb +18 -1
- data/lib/sparql/algebra/operator/group.rb +22 -1
- data/lib/sparql/algebra/operator/join.rb +16 -5
- data/lib/sparql/algebra/operator/left_join.rb +12 -1
- data/lib/sparql/algebra/operator/notin.rb +1 -1
- data/lib/sparql/algebra/operator/notoneof.rb +52 -0
- data/lib/sparql/algebra/operator/path.rb +49 -0
- data/lib/sparql/algebra/operator/path_opt.rb +112 -0
- data/lib/sparql/algebra/operator/path_plus.rb +99 -0
- data/lib/sparql/algebra/operator/path_star.rb +42 -0
- data/lib/sparql/algebra/operator/reverse.rb +54 -0
- data/lib/sparql/algebra/operator/seq.rb +73 -0
- data/lib/sparql/algebra/operator/sequence.rb +63 -0
- data/lib/sparql/algebra/operator/union.rb +12 -1
- data/lib/sparql/algebra/query.rb +1 -0
- data/lib/sparql/grammar/meta.rb +4593 -4582
- data/lib/sparql/grammar/parser11.rb +301 -107
- metadata +13 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: f8e188130c333370c626f037f5c2312a222b5f3d
         | 
| 4 | 
            +
              data.tar.gz: 61c0398d56e8e2891dc97d4a8843411c5912e0d6
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 9398d997c21707cfaeef51fb8fd5dd11008e0d23bcc81ee8f9027d98ee37297b19735b4c4a6202cbf241c7faf811510af0dbf588742bcaf2389cfdda1488027a
         | 
| 7 | 
            +
              data.tar.gz: 03a7de12fa45056026eff0a2d93bfc92d375173c6afabfc462e223f10f5ea75f4ef943d22a9621aa84836b07703be4d4ee2206b917206edba70648af30f79723
         | 
    
        data/README.md
    CHANGED
    
    | @@ -9,7 +9,7 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][]. | |
| 9 9 | 
             
            ## Features
         | 
| 10 10 |  | 
| 11 11 | 
             
            * 100% free and unencumbered [public domain](http://unlicense.org/) software.
         | 
| 12 | 
            -
            * [SPARQL 1.1 Query][] parsing and execution | 
| 12 | 
            +
            * Complete [SPARQL 1.1 Query][] parsing and execution
         | 
| 13 13 | 
             
            * SPARQL results as [XML][SPARQL XML], [JSON][SPARQL JSON],
         | 
| 14 14 | 
             
              [CSV][SPARQL 1.1 Query Results CSV and TSV Formats],
         | 
| 15 15 | 
             
              [TSV][SPARQL 1.1 Query Results CSV and TSV Formats]
         | 
| @@ -21,6 +21,7 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][]. | |
| 21 21 | 
             
            * [Rack][] and [Sinatra][] middleware to perform [HTTP content negotiation][conneg] for result formats
         | 
| 22 22 | 
             
              * Compatible with any [Rack][] or [Sinatra][] application and any Rack-based framework.
         | 
| 23 23 | 
             
              * Helper method for describing [SPARQL Service Description][SSD]
         | 
| 24 | 
            +
            * Implementation Report: {file:etc/earl.html EARL}
         | 
| 24 25 | 
             
            * Compatible with Ruby >= 1.9.3.
         | 
| 25 26 | 
             
            * Compatible with older Ruby versions with the help of the [Backports][] gem.
         | 
| 26 27 | 
             
            * Supports Unicode query strings both on all versions of Ruby.
         | 
| @@ -30,8 +31,6 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][]. | |
| 30 31 | 
             
            The {SPARQL} gem implements [SPARQL 1.1 Query][], and [SPARQL 1.1 Update][], and provides [Rack][] and [Sinatra][] middleware to provide results using [HTTP Content Negotiation][conneg].
         | 
| 31 32 |  | 
| 32 33 | 
             
            * {SPARQL::Grammar} implements a [SPARQL 1.1 Query][] and [SPARQL 1.1 Update][] parser generating [SPARQL S-Expressions (SSE)][SSE].
         | 
| 33 | 
            -
              * Support for [Property Paths][] is excluded.
         | 
| 34 | 
            -
                See the section on [SPARQL 1.1 Query][] extensions and limitations for further detail.
         | 
| 35 34 | 
             
            * {SPARQL::Algebra} executes SSE against Any `RDF::Graph` or `RDF::Repository`, including compliant [RDF.rb][] repository adaptors such as [RDF::DO][] and [RDF::Mongo][].
         | 
| 36 35 | 
             
            * {Rack::SPARQL} and {Sinatra::SPARQL} provide middleware components to format results using an appropriate format based on [HTTP content negotiation][conneg].
         | 
| 37 36 |  | 
| @@ -53,13 +52,13 @@ The SPARQL gem now implements the following [SPARQL 1.1 Query][] operations: | |
| 53 52 | 
             
            * [Inline Data](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#inline-data)
         | 
| 54 53 | 
             
            * [Exists](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#func-filter-exists)
         | 
| 55 54 | 
             
            * [Negation](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#negation)
         | 
| 55 | 
            +
            * [Property Paths](http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#propertypaths)
         | 
| 56 56 |  | 
| 57 57 | 
             
            The gem also includes the following [SPARQL 1.1 Update][] operations:
         | 
| 58 58 | 
             
            * [Graph Update](http://www.w3.org/TR/sparql11-update/#graphUpdate)
         | 
| 59 59 | 
             
            * [Graph Management](http://www.w3.org/TR/sparql11-update/#graphManagement)
         | 
| 60 60 |  | 
| 61 | 
            -
             | 
| 62 | 
            -
            [Property Paths][], which will be in later release along with:
         | 
| 61 | 
            +
            Not supported:
         | 
| 63 62 |  | 
| 64 63 | 
             
            * [Federated Query][SPARQL 1.1 Federated Query],
         | 
| 65 64 | 
             
            * [Entailment Regimes][SPARQL 1.1 Entailment Regimes],
         | 
| @@ -337,7 +336,6 @@ A copy of the [SPARQL 1.0 tests][] and [SPARQL 1.1 tests][] are also included in | |
| 337 336 | 
             
            [SPARQL XML]:       http://www.w3.org/TR/rdf-sparql-XMLres/
         | 
| 338 337 | 
             
            [SPARQL JSON]:      http://www.w3.org/TR/rdf-sparql-json-res/
         | 
| 339 338 | 
             
            [SPARQL EBNF]:      http://www.w3.org/TR/sparql11-query/#sparqlGrammar
         | 
| 340 | 
            -
            [Property Paths]:   http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#propertypaths
         | 
| 341 339 |  | 
| 342 340 | 
             
            [SSD]:              http://www.w3.org/TR/sparql11-service-description/
         | 
| 343 341 | 
             
            [Rack]:             http://rack.rubyforge.org/
         | 
    
        data/VERSION
    CHANGED
    
    | @@ -1 +1 @@ | |
| 1 | 
            -
            1.1. | 
| 1 | 
            +
            1.1.7
         | 
| @@ -120,7 +120,7 @@ module SPARQL; module Algebra | |
| 120 120 | 
             
                  end
         | 
| 121 121 |  | 
| 122 122 | 
             
                  debug(options) {"#{operator.inspect}(#{operands.map(&:inspect).join(',')})"}
         | 
| 123 | 
            -
                  options.delete_if {|k, v| [:debug, :depth, :prefixes, :base_uri, :update].include?(k) }
         | 
| 123 | 
            +
                  options.delete_if {|k, v| [:debug, :depth, :prefixes, :base_uri, :update, :validate].include?(k) }
         | 
| 124 124 | 
             
                  operands << options unless options.empty?
         | 
| 125 125 | 
             
                  operator.new(*operands)
         | 
| 126 126 | 
             
                end
         | 
| @@ -317,7 +317,33 @@ module SPARQL; module Algebra | |
| 317 317 | 
             
                def to_sxp_bin
         | 
| 318 318 | 
             
                  self
         | 
| 319 319 | 
             
                end
         | 
| 320 | 
            -
             | 
| 320 | 
            +
             | 
| 321 | 
            +
                ##
         | 
| 322 | 
            +
                # Is this value valid, and composed only of valid components?
         | 
| 323 | 
            +
                #
         | 
| 324 | 
            +
                # @return [Boolean] `true` or `false`
         | 
| 325 | 
            +
                def valid?
         | 
| 326 | 
            +
                  true
         | 
| 327 | 
            +
                end
         | 
| 328 | 
            +
             | 
| 329 | 
            +
                ##
         | 
| 330 | 
            +
                # Is this value invalid, or is it composed of any invalid components?
         | 
| 331 | 
            +
                #
         | 
| 332 | 
            +
                # @return [Boolean] `true` or `false`
         | 
| 333 | 
            +
                def invalid?
         | 
| 334 | 
            +
                  !valid?
         | 
| 335 | 
            +
                end
         | 
| 336 | 
            +
             | 
| 337 | 
            +
                ##
         | 
| 338 | 
            +
                # Default validate! implementation, overridden in concrete classes
         | 
| 339 | 
            +
                # @return [SPARQL::Algebra::Expression] `self`
         | 
| 340 | 
            +
                # @raise  [ArgumentError] if the value is invalid
         | 
| 341 | 
            +
                def validate!
         | 
| 342 | 
            +
                  raise ArgumentError if invalid?
         | 
| 343 | 
            +
                  self
         | 
| 344 | 
            +
                end
         | 
| 345 | 
            +
                alias_method :validate, :validate!
         | 
| 346 | 
            +
             | 
| 321 347 | 
             
                private
         | 
| 322 348 | 
             
                # @overload: May be called with node, message and an option hash
         | 
| 323 349 | 
             
                #   @param [String] node processing node
         | 
| @@ -135,6 +135,37 @@ class Array | |
| 135 135 | 
             
                end
         | 
| 136 136 | 
             
                self
         | 
| 137 137 | 
             
              end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
              ##
         | 
| 140 | 
            +
              # Return the non-destinguished variables contained within this Array
         | 
| 141 | 
            +
              # @return [Array<RDF::Query::Variable>]
         | 
| 142 | 
            +
              def ndvars
         | 
| 143 | 
            +
                vars.reject(&:distinguished?)
         | 
| 144 | 
            +
              end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
              ##
         | 
| 147 | 
            +
              # Return the variables contained within this Array
         | 
| 148 | 
            +
              # @return [Array<RDF::Query::Variable>]
         | 
| 149 | 
            +
              def vars
         | 
| 150 | 
            +
                select {|o| o.respond_to?(:vars)}.map(&:vars).flatten.compact
         | 
| 151 | 
            +
              end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
              ##
         | 
| 154 | 
            +
              # Is this value composed only of valid components?
         | 
| 155 | 
            +
              #
         | 
| 156 | 
            +
              # @return [Boolean] `true` or `false`
         | 
| 157 | 
            +
              def valid?
         | 
| 158 | 
            +
                all? {|e| e.respond_to?(:valid?) ? e.valid? : true}
         | 
| 159 | 
            +
              end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
              ##
         | 
| 162 | 
            +
              # Validate all components.
         | 
| 163 | 
            +
              # @return [Array] `self`
         | 
| 164 | 
            +
              # @raise  [ArgumentError] if the value is invalid
         | 
| 165 | 
            +
              def validate!
         | 
| 166 | 
            +
                each {|e| e.validate! if e.respond_to?(:validate!)}
         | 
| 167 | 
            +
                self
         | 
| 168 | 
            +
              end
         | 
| 138 169 | 
             
            end
         | 
| 139 170 |  | 
| 140 171 | 
             
            ##
         | 
| @@ -199,6 +230,20 @@ module RDF::Term | |
| 199 230 | 
             
                  (language == other.language || dtr == RDF::XSD.string) :
         | 
| 200 231 | 
             
                  dtr == RDF::XSD.string
         | 
| 201 232 | 
             
              end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
              ##
         | 
| 235 | 
            +
              # Return the non-destinguished variables contained within this operator
         | 
| 236 | 
            +
              # @return [Array<RDF::Query::Variable>]
         | 
| 237 | 
            +
              def ndvars
         | 
| 238 | 
            +
                vars.reject(&:distinguished?)
         | 
| 239 | 
            +
              end
         | 
| 240 | 
            +
             | 
| 241 | 
            +
              ##
         | 
| 242 | 
            +
              # Return the variables contained within this operator
         | 
| 243 | 
            +
              # @return [Array<RDF::Query::Variable>]
         | 
| 244 | 
            +
              def vars
         | 
| 245 | 
            +
                variable? ? [self] : []
         | 
| 246 | 
            +
              end
         | 
| 202 247 | 
             
            end # RDF::Term
         | 
| 203 248 |  | 
| 204 249 | 
             
            # Override RDF::Queryable to execute against SPARQL::Algebra::Query elements as well as RDF::Query and RDF::Pattern
         | 
| @@ -311,6 +356,20 @@ class RDF::Query | |
| 311 356 | 
             
              def query_yields_solutions?
         | 
| 312 357 | 
             
                true
         | 
| 313 358 | 
             
              end
         | 
| 359 | 
            +
             | 
| 360 | 
            +
              ##
         | 
| 361 | 
            +
              # Return the non-destinguished variables contained within patterns
         | 
| 362 | 
            +
              # @return [Array<RDF::Query::Variable>]
         | 
| 363 | 
            +
              def ndvars
         | 
| 364 | 
            +
                patterns.map(&:ndvars).flatten
         | 
| 365 | 
            +
              end
         | 
| 366 | 
            +
             | 
| 367 | 
            +
              ##
         | 
| 368 | 
            +
              # Return the variables contained within patterns
         | 
| 369 | 
            +
              # @return [Array<RDF::Query::Variable>]
         | 
| 370 | 
            +
              def vars
         | 
| 371 | 
            +
                patterns.map(&:vars).flatten
         | 
| 372 | 
            +
              end
         | 
| 314 373 | 
             
            end
         | 
| 315 374 |  | 
| 316 375 | 
             
            class RDF::Query::Pattern
         | 
| @@ -323,6 +382,20 @@ class RDF::Query::Pattern | |
| 323 382 | 
             
                  [:triple, subject, predicate, object]
         | 
| 324 383 | 
             
                end
         | 
| 325 384 | 
             
              end
         | 
| 385 | 
            +
             | 
| 386 | 
            +
              ##
         | 
| 387 | 
            +
              # Return the non-destinguished variables contained within this pattern
         | 
| 388 | 
            +
              # @return [Array<RDF::Query::Variable>]
         | 
| 389 | 
            +
              def ndvars
         | 
| 390 | 
            +
                vars.reject(&:distinguished?)
         | 
| 391 | 
            +
              end
         | 
| 392 | 
            +
             | 
| 393 | 
            +
              ##
         | 
| 394 | 
            +
              # Return the variables contained within this pattern
         | 
| 395 | 
            +
              # @return [Array<RDF::Query::Variable>]
         | 
| 396 | 
            +
              def vars
         | 
| 397 | 
            +
                variables.values
         | 
| 398 | 
            +
              end
         | 
| 326 399 | 
             
            end
         | 
| 327 400 |  | 
| 328 401 | 
             
            ##
         | 
| @@ -85,6 +85,17 @@ module SPARQL; module Algebra | |
| 85 85 | 
             
                autoload :Subtract,           'sparql/algebra/operator/subtract'
         | 
| 86 86 | 
             
                autoload :UCase,              'sparql/algebra/operator/ucase'
         | 
| 87 87 |  | 
| 88 | 
            +
                # Property Paths
         | 
| 89 | 
            +
                autoload :Alt,                'sparql/algebra/operator/alt'
         | 
| 90 | 
            +
                autoload :NotOneOf,           'sparql/algebra/operator/notoneof'
         | 
| 91 | 
            +
                autoload :PathOpt,            'sparql/algebra/operator/path_opt'
         | 
| 92 | 
            +
                autoload :PathPlus,           'sparql/algebra/operator/path_plus'
         | 
| 93 | 
            +
                autoload :PathStar,           'sparql/algebra/operator/path_star'
         | 
| 94 | 
            +
                autoload :Path,               'sparql/algebra/operator/path'
         | 
| 95 | 
            +
                autoload :Reverse,            'sparql/algebra/operator/reverse'
         | 
| 96 | 
            +
                autoload :Seq,                'sparql/algebra/operator/seq'
         | 
| 97 | 
            +
                autoload :Sequence,           'sparql/algebra/operator/sequence'
         | 
| 98 | 
            +
             | 
| 88 99 | 
             
                # Miscellaneous
         | 
| 89 100 | 
             
                autoload :Asc,                'sparql/algebra/operator/asc'
         | 
| 90 101 | 
             
                autoload :Coalesce,           'sparql/algebra/operator/coalesce'
         | 
| @@ -159,6 +170,7 @@ module SPARQL; module Algebra | |
| 159 170 | 
             
                    when :>=              then GreaterThanOrEqual
         | 
| 160 171 | 
             
                    when :abs             then Abs
         | 
| 161 172 | 
             
                    when :add             then Add
         | 
| 173 | 
            +
                    when :alt             then Alt
         | 
| 162 174 | 
             
                    when :and, :'&&'      then And
         | 
| 163 175 | 
             
                    when :avg             then Avg
         | 
| 164 176 | 
             
                    when :bnode           then BNode
         | 
| @@ -195,18 +207,26 @@ module SPARQL; module Algebra | |
| 195 207 | 
             
                    when :month           then Month
         | 
| 196 208 | 
             
                    when :multiply        then Multiply
         | 
| 197 209 | 
             
                    when :not, :'!'       then Not
         | 
| 198 | 
            -
                    when :notexists | 
| 210 | 
            +
                    when :notexists       then NotExists
         | 
| 199 211 | 
             
                    when :notin           then NotIn
         | 
| 212 | 
            +
                    when :notoneof        then NotOneOf
         | 
| 200 213 | 
             
                    when :now             then Now
         | 
| 201 214 | 
             
                    when :or, :'||'       then Or
         | 
| 215 | 
            +
                    when :path            then Path
         | 
| 216 | 
            +
                    when :path?           then PathOpt
         | 
| 217 | 
            +
                    when :"path*"         then PathStar
         | 
| 218 | 
            +
                    when :"path+"         then PathPlus
         | 
| 202 219 | 
             
                    when :plus            then Plus
         | 
| 203 220 | 
             
                    when :rand            then Rand
         | 
| 204 221 | 
             
                    when :regex           then Regex
         | 
| 205 222 | 
             
                    when :replace         then Replace
         | 
| 223 | 
            +
                    when :reverse         then Reverse
         | 
| 206 224 | 
             
                    when :round           then Round
         | 
| 207 225 | 
             
                    when :sameterm        then SameTerm
         | 
| 208 226 | 
             
                    when :sample          then Sample
         | 
| 209 227 | 
             
                    when :seconds         then Seconds
         | 
| 228 | 
            +
                    when :seq             then Seq
         | 
| 229 | 
            +
                    when :sequence        then Sequence
         | 
| 210 230 | 
             
                    when :sha1            then SHA1
         | 
| 211 231 | 
             
                    when :sha256          then SHA256
         | 
| 212 232 | 
             
                    when :sha512          then SHA512
         | 
| @@ -322,7 +342,11 @@ module SPARQL; module Algebra | |
| 322 342 | 
             
                  @options  = operands.last.is_a?(Hash) ? operands.pop.dup : {}
         | 
| 323 343 | 
             
                  @operands = operands.map! do |operand|
         | 
| 324 344 | 
             
                    case operand
         | 
| 345 | 
            +
                      when Array
         | 
| 346 | 
            +
                        operand.each {|op| op.parent = self if operand.respond_to?(:parent=)}
         | 
| 347 | 
            +
                        operand
         | 
| 325 348 | 
             
                      when Operator, Variable, RDF::Term, RDF::Query, RDF::Query::Pattern, Array, Symbol
         | 
| 349 | 
            +
                        operand.parent = self if operand.respond_to?(:parent=)
         | 
| 326 350 | 
             
                        operand
         | 
| 327 351 | 
             
                      when TrueClass, FalseClass, Numeric, String, DateTime, Date, Time
         | 
| 328 352 | 
             
                        RDF::Literal(operand)
         | 
| @@ -387,12 +411,6 @@ module SPARQL; module Algebra | |
| 387 411 | 
             
                  @prefixes = hash
         | 
| 388 412 | 
             
                end
         | 
| 389 413 |  | 
| 390 | 
            -
                ##
         | 
| 391 | 
            -
                # Any additional options for this operator.
         | 
| 392 | 
            -
                #
         | 
| 393 | 
            -
                # @return [Hash]
         | 
| 394 | 
            -
                attr_reader :options
         | 
| 395 | 
            -
             | 
| 396 414 | 
             
                ##
         | 
| 397 415 | 
             
                # The operands to this operator.
         | 
| 398 416 | 
             
                #
         | 
| @@ -540,7 +558,7 @@ module SPARQL; module Algebra | |
| 540 558 | 
             
                #
         | 
| 541 559 | 
             
                # @return [String]
         | 
| 542 560 | 
             
                def inspect
         | 
| 543 | 
            -
                  sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, operands. | 
| 561 | 
            +
                  sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, operands.to_sse.gsub(/\s+/m, ' '))
         | 
| 544 562 | 
             
                end
         | 
| 545 563 |  | 
| 546 564 | 
             
                ##
         | 
| @@ -552,7 +570,21 @@ module SPARQL; module Algebra | |
| 552 570 | 
             
                alias_method :==, :eql?
         | 
| 553 571 |  | 
| 554 572 | 
             
                ##
         | 
| 555 | 
            -
                #  | 
| 573 | 
            +
                # Return the non-destinguished variables contained within this operator
         | 
| 574 | 
            +
                # @return [Array<RDF::Query::Variable>]
         | 
| 575 | 
            +
                def ndvars
         | 
| 576 | 
            +
                  vars.reject(&:distinguished?)
         | 
| 577 | 
            +
                end
         | 
| 578 | 
            +
             | 
| 579 | 
            +
                ##
         | 
| 580 | 
            +
                # Return the variables contained within this operator
         | 
| 581 | 
            +
                # @return [Array<RDF::Query::Variable>]
         | 
| 582 | 
            +
                def vars
         | 
| 583 | 
            +
                  operands.select {|o| o.respond_to?(:vars)}.map(&:vars).flatten
         | 
| 584 | 
            +
                end
         | 
| 585 | 
            +
             | 
| 586 | 
            +
                ##
         | 
| 587 | 
            +
                # Iterate via depth-first recursive descent over operands, yielding each operator
         | 
| 556 588 | 
             
                # @yield operator
         | 
| 557 589 | 
             
                # @yieldparam [Object] operator
         | 
| 558 590 | 
             
                def descendants(&block)
         | 
| @@ -569,6 +601,38 @@ module SPARQL; module Algebra | |
| 569 601 | 
             
                    block.call(operand)
         | 
| 570 602 | 
             
                  end
         | 
| 571 603 | 
             
                end
         | 
| 604 | 
            +
             | 
| 605 | 
            +
                ##
         | 
| 606 | 
            +
                # Parent expression, if any
         | 
| 607 | 
            +
                #
         | 
| 608 | 
            +
                # @return [Operator]
         | 
| 609 | 
            +
                def parent; @options[:parent]; end
         | 
| 610 | 
            +
             | 
| 611 | 
            +
                ##
         | 
| 612 | 
            +
                # Parent operator, if any
         | 
| 613 | 
            +
                #
         | 
| 614 | 
            +
                # @return [Operator]
         | 
| 615 | 
            +
                def parent=(operator)
         | 
| 616 | 
            +
                  @options[:parent]= operator
         | 
| 617 | 
            +
                end
         | 
| 618 | 
            +
             | 
| 619 | 
            +
                ##
         | 
| 620 | 
            +
                # First ancestor operator of type `klass`
         | 
| 621 | 
            +
                #
         | 
| 622 | 
            +
                # @param [Class] klass
         | 
| 623 | 
            +
                # @return [Operator]
         | 
| 624 | 
            +
                def first_ancestor(klass)
         | 
| 625 | 
            +
                  parent.is_a?(klass) ? parent : parent.first_ancestor(klass) if parent
         | 
| 626 | 
            +
                end
         | 
| 627 | 
            +
             | 
| 628 | 
            +
                ##
         | 
| 629 | 
            +
                # Validate all operands, operator specific classes should override for operator-specific validation
         | 
| 630 | 
            +
                # @return [SPARQL::Algebra::Expression] `self`
         | 
| 631 | 
            +
                # @raise  [ArgumentError] if the value is invalid
         | 
| 632 | 
            +
                def validate!
         | 
| 633 | 
            +
                  operands.each {|op| op.validate! if op.respond_to?(:validate!)}
         | 
| 634 | 
            +
                  self
         | 
| 635 | 
            +
                end
         | 
| 572 636 | 
             
              protected
         | 
| 573 637 |  | 
| 574 638 | 
             
                ##
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            module SPARQL; module Algebra
         | 
| 2 | 
            +
              class Operator
         | 
| 3 | 
            +
                ##
         | 
| 4 | 
            +
                # The SPARQL Property Path `alt` (Alternative Property Path) operator.
         | 
| 5 | 
            +
                #
         | 
| 6 | 
            +
                # @example
         | 
| 7 | 
            +
                #   (alt a b)
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # @see http://www.w3.org/TR/sparql11-query/#defn_evalPP_alternative
         | 
| 10 | 
            +
                class Alt < Operator::Binary
         | 
| 11 | 
            +
                  include Query
         | 
| 12 | 
            +
                  
         | 
| 13 | 
            +
                  NAME = :alt
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  ##
         | 
| 16 | 
            +
                  # Equivalent to:
         | 
| 17 | 
            +
                  #
         | 
| 18 | 
            +
                  #    (path x (alt :p :q) y)
         | 
| 19 | 
            +
                  #     => (union (bgp (x :p y)) (bgp (x :q y)))
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  # @param  [RDF::Queryable] queryable
         | 
| 22 | 
            +
                  #   the graph or repository to query
         | 
| 23 | 
            +
                  # @param  [Hash{Symbol => Object}] options
         | 
| 24 | 
            +
                  #   any additional keyword options
         | 
| 25 | 
            +
                  # @option options [RDF::Term, RDF::Variable] :subject
         | 
| 26 | 
            +
                  # @option options [RDF::Term, RDF::Variable] :object
         | 
| 27 | 
            +
                  # @yield  [solution]
         | 
| 28 | 
            +
                  #   each matching solution
         | 
| 29 | 
            +
                  # @yieldparam  [RDF::Query::Solution] solution
         | 
| 30 | 
            +
                  # @yieldreturn [void] ignored
         | 
| 31 | 
            +
                  # @see    http://www.w3.org/TR/rdf-sparql-query/#sparqlAlgebra
         | 
| 32 | 
            +
                  def execute(queryable, options = {}, &block)
         | 
| 33 | 
            +
                    subject, object = options[:subject], options[:object]
         | 
| 34 | 
            +
                    debug(options) {"Alt #{[subject, operands, object].to_sse}"}
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    # Solutions where predicate exists
         | 
| 37 | 
            +
                    qa = if operand(0).is_a?(RDF::Term)
         | 
| 38 | 
            +
                      RDF::Query.new do |q|
         | 
| 39 | 
            +
                        q.pattern [subject, operand(0), object]
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
                    else
         | 
| 42 | 
            +
                      operand(0)
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    qb = if operand(1).is_a?(RDF::Term)
         | 
| 46 | 
            +
                      RDF::Query.new do |q|
         | 
| 47 | 
            +
                        q.pattern [subject, operand(1), object]
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                    else
         | 
| 50 | 
            +
                      operand(1)
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    query = Union.new(qa, qb)
         | 
| 54 | 
            +
                    queryable.query(query, options.merge(depth: options[:depth].to_i + 1), &block)
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
                end # Alt
         | 
| 57 | 
            +
              end # Operator
         | 
| 58 | 
            +
            end; end # SPARQL::Algebra
         | 
| @@ -41,10 +41,10 @@ module SPARQL; module Algebra | |
| 41 41 | 
             
                  # @see http://www.w3.org/TR/rdf-sparql-query/#evaluation
         | 
| 42 42 | 
             
                  def execute(queryable, options = {}, &block)
         | 
| 43 43 | 
             
                    debug(options) {"Extend"}
         | 
| 44 | 
            -
                    @solutions =  | 
| 44 | 
            +
                    @solutions = operand(1).execute(queryable, options.merge(depth: options[:depth].to_i + 1))
         | 
| 45 45 | 
             
                    @solutions.each do |solution|
         | 
| 46 46 | 
             
                      debug(options) {"===> soln #{solution.to_hash.inspect}"}
         | 
| 47 | 
            -
                       | 
| 47 | 
            +
                      operand(0).each do |(var, expr)|
         | 
| 48 48 | 
             
                        begin
         | 
| 49 49 | 
             
                          val = expr.evaluate(solution, options.merge(
         | 
| 50 50 | 
             
                                                          queryable: queryable,
         | 
| @@ -60,7 +60,19 @@ module SPARQL; module Algebra | |
| 60 60 | 
             
                    @solutions.each(&block) if block_given?
         | 
| 61 61 | 
             
                    @solutions
         | 
| 62 62 | 
             
                  end
         | 
| 63 | 
            -
             | 
| 63 | 
            +
             | 
| 64 | 
            +
                  # The variable introduced by the BIND clause must not have been used in the group graph pattern up to the point of use in BIND
         | 
| 65 | 
            +
                  def validate!
         | 
| 66 | 
            +
                    bind_vars = operand(0).map(&:first)
         | 
| 67 | 
            +
                    query_vars = operand(1).vars
         | 
| 68 | 
            +
                    
         | 
| 69 | 
            +
                    unless (bind_vars.compact & query_vars.compact).empty?
         | 
| 70 | 
            +
                      raise ArgumentError,
         | 
| 71 | 
            +
                           "bound variable used in query: #{(bind_vars.compact & query_vars.compact).to_sse}"
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                    super
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
             | 
| 64 76 | 
             
                  ##
         | 
| 65 77 | 
             
                  # Returns an optimized version of this query.
         | 
| 66 78 | 
             
                  #
         | 
| @@ -52,7 +52,24 @@ module SPARQL; module Algebra | |
| 52 52 | 
             
                    @solutions.each(&block) if block_given?
         | 
| 53 53 | 
             
                    @solutions
         | 
| 54 54 | 
             
                  end
         | 
| 55 | 
            -
             | 
| 55 | 
            +
             | 
| 56 | 
            +
                  # If filtering a join of two BGPs (having the same graph name), don't worry about validating, for shared ndvars, anyway,
         | 
| 57 | 
            +
                  #
         | 
| 58 | 
            +
                  #       (filter (regex ?homepage "^http://example.org/" "")
         | 
| 59 | 
            +
                  #         (join
         | 
| 60 | 
            +
                  #           (bgp (triple ??who :homepage ?homepage))
         | 
| 61 | 
            +
                  #           (bgp (triple ??who :schoolHomepage ?schoolPage))))))
         | 
| 62 | 
            +
                  #
         | 
| 63 | 
            +
                  # is legitimate
         | 
| 64 | 
            +
                  def validate!
         | 
| 65 | 
            +
                    unless (join = operands.last).is_a?(Join) &&
         | 
| 66 | 
            +
                            join.operands.all? {|op| op.is_a?(RDF::Query)} &&
         | 
| 67 | 
            +
                            join.operands.map(&:context).uniq.length == 1
         | 
| 68 | 
            +
                      operands.last.validate!
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
                    self
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 56 73 | 
             
                  ##
         | 
| 57 74 | 
             
                  # Returns an optimized version of this query.
         | 
| 58 75 | 
             
                  #
         |