class SPARQL::Algebra::Operator
A SPARQL
operator.
@abstract
Constants
- ARITY
- IsURI
The
SPARQL
`isIRI`/`isURI` operator.@example
(prefix ((xsd: <http://www.w3.org/2001/XMLSchema#>) (: <http://example.org/things#>)) (project (?x ?v) (filter (isIRI ?v) (bgp (triple ?x :p ?v)))))
- URI
The
SPARQL
`iri` operator.@example
(base <http://example.org/> (project (?uri ?iri) (extend ((?uri (uri "uri")) (?iri (iri "iri"))) (bgp))))
Attributes
The operands to this operator.
@return [Array]
Public Class Methods
Returns the arity of this operator class.
@example
Operator.arity #=> -1 Operator::Nullary.arity #=> 0 Operator::Unary.arity #=> 1 Operator::Binary.arity #=> 2 Operator::Ternary.arity #=> 3
@return [Integer] an integer in the range `(-1..3)`
# File lib/sparql/algebra/operator.rb, line 332 def self.arity self.const_get(:ARITY) end
@param [Array<RDF::Term>] operands @return [RDF::Term] @see Operator#evaluate
# File lib/sparql/algebra/operator.rb, line 317 def self.evaluate(*operands, **options) self.new(*operands, **options).evaluate(RDF::Query::Solution.new, **options) end
Returns an operator class for the given operator `name`.
@param [Symbol, to_s] name @param [Integer] arity @return [Class] an operator class, or `nil` if an operator was not found
# File lib/sparql/algebra/operator.rb, line 159 def self.for(name, arity = nil) # TODO: refactor this to dynamically introspect loaded operator classes. case (name.to_s.downcase.to_sym rescue nil) when :'!=' then NotEqual when :'/' then Divide when :'=' then Equal when :* then Multiply when :+ then Plus when :- then arity.eql?(1) ? Negate : Subtract when :< then LessThan when :<= then LessThanOrEqual when :<=> then Compare # non-standard when :> then GreaterThan when :>= then GreaterThanOrEqual when :abs then Abs when :add then Add when :alt then Alt when :and, :'&&' then And when :avg then Avg when :bnode then BNode when :bound then Bound when :coalesce then Coalesce when :ceil then Ceil when :concat then Concat when :contains then Contains when :count then Count when :datatype then Datatype when :day then Day when :encode_for_uri then EncodeForURI when :divide then Divide when :exists then Exists when :floor then Floor when :group_concat then GroupConcat when :hours then Hours when :if then If when :in then In when :iri, :uri then IRI when :isblank then IsBlank when :isiri then IsIRI when :isliteral then IsLiteral when :isnumeric then IsNumeric when :isuri then IsIRI # alias when :lang then Lang when :langmatches then LangMatches when :lcase then LCase when :md5 then MD5 when :max then Max when :min then Min when :minus then Minus when :minutes then Minutes when :month then Month when :multiply then Multiply when :not, :'!' then Not when :notexists then NotExists when :notin then NotIn when :notoneof then NotOneOf when :now then Now when :or, :'||' then Or when :path then Path when :path? then PathOpt when :"path*" then PathStar when :"path+" then PathPlus when :plus then Plus when :rand then Rand when :regex then Regex when :replace then Replace when :reverse then Reverse when :round then Round when :sameterm then SameTerm when :sample then Sample when :seconds then Seconds when :seq then Seq when :sequence then Sequence when :sha1 then SHA1 when :sha256 then SHA256 when :sha512 then SHA512 when :str then Str when :strafter then StrAfter when :strbefore then StrBefore when :strdt then StrDT when :strends then StrEnds when :strlang then StrLang when :strlen then StrLen when :strstarts then StrStarts when :struuid then StrUUID when :substr then SubStr when :subtract then Subtract when :sum then Sum when :timezone then Timezone when :tz then TZ when :ucase then UCase when :uuid then UUID when :year then Year # Miscellaneous when :asc then Asc when :desc then Desc when :exprlist then Exprlist # Datasets when :dataset then Dataset # Query forms when :ask then Ask when :base then Base when :bgp then BGP when :construct then Construct when :describe then Describe when :distinct then Distinct when :extend then Extend when :filter then Filter when :graph then Graph when :group then Group when :join then Join when :leftjoin then LeftJoin when :order then Order when :minus then Minus when :prefix then Prefix when :project then Project when :reduced then Reduced when :slice then Slice when :table then Table when :triple then RDF::Query::Pattern when :union then Union # Update forms when :add then Add when :clear then Clear when :copy then Copy when :create then Create when :delete then Delete when :deletedata then DeleteData when :deletewhere then DeleteWhere when :drop then Drop when :insert then Insert when :insertdata then InsertData when :load then Load when :modify then Modify when :move then Move when :update then Update when :using then Using when :with then With # RDF-star when :istriple then IsTriple when :triple then Triple when :subject then Subject when :predicate then Predicate when :object then Object else nil # not found end end
Initializes a new operator instance.
@overload initialize(*operands)
@param [Array<RDF::Term>] operands
@overload initialize(*operands, **options)
@param [Array<RDF::Term>] operands @param [Hash{Symbol => Object}] options any additional options @option options [Boolean] :memoize (false) whether to memoize results for particular operands
@raise [TypeError] if any operand is invalid
# File lib/sparql/algebra/operator.rb, line 351 def initialize(*operands, **options) @options = options.dup @operands = operands.map! do |operand| case operand when Array operand.each {|op| op.parent = self if operand.respond_to?(:parent=)} operand when Operator, Variable, RDF::Term, RDF::Query, RDF::Query::Pattern, Array, Symbol operand.parent = self if operand.respond_to?(:parent=) operand when TrueClass, FalseClass, Numeric, String, DateTime, Date, Time RDF::Literal(operand) when NilClass nil else raise TypeError, "invalid SPARQL::Algebra::Operator operand: #{operand.inspect}" end end end
Prefixes useful for future serialization
@return [Hash{Symbol => RDF::URI}]
Prefix definitions
# File lib/sparql/algebra/operator.rb, line 426 def self.prefixes @prefixes end
Prefixes useful for future serialization
@param [Hash{Symbol => RDF::URI}] hash
Prefix definitions
@return [Hash{Symbol => RDF::URI}]
# File lib/sparql/algebra/operator.rb, line 436 def self.prefixes=(hash) @prefixes = hash end
Private Class Methods
@private @return [void]
# File lib/sparql/algebra/operator.rb, line 743 def self.inherited(child) @@subclasses << child unless child.superclass.equal?(Operator) # grandchildren only super end
Public Instance Methods
Returns `true` if this is an aggregate
Overridden in evaluatables which are aggregates
@return [Boolean] `true` or `false`
# File lib/sparql/algebra/operator.rb, line 511 def aggregate? respond_to?(:aggregate) end
Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
@param [RDF::Query::Solution] solution @return [self]
# File lib/sparql/algebra/operator.rb, line 381 def bind(solution) @operands.each {|op| op.bind(solution)} self end
Returns `true` if none of the operands are variables, `false` otherwise.
@return [Boolean] `true` or `false` @see variable?
# File lib/sparql/algebra/operator.rb, line 501 def constant? !(variable?) end
Deep duplicate operands
# File lib/sparql/algebra/operator.rb, line 372 def deep_dup self.class.new(*operands.map(&:deep_dup), **@options) end
Enumerate via depth-first recursive descent over operands, yielding each operator @yield operator @yieldparam [Object] operator @return [Enumerator]
# File lib/sparql/algebra/operator.rb, line 624 def each_descendant(&block) if block_given? operands.each do |operand| case operand when Array operand.each do |op| op.each_descendant(&block) if op.respond_to?(:each_descendant) block.call(op) end else operand.each_descendant(&block) if operand.respond_to?(:each_descendant) end block.call(operand) end end enum_for(:each_descendant) end
@param [Statement] other @return [Boolean]
# File lib/sparql/algebra/operator.rb, line 600 def eql?(other) other.class == self.class && other.operands == self.operands end
Returns `true` if this is evaluatable (i.e., returns values for a binding), `false` otherwise.
@return [Boolean] `true` or `false`
# File lib/sparql/algebra/operator.rb, line 482 def evaluatable? respond_to?(:evaluate) end
Returns `true` if this is executable (i.e., contains a graph patterns), `false` otherwise.
@return [Boolean] `true` or `false`
# File lib/sparql/algebra/operator.rb, line 491 def executable? respond_to?(:execute) end
First ancestor operator of type `klass`
@param [Class] klass @return [Operator]
# File lib/sparql/algebra/operator.rb, line 663 def first_ancestor(klass) parent.is_a?(klass) ? parent : parent.first_ancestor(klass) if parent end
Returns a developer-friendly representation of this operator.
@return [String]
# File lib/sparql/algebra/operator.rb, line 593 def inspect sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, operands.to_sse.gsub(/\s+/m, ' ')) end
Return the non-destinguished variables contained within this operator @return [Array<RDF::Query::Variable>]
# File lib/sparql/algebra/operator.rb, line 608 def ndvars vars.reject(&:distinguished?) end
Returns `true` if any of the operands are nodes, `false` otherwise.
@return [Boolean]
# File lib/sparql/algebra/operator.rb, line 471 def node? operands.any? do |operand| operand.respond_to?(:node?) ? operand.node? : operand.node? end end
Returns the operand at the given `index`.
@param [Integer] index
an operand index in the range `(0...(operands.count))`
@return [RDF::Term]
# File lib/sparql/algebra/operator.rb, line 452 def operand(index = 0) operands[index] end
Returns an optimized version of this expression.
For constant expressions containing no variables, returns the result of evaluating the expression with empty bindings; otherwise returns a copy of `self`.
Optimization is not possible if the expression raises an exception, such as a `TypeError` or `ZeroDivisionError`, which must be conserved at runtime.
@return [SPARQL::Algebra::Expression] @see RDF::Query#optimize
SPARQL::Algebra::Expression#optimize
# File lib/sparql/algebra/operator.rb, line 528 def optimize(**options) if constant? # Note that if evaluation results in a `TypeError` or other error, # we must return `self` so that the error is conserved at runtime: evaluate(RDF::Query::Solution.new) rescue self else super # returns a copy of `self` end end
Optimizes this query by optimizing its constituent operands according to their cost estimates.
@return [self] @see RDF::Query#optimize!
# File lib/sparql/algebra/operator.rb, line 544 def optimize!(**options) @operands.map! do |op| op.optimize(**options) if op.respond_to?(:optimize) end self end
Parent expression, if any
@return [Operator]
# File lib/sparql/algebra/operator.rb, line 648 def parent; @options[:parent]; end
Parent operator, if any
@return [Operator]
# File lib/sparql/algebra/operator.rb, line 654 def parent=(operator) @options[:parent]= operator end
Prefixes useful for future serialization
@return [Hash{Symbol => RDF::URI}]
Prefix definitions
# File lib/sparql/algebra/operator.rb, line 417 def prefixes Operator.prefixes end
Rewrite operands by yielding each operand. Recursively descends through operands implementing this method.
@yield operand @yieldparam [] operand @yieldreturn [SPARQL::Algebra::Expression] the re-written operand @return [SPARQL::Algebra::Expression] `self`
# File lib/sparql/algebra/operator.rb, line 559 def rewrite(&block) @operands = @operands.map do |op| # Rewrite the operand unless new_op = block.call(op) # Not re-written, rewrite new_op = op.respond_to?(:rewrite) ? op.rewrite(&block) : op end new_op end self end
Returns an S-Expression (SXP) representation of this operator
@return [String]
# File lib/sparql/algebra/operator.rb, line 585 def to_sxp to_sxp_bin.to_sxp end
Returns the SPARQL
S-Expression (SSE) representation of this operator.
@return [Array] @see openjena.org/wiki/SSE
# File lib/sparql/algebra/operator.rb, line 576 def to_sxp_bin operator = [self.class.const_get(:NAME)].flatten.first [operator, *(operands || []).map(&:to_sxp_bin)] end
Validate all operands, operator specific classes should override for operator-specific validation @return [SPARQL::Algebra::Expression] `self` @raise [ArgumentError] if the value is invalid
# File lib/sparql/algebra/operator.rb, line 671 def validate! operands.each {|op| op.validate! if op.respond_to?(:validate!)} self end
Returns `true` if any of the operands are variables, `false` otherwise.
@return [Boolean] `true` or `false` @see constant?
# File lib/sparql/algebra/operator.rb, line 462 def variable? operands.any? {|op| op.respond_to?(:variable?) && op.variable?} end
Return the variables contained within this operator @return [Array<RDF::Query::Variable>]
# File lib/sparql/algebra/operator.rb, line 615 def vars operands.select {|o| o.respond_to?(:vars)}.map(&:vars).flatten end
Protected Instance Methods
Returns the effective boolean value (EBV) of the given `literal`.
@param [RDF::Literal] literal @return [RDF::Literal::Boolean] `true` or `false` @raise [TypeError] if the literal could not be coerced to an `RDF::Literal::Boolean` @see www.w3.org/TR/sparql11-query/#ebv
# File lib/sparql/algebra/operator.rb, line 684 def boolean(literal) case literal when FalseClass then RDF::Literal::FALSE when TrueClass then RDF::Literal::TRUE when RDF::Literal::Boolean # If the argument is a typed literal with a datatype of # `xsd:boolean`, the EBV is the value of that argument. # However, the EBV of any literal whose type is `xsd:boolean` is # false if the lexical form is not valid for that datatype. RDF::Literal(literal.valid? && literal.true?) when RDF::Literal::Numeric # If the argument is a numeric type or a typed literal with a # datatype derived from a numeric type, the EBV is false if the # operand value is NaN or is numerically equal to zero; otherwise # the EBV is true. # However, the EBV of any literal whose type is numeric is # false if the lexical form is not valid for that datatype. RDF::Literal(literal.valid? && !(literal.zero?) && !(literal.respond_to?(:nan?) && literal.nan?)) else case when literal.is_a?(RDF::Literal) && literal.plain? # If the argument is a plain literal or a typed literal with a # datatype of `xsd:string`, the EBV is false if the operand value # has zero length; otherwise the EBV is true. RDF::Literal(!(literal.value.empty?)) else # All other arguments, including unbound arguments, produce a type error. raise TypeError, "could not coerce #{literal.inspect} to an RDF::Literal::Boolean" end end end
Transform an array of expressions into a recursive set of binary operations e.g.: a || b || c => (|| a (|| b c)) @param [Class] klass Binary
Operator
class @param [Array<SPARQL::Algebra::Expression>] expressions @return [SPARQL::Algebra::Expression]
# File lib/sparql/algebra/operator.rb, line 722 def to_binary(klass, *expressions) case expressions.length when 0 # Oops! raise "Operator#to_binary requires two or more expressions" when 1 expressions.first when 2 klass.new(*expressions) else klass.new(expressions.shift, to_binary(klass, *expressions)) end end