Class: ShEx::Algebra::Shape

Inherits:
Operator show all
Includes:
Satisfiable
Defined in:
lib/shex/algebra/shape.rb

Constant Summary collapse

NAME =
:shape

Constants inherited from Operator

Operator::ARITY

Instance Attribute Summary collapse

Attributes inherited from Operator

#label, #logger, #operands, #options, #schema

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Satisfiable

#satisfiable?

Methods inherited from Operator

#base_uri, #closed?, #each_descendant, #eql?, #first_ancestor, #focus, #focus=, #initialize, #inspect, iri, #iri, #json_type, #matched, #matched=, #message, #message=, #not_matched, #not_satisfied, #operand, #parent, #parent=, #satisfiable?, #satisfied, #satisfied=, #satisfy, #semact?, #semantic_actions, #serialize_value, #status, #structure_error, #to_h, #to_json, #to_sxp, #to_sxp_bin, #triple_expression?, #unmatched, #unmatched=, #unsatisfied, #unsatisfied=, #validate!, #value, value

Constructor Details

This class inherits a constructor from ShEx::Algebra::Operator

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class ShEx::Algebra::Operator

Instance Attribute Details

#matchablesArray<RDF::Statement>

Let matchables be the triples in outs whose predicate appears in a TripleConstraint in expression. If expression is absent, ‘matchables = Ø` (the empty set).

Returns:

  • (Array<RDF::Statement>)


15
16
17
# File 'lib/shex/algebra/shape.rb', line 15

def matchables
  @matchables
end

#outsArray<RDF::Statement>

Let outs be the arcsOut in remainder: ‘outs = remainder ∩ arcsOut(G, n)`.

Returns:

  • (Array<RDF::Statement>)


10
11
12
# File 'lib/shex/algebra/shape.rb', line 10

def outs
  @outs
end

#unmatchablesArray<RDF::Statement>

Let unmatchables be the triples in outs which are not in matchables. ‘matchables ∪ unmatchables = outs.`

Returns:

  • (Array<RDF::Statement>)


20
21
22
# File 'lib/shex/algebra/shape.rb', line 20

def unmatchables
  @unmatchables
end

Class Method Details

.from_shexj(operator, options = {}) ⇒ Operator

Creates an operator instance from a parsed ShExJ representation

Returns:

Raises:

  • (ArgumentError)


26
27
28
29
# File 'lib/shex/algebra/shape.rb', line 26

def self.from_shexj(operator, options = {})
  raise ArgumentError unless operator.is_a?(Hash) && operator['type'] == "Shape"
  super
end

Instance Method Details

#expressionTripleExpression

The optional TripleExpression for this Shape.

Returns:



108
109
110
# File 'lib/shex/algebra/shape.rb', line 108

def expression
  operands.detect {|op| op.is_a?(TripleExpression)}
end

#satisfies?(focus, depth: 0) ⇒ Boolean, Operator

The satisfies semantics for a Shape depend on a matches function defined below. For a node n, shape S, graph G, and shapeMap m, ‘satisfies(n, S, G, m)`.

Parameters:

  • focus (RDF::Resource)
  • depth (Integer) (defaults to: 0)

    for logging

  • options (Hash{Symbol => Object})

    Other, operand-specific options

Returns:

  • (Boolean)
  • (Operator)

    with matched and satisfied accessors for matched triples and sub-expressions

Raises:

  • (ShEx::NotMatched)

    with expression accessor to access matched and unmatched statements along with satisfied and unsatisfied operations.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/shex/algebra/shape.rb', line 35

def satisfies?(focus, depth: 0)
  expression = self.expression
  # neigh(G, n) is the neighbourhood of the node n in the graph G.
  #
  #    neigh(G, n) = arcsOut(G, n) ∪ arcsIn(G, n)
  arcs_in = schema.graph.query(object: focus).to_a.sort_by(&:to_sxp)
  arcs_out = schema.graph.query(subject: focus).to_a.sort_by(&:to_sxp)
  neigh = (arcs_in + arcs_out).uniq

  # `matched` is the subset of statements which match `expression`.
  status("arcsIn: #{arcs_in.count}, arcsOut: #{arcs_out.count}", depth: depth)
  matched_expression = expression.matches(arcs_in, arcs_out, depth: depth + 1) if expression
  matched = Array(matched_expression && matched_expression.matched)

  # `remainder` is the set of unmatched statements
  remainder = neigh - matched

  # Let `outs` be the `arcsOut` in `remainder`: `outs = remainder ∩ arcsOut(G, n)`.
  @outs = remainder.select {|s| s.subject == focus}

  # Let `matchables` be the triples in `outs` whose predicate appears in a `TripleConstraint` in `expression`. If `expression` is absent, `matchables = Ø` (the empty set).
  predicates = expression ? expression.triple_constraints.map(&:predicate).uniq : []
  @matchables = outs.select {|s| predicates.include?(s.predicate)}

  # Let `unmatchables` be the triples in `outs` which are not in `matchables`.
  @unmatchables = outs - matchables

  # No matchable can be matched by any TripleConstraint in expression
  unmatched = matchables.select do |statement|
    expression.triple_constraints.any? do |expr|
      begin
        statement.predicate == expr.predicate && expr.matches([], [statement], depth: depth + 1)
      rescue ShEx::NotMatched
        false # Expected not to match
      end
    end if expression
  end
  unless unmatched.empty?
    not_satisfied "Statements remain matching TripleConstraints",
                  matched: matched,
                  unmatched: unmatched,
                  satisfied: expression,
                  depth: depth
  end

  # There is no triple in matchables whose predicate does not appear in extra.
  unmatched = matchables.reject {|st| extra.include?(st.predicate)}
  unless unmatched.empty?
    not_satisfied "Statements remains with predicate #{unmatched.map(&:predicate).compact.join(',')} not in extra",
                  matched: matched,
                  unmatched: unmatched,
                  satisfied: expression,
                  depth: depth
  end

  # closed is false or unmatchables is empty.
  not_satisfied "Unmatchables remain on a closed shape", depth: depth unless !closed? || unmatchables.empty?

  # Presumably, to be satisfied, there must be some triples in matches
  semantic_actions.each do |op|
    op.satisfies?(matched, matched: matched, depth: depth + 1)
  end unless matched.empty?

  # FIXME: also record matchables, outs and others?
  satisfy focus: focus, matched: matched, depth: depth
rescue ShEx::NotMatched => e
  not_satisfied e.message, focus: focus, unsatisfied: e.expression, depth: depth
end