Skip to content

Soprano SPARQL Queries in Ruby

Tuesday, 18 March 2008  |  richard dale

I've added bindings to the KDE 4.1 trunk for using Soprano with Ruby. It allows you to add and remove statements from the Soprano RDF database and to make SPARQL queries over D-Bus. Also included is an optional adaptor to use ActiveRDF with Soprano.

See Sebastian Trüg's blog Fetch, Nepomuk, fetch! for an introduction to what Soprano is about. There is some more info on the TechBase wiki the Nepomuk Server and Advanced Queries.

Here is how you connect to Soprano:

require 'korundum4'
client = Soprano::Client::DBusClient.new("org.soprano.Server"")
model = client.createModel("main")

subject = Soprano::Node.new(Qt::Url.new("file:///home/rdale/play/tryactiverdf/schema.nt#Person")) predicate = Soprano::Node.new(Qt::Url.new("http://www.w3.org/2000/01/rdf-schema#comment")) object = Soprano::Node.new(Soprano::LiteralValue.new("Person Class"))

statement = Soprano::Statement.new(subject, predicate, object) model.addStatement(statement)

statements = model.listStatements(Soprano::Statement.new(Soprano::Node.new, Soprano::Node.new, Soprano::Node.new)) statements.each do |statement| p statement.subject p statement.predicate p statement.object end

An empty node created with 'Soprano::Node.new' acts like a wild card when listing the statements in the model. You can make queries on the database like this:

query = getFancyQueryString()
binding_set = model.executeQuery( query,
                                  Soprano::Query::QueryLanguageSparql )

binding_set.each do |binding| value = binding.bindingByName("someVariableName") end

Note that you can iterate through the result set using the Ruby 'each' method as the Soprano::Client::DBusQueryResultIterator class is Enumerable and will return each set of bindings in turn to a block you provide. Here is an extract the code to do that:

class DBusQueryResultIterator
  include Enumerable

def initialize(serviceName, dbusObject) @interface = Qt::DBusInterface.new("org.soprano.Server", dbusObject, "org.soprano.QueryResultIterator") end

def each while @interface.next do reply = @interface.current set = Soprano::BindingSet.demarshall(reply) yield set end end

... end

Each set of bindings is demarshalled from D-Bus and then the 'yield' keyword passes control to the block given to the 'each' method. The way that Soprano handles iteration over D-Bus is interesting - instead of sending all the results over in one big message, it sends you the name of a D-Bus object called something like '/org/soprano/Server/models/main/iterator147', which is an instance of the 'org.soprano.QueryResultIterator' interface. Then you connect to that dbus object and call 'next()' and 'current()' on it, to obtain each binding set in turn, as in the Ruby code above. I did hit a problem with demarshalling QDBusArguments that I've reported to the Qt-bugs mailing list, but I've worked round it for the current code in the svn at the expense of needing a D-Bus call for each binding value, instead of for each binding set.

Soprano includes a Domain Specific Language (DSL) for using with RDF and SPARQL in C++ code. The ActiveRDF project does something very similar for use with Ruby, and so I've included an optional gem to allow activerdf to be combined with soprano. Download and install activerdf as a gem with a 'sudo gem install activerdf' command, the build and install the activerdf-soprano gem as follows:

$ rake package
$ sudo gem install pkg/activerdf_soprano-0.7.gem --local

Then use activerdf like this:

require 'activerdf_soprano/soprano'

pool = ConnectionPool.add_data_source :type => :soprano, :model => "NewModel" query = Query.new.select(:subject,:predicate,:object).where(:subject,:predicate,:object) res = query.execute

This query will retrieve all the statements from a model called 'NewModel', with the SPARQL query string "SELECT ?subject ?predicate ?object WHERE { ?subject ?predicate ?object . } ". See the ActiveRDF site for more details about the query language and how to add RDF namespaces (which map nicely onto Ruby classes).