Class: Reddy::Graph

Inherits:
Object
  • Object
show all
Defined in:
lib/reddy/graph.rb

Overview

A simple graph to hold triples.

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

Direct Known Subclasses

ConjunctiveGraph

Instance Attribute Summary collapse

Instance Method Summary collapse

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

options[:store]

storage, defaults to a new ListStore instance

options[:identifier]

Identifier for this graph, Literal, BNode or URIRef

Parameters:

  • options:: (Hash)

    Options

Author:

  • Gregg Kellogg



30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/reddy/graph.rb', line 30

def initialize(options = {})
  @nsbinding = {}

  # 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                     MemoryStore.new
  end
  
  @identifier = options[:identifier] || BNode.new
end

Instance Attribute Details

#identifierObject (readonly)

Returns the value of attribute identifier.



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

def identifier
  @identifier
end

#nsbindingObject (readonly)

Data Store interface



51
52
53
# File 'lib/reddy/graph.rb', line 51

def nsbinding
  @nsbinding
end

#storeObject (readonly)

Returns the value of attribute store.



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

def store
  @store
end

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

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

Parameters:

  • triple:: (Triple, nil)

    Triple to match, may be a patern triple or nil

Returns:

  • (Array)

    List of matched triples



235
236
237
# File 'lib/reddy/graph.rb', line 235

def triples
  @triples
end

Instance Method Details

#<<(triple) ⇒ Graph

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

Example

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

Parameters:

  • t:: (Triple)

    the triple to be added to the graph

Returns:

  • (Graph)

    Returns the graph



202
203
204
205
# File 'lib/reddy/graph.rb', line 202

def << (triple)
  @store.add(triple, self)
  self
end

#[](item) ⇒ Object

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



175
# File 'lib/reddy/graph.rb', line 175

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

#add(*triples) ⇒ Graph

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

Example

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, ...)
options[:context]

Graph context in which to deposit triples, defaults to default_context or self

Parameters:

  • triples:: (Triple)

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

Returns:

  • (Graph)

    Returns the graph



219
220
221
222
223
224
# File 'lib/reddy/graph.rb', line 219

def add(*triples)
  options = triples.last.is_a?(Hash) ? triples.pop : {}
  ctx = options[:context] || @default_context || self
  triples.each {|t| @store.add(t, ctx)}
  self
end

#add_triple(subject, predicate, object) ⇒ Graph

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

Example

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; returns an array of g's triples

Parameters:

Returns:

  • (Graph)

    Returns the graph

Raises:

  • (Error)

    Checks parameter types and raises if they are incorrect.



187
188
189
190
# File 'lib/reddy/graph.rb', line 187

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

#bind(namespace) ⇒ Namespace

Bind a namespace to the graph.

Example

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

Parameters:

  • namespace:: (String)

    the namespace to bind

Returns:

  • (Namespace)

    The newly bound or pre-existing namespace.

Raises:



151
152
153
154
# File 'lib/reddy/graph.rb', line 151

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

#bnodesObject

Get all BNodes with usage count used within graph



257
258
259
# File 'lib/reddy/graph.rb', line 257

def bnodes
  @store.bnodes(self)
end

#close(commit_pending_transaction = false) ⇒ Object

Close the graph store

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



77
78
79
# File 'lib/reddy/graph.rb', line 77

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

#commitObject

Commit changes to graph



60
# File 'lib/reddy/graph.rb', line 60

def commit; @store.commit; end

#contains?(triple) ⇒ Boolean

Check to see if this graph contains the specified triple

Returns:

  • (Boolean)


252
253
254
# File 'lib/reddy/graph.rb', line 252

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

#context_aware?Boolean

Returns:

  • (Boolean)


48
# File 'lib/reddy/graph.rb', line 48

def context_aware?; @context_aware; end

#destroy(configuration = nil) ⇒ Object

Destroy the store identified by configuration if supported



54
55
56
57
# File 'lib/reddy/graph.rb', line 54

def destroy(configuration = nil)
  @store.destroy(configuration)
  self.freeze
end

#eql?(other) ⇒ Boolean Also known as: ==

Two graphs are equal 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

Returns:

  • (Boolean)


293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/reddy/graph.rb', line 293

def eql? (other)
  #puts "eql? size #{self.size} vs #{other.size}"
  return false if !other.is_a?(Graph) || self.size != other.size
  bn_self = bnodes.values.sort
  bn_other = other.bnodes.values.sort
  #puts "eql? bnodes '#{bn_self.to_sentence}' vs '#{bn_other.to_sentence}'"
  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.object.is_a?(BNode)
    #puts "eql? contains '#{t.to_ntriples}'"
    return false unless other.contains?(t)
  end
  true
end

#get_by_type(object) ⇒ Object

Get list of subjects having rdf:type == object

Parameters:

  • object:: (Resource, Regexp, String)

    Type resource



264
265
266
# File 'lib/reddy/graph.rb', line 264

def get_by_type(object)
  triples(Triple.new(nil, RDF_TYPE, object)).map {|t, ctx| t.subject}
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)


244
245
246
247
248
249
# File 'lib/reddy/graph.rb', line 244

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

#inspectObject



44
45
46
# File 'lib/reddy/graph.rb', line 44

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

#merge!(graph) ⇒ Object

Merge a graph into this graph

Raises:



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/reddy/graph.rb', line 269

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.object.is_a?(BNode)
      triple = triple.clone
      triple.subject = bn[triple.subject] if triple.subject.is_a?(BNode)
      triple.object = bn[triple.object] if triple.object.is_a?(BNode)
    end
    self << triple
  end
end

#namespace(prefix) ⇒ Object

Namespace for prefix



157
# File 'lib/reddy/graph.rb', line 157

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

#objectsObject

List of distinct objects in graph



172
# File 'lib/reddy/graph.rb', line 172

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

#open(configuration, create = false) ⇒ Object

Open the graph store

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



69
70
71
# File 'lib/reddy/graph.rb', line 69

def open(configuration, create=false)
  @store.open(configuration, create)
end

#predicatesObject

List of distinct predicates in graph



169
# File 'lib/reddy/graph.rb', line 169

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

#prefix(namespace) ⇒ Object

Prefix for namespace



160
# File 'lib/reddy/graph.rb', line 160

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

#remove(triple) ⇒ Object

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



228
# File 'lib/reddy/graph.rb', line 228

def remove(triple); @store.remove(triple, self); end

#rollbackObject

Rollback active transactions



63
# File 'lib/reddy/graph.rb', line 63

def rollback; @store.rollback; end

#sizeObject

Number of Triples in the graph



163
# File 'lib/reddy/graph.rb', line 163

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

#subjectsObject

List of distinct subjects in graph



166
# File 'lib/reddy/graph.rb', line 166

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

#to_ntriplesString

Exports the graph to RDF in N-Triples form.

Example

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 graph in N-Triples.

Author:

  • Tom Morris



90
91
92
93
94
# File 'lib/reddy/graph.rb', line 90

def to_ntriples
  triples.collect do |t|
    t.to_ntriples
  end * "\n" + "\n"
end

#to_rdfxmlString

Exports the graph to RDF in RDF/XML form.

Returns:

  • (String)

    The RDF/XML graph

Author:

  • Gregg Kellogg



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/reddy/graph.rb', line 105

def to_rdfxml
  replace_text = {}
  rdfxml = ""
  xml = builder = Builder::XmlMarkup.new(:target => rdfxml, :indent => 2)

  extended_bindings = nsbinding.merge(
    "rdf"   => RDF_NS,
    "rdfs"  => RDFS_NS,
    "xhv"   => XHV_NS,
    "xml"   => XML_NS
  )
  rdf_attrs = extended_bindings.values.inject({}) { |hash, ns| hash.merge(ns.xmlns_attr => ns.uri.to_s)}
  uri_bindings = extended_bindings.values.inject({}) { |hash, ns| hash.merge(ns.uri.to_s => ns.prefix)}
  xml.instruct!
  xml.rdf(:RDF, rdf_attrs) do
    # Add statements for each subject
    subjects.each do |s|
      xml.rdf(:Description, (s.is_a?(BNode) ? "rdf:nodeID" : "rdf:about") => s) do
        triples(Triple.new(s, nil, nil)) do |triple, context|
          xml_args = triple.object.xml_args
          if triple.object.is_a?(Literal) && triple.object.xmlliteral?
            replace_text["__replace_with_#{triple.object.object_id}__"] = xml_args[0]
            xml_args[0] = "__replace_with_#{triple.object.object_id}__"
          end
          xml.tag!(triple.predicate.to_qname(uri_bindings), *xml_args)
        end
      end
    end
  end

  # Perform literal substitutions
  replace_text.each_pair do |match, value|
    rdfxml.sub!(match, value)
  end
  
  rdfxml
end

#to_sObject

Output graph using to_ntriples



97
# File 'lib/reddy/graph.rb', line 97

def to_s; self.to_ntriples; end