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

- (Graph) initialize(options = {})

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

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

- (Object) allow_n3

Returns the value of attribute allow_n3



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

def allow_n3
  @allow_n3
end

- (Object) identifier (readonly)

Returns the value of attribute identifier



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

def identifier
  @identifier
end

- (Object) store (readonly)

Returns the value of attribute store



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

def store
  @store
end

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

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



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

def triples(triple = Triple.new(nil, nil, nil), &block) # :yields: triple, context
  @store.triples(triple, self, &block) || []
end

Instance Method Details

- (Graph) <<(triple)

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


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

- (Boolean) ==(other)

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



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

- (Triple) [](item)

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



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

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

- (Graph) add(*triples)

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, ...)


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

- (Graph) add_seq(subject, predicate, objects)

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

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

- (Graph) add_triple(subject, predicate, object)

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

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

- (Namespace) bind(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

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

- (Array<BNode>) bnodes

Get all BNodes with usage count used within graph



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.



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

- (Boolean) contains?(triple)

Check to see if this graph contains the specified triple



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

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

- (Boolean) context_aware?



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

- (Array<Triple>) get_by_type(object)

Get list of subjects having rdf:type == object



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

- (Boolean) graph?

Returns `false`, overridden in BNode



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

def graph?
  true
end

- (Boolean) has_bnode_identifier?(bn)

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



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

- (String) hash

Hash of graph, based on graph type and identifier



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

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

- (Object) inspect



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

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

- (Boolean) isomorphic?(other)

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



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

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

- (String) n3

Return an n3 identifier for the Graph



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

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

- (Namespace) namespace(prefix)

Namespace for prefix



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

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

- (Hash{String => Namespace}) nsbinding



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

def nsbinding; @store.nsbinding; end

- (Array<Resource>) objects

List of distinct objects in graph



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

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

Parse source into Graph.

Merges results into a common Graph

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



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

- (Array<Resource>) predicates

List of distinct predicates in graph



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

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

- (String) prefix(namespace)

Prefix for namespace



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

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

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

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"]
}


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

- (String) qname(uri)

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



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



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

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

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



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

- (IO, String) serialize(options)

Serialize graph using specified serializer class.

Other options are parser specific.

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



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

- (Integer) size

Number of Triples in the graph



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

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

- (Array<Resource>) subjects

List of distinct subjects in graph



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



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

- (String) to_ntriples

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

Author:

  • Tom Morris



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

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

- (String) to_rdfxml

Exports the graph to RDF in RDF/XML form.



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

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

- (String) to_s

Output graph using to_ntriples



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

def to_s; self.to_ntriples; end

- (URIRef) type_of(subject)

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



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

- (Hash{URIRef => Namespace}) uri_binding



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

def uri_binding; @store.uri_binding; end