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
Accept
header for service content negotiation