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 # =>
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
# =>
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 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 # =>
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' )
http and file URLsLoading 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' ) # =>
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 ] # =>
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 }
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'
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
# =>
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"}}
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+/, ' ' )
# =>
- Future goals
- DSL
- Sequel-like datasets
- Construction of queries via a DSL
- Sequel-like datasets
- Support for altenative query syntaxes like RDQL and MQL.
Persistent Graphs via Redleaf::Store
- Stores can be used to persist graphs
- You implicitly get a MemoryHashesStore whenever you use a Graph by itself
- You can upgrade at any time
- …or you can start out with a store and get the associated Graph
- See the Triple Stores chapter for more specifics
Persistent Graphs via Serialization
- Serializers turn a graph into text in one of several different formats
- Which ones are built in, and how to find out which ones are supported
- How to access the serializer you want
- Generate an appropriate
Acceptheader for service content negotiation