How do I build a SPARQL list input using jena querybuilder?

962 Views Asked by At

I have a bunch of code that uses the Apache Jena querybuilder API (SelectBuilder class). I am trying to add a term like this to my existing SPARQL query:

        (?a ?b ?c) :hasMagicProperty ?this .

I have verified that this query works in TopBraid, but I can't figure out how to represent (?a, ?b, ?c) in the Jena API. What do I have to do to convert this list of Vars into a valid Jena resource node?

I am willing to explore alternate SPARQL-building frameworks, if they have robust support for typed literals, IRIs, and filters, as well as this list construct. I have skimmed over several other frameworks for building up SPARQL queries, but none of them seem to have a list construct.

Edit

My query building code (in Groovy) looks something like this:

def selectBuilder = new SelectBuilder()
selectBuilder.addPrefixes(...)
def thisVar = Var.alloc('this')
selectBuilder.addOptional(thisVar, 'rdf:type', ':MyEntity')

def aVar = Var.alloc('a')
def bVar = Var.alloc('b')
def cVar = Var.alloc('c')
List<Var> abc = [aVar, bVar, cVar]
//this doesn't work!!!
selectBuilder.addWhere(abc, ':hasMagicProperty', thisVar)
selectBuilder.addWhere(aVar, ':hasACode', 'code A')
selectBuilder.addWhere(bVar, ':hasBCode', 'code B')
selectBuilder.addWhere(cVar, ':hasCCode', 'code C')

def sparqlQuery = selectBuilder.buildString()

I have spent a couple of hours trying to work with the RDFList class, and I haven't figured it out. I'll keep trying, and see if I can grok it. In the meantime, any help would be appreciated. :)

Edit

Here is an unsuccessful attempt to use RDFList:

//this code does not work!
def varNode = NodeFactory.createVariable('a')
def model = ModelFactory.createDefaultModel()
def rdfNode = model.asRDFNode(varNode)

def rdfList = new RDFListImpl(model.createResource().asNode(), model)
//this line throws an exception!!
rdfList.add(rdfNode)
selectBuilder.addWhere(rdfList, ':hasMagicProperty', thisVar)

//com.hp.hpl.jena.shared.PropertyNotFoundException: http://www.w3.org/1999/02/22-rdf-syntax-ns#rest
2

There are 2 best solutions below

2
On

I wrote the queryBuilder and I don't think that in it's current state it will do what you want. Query builder is based on (but does not yet fully implement) the w3c SPARQL 1.1 recommendation: http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#rQuery

However, I think you can create your query using the Jena QueryFactory

String queryString = "SELECT * WHERE {  "+
  " OPTIONAL { ?this a :MyEntity } ."+
  " (?a ?b ?c) :hasMagicProperty ?result . "+
  " ?a :hasACode 'code A' . "+
  " ?b :hasACode 'code B' . "+
  " ?c :hasACode 'code C' ."+
  " }";
Query query = QueryFactory.create(queryString) ;

Unfortunately, I don't think this is what you really want. Notice that ?this is not bound to any of the other statements and so will produce a cross product of all :MyEntity type subjects with the ?a, ?b, ?c and `?result`` bindings.

If you can create the query with QueryFactory, I can ensure that QueryBuilder will support it.

UPDATE I have updated QueryBuilder (the next Snapshot should contain the changes). You should now be able to do the following:

Var aVar = Var.alloc('a')
Var bVar = Var.alloc('b')
Var cVar = Var.alloc('c')
selectBuilder.addWhere(selectBuilder.list(aVar, bVar, cVar),  ':hasMagicProperty', thisVar)
selectBuilder.addWhere(aVar, ':hasACode', 'code A')
selectBuilder.addWhere(bVar, ':hasBCode', 'code B')
selectBuilder.addWhere(cVar, ':hasCCode', 'code C')

If you can also simply add the standard text versions of values in the list parameters like:

selectBuilder.list( "<a>", "?b", "'c'" )
0
On

The following method is a workaround, using multiple triples to recursively build up the RDF list:

/*
 * Jena querybuilder does not yet support RDF lists. See:
 * http://www.w3.org/TR/2013/REC-sparql11-query-20130321/#collections
 */
private Node buildRdfCollection(SelectBuilder queryBuilder, List<?> itemList) {
    if (itemList.isEmpty()) {
        return RDF.nil.asNode()
    }

    def head = itemList.first()
    def rest = buildRdfCollection(queryBuilder, itemList.subList(1, itemList.size()))

    def listNode = NodeFactory.createAnon()
    queryBuilder.addWhere(listNode, RDF.first, head)
    queryBuilder.addWhere(listNode, RDF.rest, rest)
    return listNode
}

...

def listNode = buildRdfCollection(queryBuilder, abc)
queryBuilder.addWhere(listNode, ':hasMagicProperty', thisVar)

The generated SPARQL code looks like this:

_:b0      rdf:first             ?c ;
          rdf:rest              rdf:nil .
_:b1      rdf:first             ?b ;
          rdf:rest              _:b0 .
_:b2      rdf:first             ?a ;
          rdf:rest              _:b1 ;
          :hasMagicProperty  ?this .

This is a long-winded equivalent to:

(?a ?b ?c) :hasMagicProperty ?this .