Class: RdfContext::Graph
Overview
A simple graph to hold triples.
Graphs store triples, and the namespaces associated with those triples, where defined
Direct Known Subclasses
Instance Attribute Summary collapse
-
#allow_n3 ⇒ Object
Returns the value of attribute allow_n3.
-
#identifier ⇒ Object
readonly
Returns the value of attribute identifier.
-
#store ⇒ Object
readonly
Returns the value of attribute store.
-
#triples(triple = Triple.new(nil, nil, nil), &block) ⇒ Array<Triple>
(also: #find)
readonly
Triples from graph, optionally matching subject, predicate, or object.
Instance Method Summary collapse
-
#<<(triple) ⇒ Graph
Adds an more extant triples to a graph.
-
#==(other) ⇒ Boolean
Two graphs are equal (==) if each other if they are both graphs have the same identifiers and the same size.
-
#[](item) ⇒ Triple
Indexed statement in serialized graph triples.
-
#add(*triples) ⇒ Graph
Adds one or more extant triples to a graph.
-
#add_seq(subject, predicate, objects) ⇒ Graph
Adds a list of resources as an RDF list by creating bnodes and first/rest triples.
-
#add_triple(subject, predicate, object) ⇒ Graph
Adds a triple to a graph directly from the intended subject, predicate, and object.
-
#bind(namespace) ⇒ Namespace
Bind a namespace to the graph.
-
#bnodes ⇒ Array<BNode>
Get all BNodes with usage count used within graph.
-
#close(commit_pending_transaction = false)
Close the graph store.
-
#commit
Commit changes to graph.
-
#contains?(triple) ⇒ Boolean
Check to see if this graph contains the specified triple.
- #context_aware? ⇒ Boolean
-
#destroy(configuration = nil)
Destroy the store identified by configuration if supported If configuration is nil, remove the graph context.
-
#get_by_type(object) ⇒ Array<Triple>
Get list of subjects having rdf:type == object.
-
#graph? ⇒ Boolean
Returns ‘false`, overridden in BNode.
-
#has_bnode_identifier?(bn) ⇒ Boolean
Detect the presence of a BNode in the graph, either as a subject or an object.
-
#hash ⇒ String
Hash of graph, based on graph type and identifier.
-
#initialize(options = {}) ⇒ Graph
constructor
Create a Graph with the given store and identifier.
- #inspect ⇒ Object
-
#isomorphic?(other) ⇒ Boolean
Two graphs are isomorphic if each is an instance of the other, considering BNode equivalence.
-
#merge!(graph)
Merge a graph into this graph.
-
#n3 ⇒ String
Return an n3 identifier for the Graph.
-
#namespace(prefix) ⇒ Namespace
Namespace for prefix.
- #nsbinding ⇒ Hash{String => Namespace}
-
#objects ⇒ Array<Resource>
List of distinct objects in graph.
-
#open(configuration = {})
Open the graph store.
-
#parse(stream, uri = nil, options = {}, &block) ⇒ Graph
Parse source into Graph.
-
#predicates ⇒ Array<Resource>
List of distinct predicates in graph.
-
#prefix(namespace) ⇒ String
Prefix for namespace.
-
#properties(subject, recalc = false) ⇒ Hash{String => Resource}
Resource properties.
-
#qname(uri) ⇒ String
QName for a URI Try bound namespaces, and if not found, try well-known namespaces.
-
#remove(triple)
Remove a triple from the graph.
-
#rollback
Rollback active transactions.
-
#seq(subject, predicate = RDF_NS.first) ⇒ Array<Resource>
Returns ordered rdf:_n objects or rdf:first, rdf:rest for a given subject.
-
#serialize(options) ⇒ IO, String
Serialize graph using specified serializer class.
-
#size ⇒ Integer
Number of Triples in the graph.
-
#subjects ⇒ Array<Resource>
List of distinct subjects in graph.
-
#sync_properties(subject)
Synchronize properties to graph.
-
#to_ntriples ⇒ String
Exports the graph to RDF in N-Triples form.
-
#to_rdfxml ⇒ String
Exports the graph to RDF in RDF/XML form.
-
#to_s ⇒ String
Output graph using to_ntriples.
-
#type_of(subject) ⇒ URIRef
Get type(s) of subject, returns a list of symbols.
- #uri_binding ⇒ Hash{URIRef => Namespace}
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
28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/rdf_context/graph.rb', line 28 def initialize( = {}) # Instantiate triple store @store = case [:store] when AbstractStore then [:store] when :list_store then ListStore.new when :memory_store then MemoryStore.new else ListStore.new end @allow_n3 = [:allow_n3] @identifier = Triple.coerce_node([:identifier]) || BNode.new end |
Instance Attribute Details
#allow_n3 ⇒ Object
Returns the value of attribute allow_n3.
9 10 11 |
# File 'lib/rdf_context/graph.rb', line 9 def allow_n3 @allow_n3 end |
#identifier ⇒ Object (readonly)
Returns the value of attribute identifier.
7 8 9 |
# File 'lib/rdf_context/graph.rb', line 7 def identifier @identifier end |
#store ⇒ Object (readonly)
Returns the value of attribute store.
8 9 10 |
# File 'lib/rdf_context/graph.rb', line 8 def store @store end |
Instance Method Details
#<<(triple) ⇒ Graph
Adds an more extant triples to a graph. Delegates to Store.
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
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
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.
253 254 255 256 257 258 259 260 261 262 |
# File 'lib/rdf_context/graph.rb', line 253 def add(*triples) = triples.last.is_a?(Hash) ? triples.pop : {} ctx = [: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.
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.
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.
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 |
#bnodes ⇒ Array<BNode>
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 |
#contains?(triple) ⇒ Boolean
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 |
#context_aware? ⇒ 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
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
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
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 |
#hash ⇒ String
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 |
#inspect ⇒ Object
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
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
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 |
#n3 ⇒ String
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(prefix) ⇒ Namespace
Namespace for prefix
181 |
# File 'lib/rdf_context/graph.rb', line 181 def namespace(prefix); @store.namespace(prefix); end |
#nsbinding ⇒ Hash{String => Namespace}
161 |
# File 'lib/rdf_context/graph.rb', line 161 def nsbinding; @store.nsbinding; end |
#objects ⇒ Array<Resource>
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 |
#parse(stream, uri = nil, options = {}, &block) ⇒ Graph
Parse source into Graph.
Merges results into a common Graph
561 562 563 564 |
# File 'lib/rdf_context/graph.rb', line 561 def parse(stream, uri = nil, = {}, &block) # :yields: triple @allow_n3 ||= [:allow_n3] Parser.parse(stream, uri, .merge(:graph => self), &block) end |
#predicates ⇒ Array<Resource>
List of distinct predicates in graph
198 |
# File 'lib/rdf_context/graph.rb', line 198 def predicates; @store.predicates(self); end |
#prefix(namespace) ⇒ String
Prefix for namespace
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
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
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 |
#seq(subject, predicate = RDF_NS.first) ⇒ Array<Resource>
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 |
#serialize(options) ⇒ IO, String
Serialize graph using specified serializer class.
Other options are parser specific.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/rdf_context/graph.rb', line 107 def serialize() serializer = case [:format].to_sym when AbstractSerializer then [: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 = [:io] || StringIO.new serializer.serialize(io, ) [:io] ? io : (io.rewind; io.read) end |
#size ⇒ Integer
Number of Triples in the graph
190 |
# File 'lib/rdf_context/graph.rb', line 190 def size; @store.size(self); end |
#subjects ⇒ Array<Resource>
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 |
#to_ntriples ⇒ String
Exports the graph to RDF in N-Triples form.
131 132 133 |
# File 'lib/rdf_context/graph.rb', line 131 def to_ntriples serialize(:format => :nt) end |
#to_rdfxml ⇒ String
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 |
#to_s ⇒ String
Output graph using to_ntriples
137 |
# File 'lib/rdf_context/graph.rb', line 137 def to_s; self.to_ntriples; end |