Version: 0.0.1

Working With Graphs

Redleaf::Graph is the workhorse of Redleaf, the thing which you use to actually do stuff with statements.

Creating A Graph

You can create a simple in-memory graph via the usual Ruby constructor:

require 'redleaf'
Redleaf::Graph.new  # =>
Create an empty graph

It accepts a store argument, too, but that’ll come up a little later, so just create them without an argument for a bit.

Once you’ve created a new Graph, you’re ready to add some statements to it.

Adding Statements

There are several ways to add statements to a Graph: you can read data in from some pre-existing source, copy them from other graph objects, or create new statements yourself. You’ve probably already seen how to create statements in the chapter on Statements, so let’s do that first.

The way to add existing statement objects to a Graph is via its #append method:

require 'redleaf'
include Redleaf::Constants::CommonNamespaces

myfoaf = Redleaf::Namespace.new( 'http://deveiate.org/foaf.xml#' )
st1 = Redleaf::Statement.new( myfoaf[:me], RDF[:type], FOAF[:Person] )
st2 = Redleaf::Statement.new( myfoaf[:me], FOAF[:name], "Michael Granger" )
st3 = Redleaf::Statement.new( myfoaf[:me], FOAF[:homepage], URI('http://deveiate.org/') )

graph = Redleaf::Graph.new
graph.append( st1, st2, st3 )
# => 
graph.statements
# =>
Appending with #append

The append method is also known as the #<< operator, and because it returns the receiving object, you can chain as many statements together as you want. The above example could be written like this instead:

require 'redleaf'
include Redleaf::Constants::CommonNamespaces

myfoaf = Redleaf::Namespace.new( 'http://deveiate.org/foaf.xml#' )

graph = Redleaf::Graph.new
graph <<
	Redleaf::Statement.new( myfoaf[:me], RDF[:type], FOAF[:Person] ) <<
	Redleaf::Statement.new( myfoaf[:me], FOAF[:name], "Michael Granger" ) <<
	Redleaf::Statement.new( myfoaf[:me], FOAF[:homepage], URI('http://deveiate.org/') )

graph.statements
# =>
Appending with the << operator

Appending With Syntax Shortcuts

The #append method (and its sidekick the #<< operator) also support several shortcut syntaxes so you don’t have to do all those Redleaf::Statement.new calls. If you want to append one or more simple statements, you can just send in three-element Arrays instead of Redleaf::Statement objects, and they’ll be converted on the fly:

require 'redleaf'
graph = Redleaf::Graph.new
exstaff = Redleaf::Namespace[ 'http://www.example.org/staffid/' ]
exterms = Redleaf::Namespace[ 'http://www.example.org/terms/' ]

graph <<
	[ exstaff["85740"],  exterms[:address],    :johnaddress ] <<
	[ :johnaddress,      exterms[:street],     "1501 Grant Avenue" ] <<
	[ :johnaddress,      exterms[:city],       "Bedford" ] <<
	[ :johnaddress,      exterms[:state],      "Massachusetts" ] <<
	[ :johnaddress,      exterms[:postalCode], "01730" ]

graph.statements
# =>
Appending triples as Arrays

Loading Serialized RDF

Chances are you’ll quickly grow bored of inserting all those example statements, and will want to load a useful set of triples to work with. You can load statements from serialized RDF via the Redleaf::Graph#load method. It supports several different URL schemes, including http and file, so you can load triples from a remote resource or from a local file:

graph = Redleaf::Graph.new
graph.load( 'http://deveiate.org/foaf.xml' )
graph.load( 'file:spec/data/mgranger-foaf.xml' )
Loading statements with http and file URLs

Loading RDF Data From A String

If you have some RDF data in a String and need to be able to load it directly, you can do so with a Redleaf::Parser. You can read more about parsers in the Parsing RDF chapter.

Searching A Graph

Now that you’ve loaded a bunch of statements into a Graph, you’re probably wondering how you can go about finding them again.

The #search Method

The easiest way is to use the #search method, which takes a subject, a predicate, and an object that specify what triples to search for. Any of those three that are nil will be treated as a wildcard, and will match any value for that part of the triples it finds.

require 'redleaf'
graph = Redleaf::Graph.new
exstaff = Redleaf::Namespace[ 'http://www.example.org/staffid/' ]
exterms = Redleaf::Namespace[ 'http://www.example.org/terms/' ]

graph <<
	[ exstaff["85740"],  exterms[:address],    :johnaddress ] <<
	[ :johnaddress,      exterms[:street],     "1501 Grant Avenue" ] <<
	[ :johnaddress,      exterms[:city],       "Bedford" ] <<
	[ :johnaddress,      exterms[:state],      "Massachusetts" ] <<
	[ :johnaddress,      exterms[:postalCode], "01730" ]

graph.search( nil, exterms[:city], 'Bedford' )
# =>
Searching for triples.

The #search method is also aliased to #[], so searching a graph looks much the same as appending a triple to it:

require 'redleaf'
graph = Redleaf::Graph.new
exstaff = Redleaf::Namespace[ 'http://www.example.org/staffid/' ]
exterms = Redleaf::Namespace[ 'http://www.example.org/terms/' ]

graph <<
	[ exstaff["85740"],  exterms[:address],    :johnaddress ] <<
	[ :johnaddress,      exterms[:street],     "1501 Grant Avenue" ] <<
	[ :johnaddress,      exterms[:city],       "Bedford" ] <<
	[ :johnaddress,      exterms[:state],      "Massachusetts" ] <<
	[ :johnaddress,      exterms[:postalCode], "01730" ]

graph[ nil, exterms[:postalCode], nil ]
# =>
Searching for triples with #[].

Graphs are Enumerable

Redleaf::Graph mixes in Enumerable, so you can use any of the methods it adds to work with the Graph’s statements:

require 'redleaf'
include Redleaf::Constants::CommonNamespaces
graph = Redleaf::Graph.new
graph.load( 'http://deveiant.livejournal.com/data/foaf' )

graph.any? {|stmt| stmt.object == FOAF[:Person] }
graph.collect {|stmt| stmt.predicate }.uniq

counts = Hash.new( 0 )
graph.inject(counts) {|counts,stmt| counts[stmt.predicate] += 1; counts }
Silly Enumerable tricks.

Node-Match Methods

Redleaf also provides optimized matchers for particular situations, see the API documentation for more information on : has_predicate_about?, has_predicate_entailing?, include_object?, include_subject?, object, objects, predicate, predicates, predicates_about, predicates_entailing, subject, and subjects.

Querying With SPARQL

If you require a more exact way to search your graph, Redland also comes with a query engine for the SPARQL Query Language.

require 'redleaf'
A simple SPARQL query.

There are four kinds of responses you can expect from a query, all of which are specializations of the Redleaf::QueryResult class; which one will be returned depends on what kind of query you use:

SELECT Queries

From the W3C recommendation:

The SELECT form of results returns variables and their bindings directly. The syntax SELECT * is an abbreviation that selects all of the variables in a query.

The bindings are returned via a Redleaf::BindingQueryResult:

require 'redleaf'
graph = Redleaf::Graph.new
include Redleaf::Constants::CommonNamespaces

graph <<
	[ :_a, FOAF[:name],   "Alice" ] <<
	[ :_a, FOAF[:knows],  :_b     ] <<
	[ :_a, FOAF[:knows],  :_c     ] <<
	[ :_b, FOAF[:name],   "Bob"   ] <<
	[ :_c, FOAF[:name],   "Clare" ] <<
	[ :_c, FOAF[:nick],   "CT"    ]

sparql = %|
  SELECT ?nameX ?nameY ?nickY
	WHERE
	  { ?x foaf:knows ?y ;
	       foaf:name ?nameX .
	    ?y foaf:name ?nameY .
	    OPTIONAL { ?y foaf:nick ?nickY }
	  }
|

result = graph.query( sparql, :foaf => FOAF )
# => result.length 
# => result.rows
# =>
A simple SELECT SPARQL query.

CONSTRUCT Queries

Again, from the W3C recommendation:

The CONSTRUCT query form returns a single RDF graph specified by a graph template. The result is an RDF graph formed by taking each query solution in the solution sequence, substituting for the variables in the graph template, and combining the triples into a single RDF graph by set union.

Returns a Redleaf::GraphQueryResult

ASK Queries

Returns a Redleaf::BooleanQueryResult

DESCRIBE Queries

Returns a Redleaf::SyntaxQueryResult

These four result types have different result data (e.g., BindingQueryResult objects have bindings which map to keywords in the query, whereas BooleanQueryResult objects just contain true or false). All of them support the Enumerable interface, however. See the individual API documentation for more on how to use the result type you’re expecting.

Formatting Query Results

Query results also support convenient exporting in a few different formats, which makes it easy to set up a SPARQL service that can return results formatted appropriately via content negotiation.

The formats supported by your installation of librdf can be queried via Redleaf::QueryResult.formatters:

pp Redleaf::QueryResult.formatters
# => {"xml"=>
# 	  {:mimetype=>"application/sparql-results+xml",
# 	   :uri=>#<URI::HTTP:0x111ef1c URL:http://www.w3.org/2005/sparql-results#>,
# 	   :label=>"SPARQL Query Results Format 2007-06-14"},
# 	 "json"=>
# 	  {:mimetype=>"text/json",
# 	   :uri=>
# 	    #<URI::HTTP:0x111ebe8 URL:http://www.w3.org/2001/sw/DataAccess/json-sparql/>,
# 	   :label=>"JSON"}}
Checking available query result formatters.

There are convenience methods (#to_json and #to_xml) for formatting results as well:

require 'redleaf'
require 'redleaf/constants'
include Redleaf::Constants::CommonNamespaces

# Example from http://www.w3.org/TR/rdf-sparql-query/#constructGraph
ORG = Redleaf::Namespace.new( 'http://example.com/ns#' )

graph = Redleaf::Graph.new
graph << 
	[:a,  ORG[:employeeName],   "Alice"] <<
	[:a,  ORG[:employeeId],     12345 ]  <<
	[:b,  ORG[:employeeName],   "Bob" ]  <<
	[:b,  ORG[:employeeId],     67890 ]

sparql = <<END_SPARQL
CONSTRUCT { ?x foaf:name ?name }
WHERE  { ?x org:employeeName ?name }
END_SPARQL

result = graph.query( sparql, :foaf => FOAF, :org => ORG )
# => result.to_json.gsub( /\s+/, ' ' )
# =>
Getting search results as JSON.

Persistent Graphs via Redleaf::Store

Persistent Graphs via Serialization