Class: RdfContext::Graph

Inherits:
Resource show all
Defined in:
lib/rdf_context/graph.rb

Overview

A simple graph to hold triples.

Graphs store triples, and the namespaces associated with those triples, where defined

Direct Known Subclasses

AggregateGraph, ConjunctiveGraph, QuotedGraph

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Resource

#bnode?, #literal?, parse, #resource?, #uri?

Constructor Details

#initialize(options = {}) ⇒ Graph

Create a Graph with the given store and identifier.

The constructor accepts a store option, that will be used to store the graph data.

Stores can be context-aware or unaware. Unaware stores take up (some) less space but cannot support features that require context, such as true merging/demerging of sub-graphs and provenance.

The Graph constructor can take an identifier which identifies the Graph by name. If none is given, the graph is assigned a BNode for it’s identifier. For more on named graphs, see: en.wikipedia.org/wiki/RDFLib

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

  • options[Resource] (Hash)

    a customizable set of options

  • options[Boolean] (Hash)

    a customizable set of options

Options Hash (options):

  • :store (AbstractStore, Symbol)

    defaults to a new ListStore instance. May be symbol :list_store or :memory_store



28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/rdf_context/graph.rb', line 28

def initialize(options = {})
  # Instantiate triple store
  @store = case options[:store]
  when AbstractStore  then options[:store]
  when :list_store    then ListStore.new
  when :memory_store  then MemoryStore.new
  else                     ListStore.new
  end
  
  @allow_n3 = options[:allow_n3]
  
  @identifier = Triple.coerce_node(options[:identifier]) || BNode.new
end

Instance Attribute Details

#allow_n3Object

Returns the value of attribute allow_n3.



9
10
11
# File 'lib/rdf_context/graph.rb', line 9

def allow_n3
  @allow_n3
end

#identifierObject (readonly)

Returns the value of attribute identifier.



7
8
9
# File 'lib/rdf_context/graph.rb', line 7

def identifier
  @identifier
end

#storeObject (readonly)

Returns the value of attribute store.



8
9
10
# File 'lib/rdf_context/graph.rb', line 8

def store
  @store
end

#triples(triple = Triple.new(nil, nil, nil), &block) ⇒ Array<Triple> (readonly) Also known as: find

Triples from graph, optionally matching subject, predicate, or object. Delegates to Store#triples.

Parameters:

  • triple (Triple) (defaults to: Triple.new(nil, nil, nil))

    (nil) Triple to match, may be a pattern triple or nil

Returns:



278
279
280
# File 'lib/rdf_context/graph.rb', line 278

def triples
  @triples
end

Instance Method Details

#<<(triple) ⇒ Graph

Adds an more extant triples to a graph. Delegates to Store.

Examples:

g = Graph.new;
t = Triple.new(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new);
g << t

Parameters:

  • triple (Triple)

    the triple to be added to the graph

Returns:

  • (Graph)

    Returns the graph



235
236
237
238
239
# File 'lib/rdf_context/graph.rb', line 235

def << (triple)
  triple.validate_rdf unless @allow_n3 # Only add triples if n3-mode is set
  @store.add(triple, self)
  self
end

#==(other) ⇒ Boolean

Two graphs are equal (==) if each other if they are both graphs have the same identifiers and the same size.

If each graph has a BNode identifier, they are considered to be equal if the have the same size

Parameters:

Returns:

  • (Boolean)


492
493
494
495
496
# File 'lib/rdf_context/graph.rb', line 492

def ==(other)
  #puts "== size #{self.size} vs #{other.size}" if ::RdfContext::debug?
  other.is_a?(Graph) && self.size == other.size &&
    ((other.identifier.is_a?(BNode) && identifier.is_a?(BNode)) || (other.identifier.to_s == identifier.to_s))
end

#[](item) ⇒ Triple

Indexed statement in serialized graph triples. Equivalent to graph.triples

Returns:



206
# File 'lib/rdf_context/graph.rb', line 206

def [] (item); @store.item(item, self); end

#add(*triples) ⇒ Graph

Adds one or more extant triples to a graph. Delegates to Store.

Examples:

g = Graph.new;
t1 = Triple.new(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new);
t2 = Triple.new(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new);
g.add(t1, t2, ...)

Parameters:

  • triples (Array<Triple>)

    one or more triples. Last element may be a hash for options

  • [Resource] (Hash)

    a customizable set of options

Returns:

  • (Graph)

    Returns the graph



253
254
255
256
257
258
259
260
261
262
# File 'lib/rdf_context/graph.rb', line 253

def add(*triples)
  options = triples.last.is_a?(Hash) ? triples.pop : {}
  ctx = options[:context] || @default_context || self
  triples.each do |t|
    t.validate_rdf unless @allow_n3 # Only add triples if n3-mode is set
    #puts "Add #{t.inspect}, ctx: #{ctx.identifier}" if $verbose
    @store.add(t, ctx)
  end
  self
end

#add_seq(subject, predicate, objects) ⇒ Graph

Adds a list of resources as an RDF list by creating bnodes and first/rest triples. Removes existing sequence nodes.

Parameters:

  • subject (URIRef, BNode)

    the subject of the triple

  • predicate (URIRef)

    the predicate of the triple

  • objects (Array)

    List of objects to serialize

Returns:

  • (Graph)

    Returns the graph

Raises:

  • (Error)

    Checks parameter types and raises if they are incorrect.



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
# File 'lib/rdf_context/graph.rb', line 329

def add_seq(subject, predicate, objects)
  self.triples(Triple.new(subject, predicate, nil)).each do |t, ctx|
    bn = t.object
    while bn != RDF_NS.nil
      rest = properties(bn)[RDF_NS.rest.to_s].first
      remove(Triple.new(bn, nil, nil))
      bn = rest
    end
  end
  remove(Triple.new(subject, predicate, nil))

  @properties.delete(subject.to_s) if @properties.is_a?(Hash)
  
  if objects.empty?
    add_triple(subject, predicate, RDF_NS.nil)
    return self
  end
  
  if RDF_NS.first != predicate
    bn = BNode.new
    add_triple(subject, predicate, bn)
    subject = bn
  end

  last = objects.pop
  
  objects.each do |o|
    add_triple(subject, RDF_NS.first, o)
    bn = BNode.new
    add_triple(subject, RDF_NS.rest, bn)
    subject = bn
  end

  # Last item in list
  add_triple(subject, RDF_NS.first, last)
  add_triple(subject, RDF_NS.rest, RDF_NS.nil)
  
  self
end

#add_triple(subject, predicate, object) ⇒ Graph

Adds a triple to a graph directly from the intended subject, predicate, and object.

Examples:

g = Graph.new
g.add_triple(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new)
# => results in the triple being added to g

Parameters:

Returns:

  • (Graph)

    Returns the graph

Raises:

  • (Error)

    Checks parameter types and raises if they are incorrect.



220
221
222
223
# File 'lib/rdf_context/graph.rb', line 220

def add_triple(subject, predicate, object)
  self.add(Triple.new(subject, predicate, object))
  self
end

#bind(namespace) ⇒ Namespace

Bind a namespace to the graph.

Examples:

g = Graph.new; g.bind(Namespace.new("http://xmlns.com/foaf/0.1/", "foaf")) # => binds the Foaf namespace to g

Parameters:

  • namespace (Nameespace)

    the namespace to bind

Returns:

  • (Namespace)

    The newly bound or pre-existing namespace.

Raises:



155
156
157
158
# File 'lib/rdf_context/graph.rb', line 155

def bind(namespace)
  raise GraphException, "Can't bind #{namespace.inspect} as namespace" unless namespace.is_a?(Namespace)
  @store.bind(namespace)
end

#bnodesArray<BNode>

Get all BNodes with usage count used within graph

Returns:



445
446
447
# File 'lib/rdf_context/graph.rb', line 445

def bnodes
  @store.bnodes(self)
end

#close(commit_pending_transaction = false)

This method returns an undefined value.

Close the graph store

Might be necessary for stores that require closing a connection to a database or releasing some resource.

Parameters:

  • commit_pending_transaction (Boolean) (defaults to: false)

    (false)



94
95
96
# File 'lib/rdf_context/graph.rb', line 94

def close(commit_pending_transaction=false)
  @store.close(commit_pending_transaction)
end

#commit

This method returns an undefined value.

Commit changes to graph



73
# File 'lib/rdf_context/graph.rb', line 73

def commit; @store.commit; end

#contains?(triple) ⇒ Boolean

Check to see if this graph contains the specified triple

Parameters:

Returns:

  • (Boolean)


439
440
441
# File 'lib/rdf_context/graph.rb', line 439

def contains?(triple)
  @store.contains?(triple, self)
end

#context_aware?Boolean

Returns:

  • (Boolean)


61
# File 'lib/rdf_context/graph.rb', line 61

def context_aware?; @store.context_aware?; end

#destroy(configuration = nil)

This method returns an undefined value.

Destroy the store identified by configuration if supported If configuration is nil, remove the graph context



66
67
68
69
# File 'lib/rdf_context/graph.rb', line 66

def destroy(configuration = nil)
  @store.destroy(configuration ? configuration : {:context => self})
  self.freeze
end

#get_by_type(object) ⇒ Array<Triple>

Get list of subjects having rdf:type == object

Parameters:

Returns:



453
454
455
# File 'lib/rdf_context/graph.rb', line 453

def get_by_type(object)
  triples(Triple.new(nil, RDF_TYPE, object)).map {|t, ctx| t.subject}
end

#graph?Boolean

Returns ‘false`, overridden in BNode

Returns:

  • (Boolean)


46
47
48
# File 'lib/rdf_context/graph.rb', line 46

def graph?
  true
end

#has_bnode_identifier?(bn) ⇒ Boolean

Detect the presence of a BNode in the graph, either as a subject or an object

Parameters:

  • bn (BNode)

    BNode to find

Returns:

  • (Boolean)


429
430
431
432
433
434
# File 'lib/rdf_context/graph.rb', line 429

def has_bnode_identifier?(bn)
  self.triples do |triple, context|
    return true if triple.subject.eql?(bn) || triple.object.eql?(bn)
  end
  false
end

#hashString

Hash of graph, based on graph type and identifier

Returns:



56
57
58
# File 'lib/rdf_context/graph.rb', line 56

def hash
  [self.class.to_s, self.identifier].hash
end

#inspectObject



50
51
52
# File 'lib/rdf_context/graph.rb', line 50

def inspect
  "#{self.class}[id=#{identifier},store=#{store.inspect}]"
end

#isomorphic?(other) ⇒ Boolean

Two graphs are isomorphic if each is an instance of the other, considering BNode equivalence. This may be done by creating a new graph an substituting each permutation of BNode identifiers from self to other until every permutation is exhausted, or a textual equivalence is found after sorting each graph.

We just follow Python RDFlib’s lead and do a simple comparison

Parameters:

Returns:

  • (Boolean)


506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
# File 'lib/rdf_context/graph.rb', line 506

def isomorphic?(other)
  return false unless self == other
  
  bn_self = bnodes.values.sort
  bn_other = other.bnodes.values.sort
  puts "isomorphic? bnodes '#{bn_self.to_sentence}' vs '#{bn_other.to_sentence}'" if ::RdfContext::debug?
  return false unless bn_self == bn_other
  
  # Check each triple to see if it's contained in the other graph
  triples do |t, ctx|
    next if t.subject.is_a?(BNode) || t.predicate.is_a?(BNode) || t.object.is_a?(BNode)
    puts "isomorphic? contains '#{t.to_ntriples}: #{other.contains?(t)}'" if ::RdfContext::debug?
    return false unless other.contains?(t)
  end
  
  # For each BNode, check permutations of similar bnodes in other graph
  bnode_permutations(bnodes, other.bnodes) do |bn_map|
    puts "bnode permutations: #{bn_map.inspect}" if ::RdfContext::debug?
    # bn_map contains 1-1 mapping of bnodes from self to other
    catch :next_perm do
      triples do |t, ctx|
        next unless t.subject.is_a?(BNode) || t.predicate.is_a?(BNode) || t.object.is_a?(BNode)
        subject, predicate, object = t.subject, t.predicate, t.object
        subject = bn_map[subject] if bn_map.has_key?(subject)
        predicate = bn_map[predicate] if bn_map.has_key?(predicate)
        object = bn_map[object] if bn_map.has_key?(object)
        tn = Triple.new(subject, predicate, object)
        puts "  isomorphic? contains '#{tn.inspect}': #{other.contains?(tn)}" if ::RdfContext::debug?
        next if other.contains?(tn)
      
        puts "  no, next permutation" if ::RdfContext::debug?
        # Not a match, try next permutation
        throw :next_perm
      end
      
      # If we matched all triples in the graph using this permutation, we're done
      return true
    end
  end
  
  # Exhausted all permutations, unless there were no bnodes
  bn_self.length == 0
end

#merge!(graph)

This method returns an undefined value.

Merge a graph into this graph

Parameters:

Raises:



467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
# File 'lib/rdf_context/graph.rb', line 467

def merge!(graph)
  raise GraphException.new("merge without a graph") unless graph.is_a?(Graph)
  
  # Map BNodes from source Graph to new BNodes
  bn = graph.bnodes
  bn.keys.each {|k| bn[k] = BNode.new}
  
  graph.triples do |triple, context|
    # If triple contains bnodes, remap to new values
    if triple.subject.is_a?(BNode) || triple.predicate.is_a?(BNode) || triple.object.is_a?(BNode)
      triple = triple.clone
      triple.subject = bn[triple.subject] if triple.subject.is_a?(BNode)
      triple.predicate = bn[triple.predicate] if triple.predicate.is_a?(BNode)
      triple.object = bn[triple.object] if triple.object.is_a?(BNode)
    end
    self << triple
  end
end

#n3String

Return an n3 identifier for the Graph

Returns:



421
422
423
# File 'lib/rdf_context/graph.rb', line 421

def n3
  "[#{self.identifier.to_n3}]"
end

#namespace(prefix) ⇒ Namespace

Namespace for prefix

Parameters:

Returns:



181
# File 'lib/rdf_context/graph.rb', line 181

def namespace(prefix); @store.namespace(prefix); end

#nsbindingHash{String => Namespace}

Returns:



161
# File 'lib/rdf_context/graph.rb', line 161

def nsbinding; @store.nsbinding; end

#objectsArray<Resource>

List of distinct objects in graph

Returns:



202
# File 'lib/rdf_context/graph.rb', line 202

def objects; @store.objects(self); end

#open(configuration = {})

This method returns an undefined value.

Open the graph store

Might be necessary for stores that require opening a connection to a database or acquiring some resource.



84
85
86
# File 'lib/rdf_context/graph.rb', line 84

def open(configuration = {})
  @store.open(configuration)
end

#parse(stream, uri = nil, options = {}, &block) ⇒ Graph

Parse source into Graph.

Merges results into a common Graph

Parameters:

  • stream:: (IO, String)

    the RDF IO stream, string, Nokogiri::HTML::Document or Nokogiri::XML::Document

  • uri:: (String)

    the URI of the document

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :debug (Array) — default: nil

    Array to place debug messages

  • :type (:rdfxml, :html, :n3) — default: nil
  • :strict (Boolean) — default: false

    Raise Error if true, continue with lax parsing, otherwise

  • :allow_n3 (Boolean) — default: false

    Allow N3-specific triples: Literals as subject, BNodes as predicate

Returns:

  • (Graph)

    Returns the graph containing parsed triples



561
562
563
564
# File 'lib/rdf_context/graph.rb', line 561

def parse(stream, uri = nil, options = {}, &block) # :yields: triple
  @allow_n3 ||= options[:allow_n3]
  Parser.parse(stream, uri, options.merge(:graph => self), &block)
end

#predicatesArray<Resource>

List of distinct predicates in graph

Returns:



198
# File 'lib/rdf_context/graph.rb', line 198

def predicates; @store.predicates(self); end

#prefix(namespace) ⇒ String

Prefix for namespace

Parameters:

Returns:



186
# File 'lib/rdf_context/graph.rb', line 186

def prefix(namespace); @store.prefix(namespace); end

#properties(subject, recalc = false) ⇒ Hash{String => Resource}

Resource properties

Properties arranged as a hash with the predicate Term as index to an array of resources or literals

Examples:

graph.parse(':foo a :bar; rdfs:label "An example" .', "http://example.com/")
graph.resources("http://example.com/subject") =>
{
  "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" => [<http://example.com/#bar>],
  "http://example.com/#label"                       => ["An example"]
}

Parameters:

  • subject (Resource)
  • recalc (Boolean) (defaults to: false)

    Refresh cache of property values

Returns:



384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/rdf_context/graph.rb', line 384

def properties(subject, recalc = false)
  @properties ||= {}
  @properties.delete(subject.to_s) if recalc
  @properties[subject.to_s] ||= begin
    hash = Hash.new
    self.triples(Triple.new(subject, nil, nil)).map do |t, ctx|
      pred = t.predicate.to_s

      hash[pred] ||= []
      hash[pred] << t.object
    end
    hash
  end
end

#qname(uri) ⇒ String

QName for a URI Try bound namespaces, and if not found, try well-known namespaces

Parameters:

Returns:



170
171
172
173
174
175
176
# File 'lib/rdf_context/graph.rb', line 170

def qname(uri)
  uri.to_qname(self.uri_binding) || begin
    qn = uri.to_qname(WELLKNOWN_NAMESPACES)
    self.bind(uri.namespace) if qn
    qn
  end
end

#remove(triple)

This method returns an undefined value.

Remove a triple from the graph. Delegates to store. Nil matches all triples and thus empties the graph

Parameters:



268
269
270
271
# File 'lib/rdf_context/graph.rb', line 268

def remove(triple)
  @properties.delete(triple.subject.to_s) if @properties.is_a?(Hash)
  @store.remove(triple, self)
end

#rollback

This method returns an undefined value.

Rollback active transactions



77
# File 'lib/rdf_context/graph.rb', line 77

def rollback; @store.rollback; end

#seq(subject, predicate = RDF_NS.first) ⇒ Array<Resource>

Returns ordered rdf:_n objects or rdf:first, rdf:rest for a given subject

Parameters:

  • subject (Resource)
  • predicate (Resource) (defaults to: RDF_NS.first)

    defaults to rdf:first, not used of subject is an rdf:List type

Returns:



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/rdf_context/graph.rb', line 287

def seq(subject, predicate = RDF_NS.first)
  props = properties(subject)
  rdf_type = (props[RDF_TYPE.to_s] || [])

  #puts "seq; #{rdf_type} #{rdf_type - [RDF_NS.Seq, RDF_NS.Bag, RDF_NS.Alt]}"
  if rdf_type.include?(RDF_NS.Seq) || rdf_type.include?(RDF_NS.Bag) || rdf_type.include?(RDF_NS.Alt)
    props.keys.select {|k| k.match(/#{RDF_NS.uri}_(\d)$/)}.
      sort_by {|i| i.sub(RDF_NS._.to_s, "").to_i}.
      map {|key| props[key]}.
      flatten
  elsif !self.triples(Triple.new(subject, predicate, nil)).empty?
    # N3-style first/rest chain
    unless predicate == RDF_NS.first
      subject = (properties(subject)[predicate.to_s] || []).first
    end
    
    list = []
    while subject != RDF_NS.nil
      props = properties(subject)
      f = props[RDF_NS.first.to_s]
      if f.to_s.empty? || f.first == RDF_NS.nil
        subject = RDF_NS.nil
      else
        list += f
        subject = props[RDF_NS.rest.to_s].first
      end
    end
    list
  else
    []
  end
end

#serialize(options) ⇒ IO, String

Serialize graph using specified serializer class.

Other options are parser specific.

Parameters:

  • options (Hash)

    a customizable set of options

Options Hash (options):

  • :format (#to_sym)

    serializer, defaults to a new NTSerializer instance. Otherwise may be a symbol from :nt, :turtle, :xml

  • :io (#read, #to_s)

    IO (or StringIO) object, otherwise serializes to a string

  • :base (URIRef)

    serializer, defaults to a new NTSerializer instance. Otherwise may be a symbol from :nt, :turtle, :xml

Returns:

  • (IO, String)

    Passed IO/StringIO object or a string



107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/rdf_context/graph.rb', line 107

def serialize(options)
  serializer = case options[:format].to_sym
  when AbstractSerializer   then options[:serializer]
  when :nt, :ntriples       then NTSerializer.new(self)
  when :ttl, :turtle, :n3   then TurtleSerializer.new(self)
  when :rdf, :xml, :rdfxml  then XmlSerializer.new(self)
  else                           NTSerializer.new(self)
  end
  
  io = options[:io] || StringIO.new
  
  serializer.serialize(io, options)
  options[:io] ? io : (io.rewind; io.read)
end

#sizeInteger

Number of Triples in the graph

Returns:

  • (Integer)


190
# File 'lib/rdf_context/graph.rb', line 190

def size; @store.size(self); end

#subjectsArray<Resource>

List of distinct subjects in graph

Returns:



194
# File 'lib/rdf_context/graph.rb', line 194

def subjects; @store.subjects(self); end

#sync_properties(subject)

This method returns an undefined value.

Synchronize properties to graph

Parameters:



403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
# File 'lib/rdf_context/graph.rb', line 403

def sync_properties(subject)
  props = properties(subject)
  
  # First remove all properties for subject
  remove(Triple.new(subject, nil, nil))
  
  # Iterate through and add properties to graph
  props.each_pair do |pred, list|
    predicate = URIRef.intern(pred)
    [list].flatten.compact.each do |object|
      add(Triple.new(subject, predicate, object))
    end
  end
  @properties.delete(subject.to_s) # Read back in from graph
end

#to_ntriplesString

Exports the graph to RDF in N-Triples form.

Examples:

g = Graph.new; g.add_triple(BNode.new, URIRef.new("http://xmlns.com/foaf/0.1/knows"), BNode.new); g.to_ntriples  # => returns a string of the graph in N-Triples form

Returns:

  • (String)

    The serialized graph in N-Triples.

Author:

  • Tom Morris



131
132
133
# File 'lib/rdf_context/graph.rb', line 131

def to_ntriples
  serialize(:format => :nt)
end

#to_rdfxmlString

Exports the graph to RDF in RDF/XML form.

Returns:

  • (String)

    The serialized RDF/XML graph



143
144
145
# File 'lib/rdf_context/graph.rb', line 143

def to_rdfxml
  serialize(:format => :rdfxml)
end

#to_sString

Output graph using to_ntriples

Returns:

  • (String)

    The serialized graph in N-Triples.



137
# File 'lib/rdf_context/graph.rb', line 137

def to_s; self.to_ntriples; end

#type_of(subject) ⇒ URIRef

Get type(s) of subject, returns a list of symbols

Parameters:

Returns:



460
461
462
# File 'lib/rdf_context/graph.rb', line 460

def type_of(subject)
  triples(Triple.new(subject, RDF_TYPE, nil)).map {|t, ctx| t.object}
end

#uri_bindingHash{URIRef => Namespace}

Returns:



164
# File 'lib/rdf_context/graph.rb', line 164

def uri_binding; @store.uri_binding; end