Parameterized SPARQL query with JENA - java

I'm trying to build a small semantic web application using Jena framework, JSP and JAVA. I have a remote SPARQL endpoint and I've already written a simple query which works fine but now I need to use some parameters. Here is my code so far:
final static String serviceEndpoint = "http://fishdelish.cs.man.ac.uk/sparql/";
String comNameQuery =
"PREFIX fd: <http://fishdelish.cs.man.ac.uk/rdf/vocab/resource/> " +
"SELECT ?name ?language ?type" +
"WHERE { ?nameID fd:comnames_ComName ?name ;" +
"fd:comnames_Language ?language ;" +
"fd:comnames_NameType ?type ." +
"}";
Query query = QueryFactory.create(comNameQuery);
QueryExecution qe = QueryExecutionFactory.sparqlService(serviceEndpoint,query);
try {
ResultSet rs = qe.execSelect();
if ( rs.hasNext() ) {
System.out.println(ResultSetFormatter.asText(rs));
}
}
catch(Exception e) {
System.out.println(e.getMessage());
}
finally {
qe.close();
}
What I want to do is to parameterized ?name. I'm new to Jena and I'm not really sure how to use parameters in a SPARQL query. I would appreciate it if someone could help me with this.

If you just want to restrict a variable to have a certain value for local queries you can do so with an overload of the QueryFactory.create() method which takes a QuerySolutionMap to set value restrictions. Note this doesn't alter your query just restricts the final results so this is not really parameterization.
If you want to actually have true parameterized queries (i.e. substitute variables for constants) then there are a couple of ways to do this depending on your version of ARQ.
Using any current release (up to 2.9.0) the only way to do it is string concatenation i.e. instead of having ?name in your query you would just insert the value you want e.g. "Bob"
Using the latest trunk (2.9.1-SNAPSHOT onwards) there is a new ParameterizedSparqlString class which makes this much more user friendly e.g.
ParameterizedSparqlString queryStr = new ParameterizedSparqlString(comNameQuery);
queryStr.setLiteral("name", "Bob");
Query query = QueryFactory.create(queryStr.toString());
And in fact you can simplify your code further since ParameterizedSparqlString has a StringBuffer style interface and can be used to build your query bit by bit and includes useful functionality like prepending prefixes to your query.
The advantage of this new method is that it provides a more generic way of doing parameterized queries that can also be used with updates and is usable for preparing remote queries which the existing methods do not cover.

You could try looking into Twinkql. It is a SPARQL-to-Java mapping framework. It uses Jena in the back end, but tries to simplify SPARQL queries and Java binding of the results.
It allows you to define SPARQL queries in xml:
<select id="getNovel" resultMap="novelResultMap">
<![CDATA[
SELECT ?novel ?author
WHERE {
?novel a <http://dbpedia.org/class/yago/EnglishNovels> ;
<http://dbpedia.org/property/name> "#{novelName}"#en ;
<http://dbpedia.org/property/author> ?author .
}
]]>
</select>
Note the #{novelName} placeholder -- this is where parameters can be passed in at query time.
Also, results can be bound to Java Beans:
<resultMap id="novelResultMap" resultClass="org.twinkql.example.Novel">
<uniqueResult>novel</uniqueResult>
<rowMap var="novel" varType="localName" beanProperty="name" />
<rowMap var="author" varType="localName" beanProperty="author"/>
</resultMap>
There is an API to call these queries, to pass in parameters, etc. It is much like MyBatis, but for SPARQL instead of SQL.

Related

Handling dynamic queries with Spring

The problem I'm trying to solve here is, filtering the table using dynamic queries supplied by the user.
Entities needed to describe the problem:
Table: run_events
Columns: user_id, distance, time, speed, date, temperature, latitude, longitude
The problem statement is to get the run_events for a user, based on a filterQuery.
Query is of the format,
((date = '2018-06-01') AND ((distance < 20) OR (distance > 10))
And this query can combine multiple fields and multiple AND/OR operations.
One approach to solving this is using hibernate and concatenating the filterQuery with your query.
"select * from run_events where user_id=:userId and "+filterQuery;
This needs you to write the entire implementation and use sessions, i.e.
String q = select * from run_events where user_id=:userId and "+filterQuery;
Query query = getSession().createQuery(q);
query.setParameter("userId", userId);
List<Object[]> result = query.list();
List<RunEvent> runEvents = new ArrayList<>();
for(Object[] obj: result){
RunEvent datum = new RunEvent();
int index = -1;
datum.setId((long) obj[++index]);
datum.setDate((Timestamp) obj[++index]);
datum.setDistance((Long) obj[++index]);
datum.setTime((Long) obj[++index]);
datum.setSpeed((Double) obj[++index]);
datum.setLatitude((Double) obj[++index]);
datum.setLongitude((Double) obj[++index]);
datum.setTemperature((Double) obj[++index]);
runEvents.add(datum);
}
This just doesn't seem very elegant and I want to use the #Query annotation to do this i.e.
#Query(value = "select run_event from RunEvent where user_id = :userId and :query order by date asc")
List<RunEvent> getRunningData(#Param("userId") Long userId,
#Param("query") String query,
);
But this doesn't work because query as a parameter cannot be supplied that way in the query.
Is there a better, elegant approach to getting this done using JPA?
Using Specifications and Predicates seems very complicated for this sort of a query.
To answer the plain question: This is not possible with #Query.
It is also in at least 99% of the cases a bad design decision because constructing SQL queries by string concatenation using strings provided by a user (or any source not under tight control) opens you up for SQL injection attacks.
Instead you should encode the query in some kind of API (Criteria, Querydsl, Query By Example) and use that to create your query. There are plenty of questions and answers about this on SO so I won't repeat them here. See for example Dynamic spring data jpa repository query with arbitrary AND clauses
If you insist on using a SQL or JPQL snippet as input a custom implementation using String concatenation is the way to go.
This opens up attack for SQL injection. Maybe that’s why this feature is not possible.
It is generally a bad idea to construct query by appending random filters at the end and running them.
What if the queryString does something awkward like
Select * from Foo where ID=1234 or true;
thereby returning all the rows and bringing a heavy load on DB possibly ceasing your whole application?
Solution: You could use multiple Criteria for filtering it dynamically in JPA, but you’ll need to parse the queryString yourself and add the necessary criteria.
You can use kolobok and ignore fields with null values.
For example create one method like bellow
findByUserIdAndDistanceaLessThanAndDistancebGreaterThan....(String userid,...)
and call that method only with the filter parameters while other parameters are null

sparql using live.dbpedia and getting XML Schema in the result

I'm adventuring with sparql and a java application, I've found a few connection basics to get up and running as it were but fear I'm making a mistake which will later turn into something worse.
Every connection suggestion regardless of the library used says to connect to "http://dbpedia.org/sparql/" yet this doesn't work for me.
I checked the url that is returned when I run a query using the online editor and noticed the live prefix, so I added that as my connection string, and it works. That is to say, my connection string looks like "http://live.dbpedia.org/sparql"
And it does return the result, however, the result has the XML Schema attached which is making me wonder whether or not it's because of this live. I've added in.
Below is the simple connection code I'm using, is this correct? Any and all help greatly appreciated thank you.
If the 'live' is correct, is it possible to extra the just the value wihtout the Schema?
StringBuilder sb = new StringBuilder();
sb.append("PREFIX dbr: <http://dbpedia.org/resource/> \n");
sb.append("PREFIX dbp: <http://dbpedia.org/property/> \n");
sb.append("PREFIX dbo: <http://dbpedia.org/ontology/> \n");
sb.append("SELECT ?dob \n");
sb.append("WHERE {dbr:Tony_Blair dbp:birthDate ?dob} \n");
Query query = QueryFactory.create(sb.toString());
QueryExecution qexec = QueryExecutionFactory.sparqlService("http://live.dbpedia.org/sparql", query);
try {
ResultSet results = qexec.execSelect();
for ( ; results.hasNext() ; )
{
QuerySolution soln = results.nextSolution() ;
System.out.println(soln.get("?dob"));
}
the result being:
1953-05-06^^http://www.w3.org/2001/XMLSchema#date
Well the result as you show it is missing some brackets and quotes, but I assume that is caused by how you copy-pasted it. More usually it would look like this:
"1953-05-06"^^<http://www.w3.org/2001/XMLSchema#date>
But in essence your query and code is correct. The "attached XML Schema" here is the datatype of the returned literal string.
An RDF literal consists of a lexical value (in your case "1953-05-06") and a datatype (in your case http://www.w3.org/2001/XMLSchema#date). It can also, optionally have a language tag e.g. "colour"#en-UK.
If you wish to remove the datatype from the result and only retrieve the lexical value, you can use the STR() function as part of the SELECT clause in your query:
SELECT (STR(?dob) as ?date_of_birth)
As for the connection string that you are struggling with: there are two separate DBPedia endpoints. The "regular" one is http://dbpedia.org/sparql (no trailing slash) - this queries a static dataset that is synced/updated with Wikipedia changes every so 6 months or so. The "live" endpoint, http://live.dbpedia.org/sparql, is an effort to have a more up-to-date dataset ready for querying. See https://wiki.dbpedia.org/online-access/DBpediaLive for more details.

writing a basic n1ql query in java

I have just started learning Couchbase. I am trying to write a basic query using java sdk but I am not able to understand how to write it. Below is the query:
SELECT *
FROM users_with_orders usr
JOIN orders_with_users orders
ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END
This is for joining without array:
LetPath path = select("*,META(usr).id as _ID,META(usr).cas as _CAS).from(bucketName +" usr").join(bucketname +" orders").onKeys("usr.order_id)
How should I proceed with the above query for on keys array?
Thanks!!!!
As described in the docs on Querying from the SDK, you can use either a simple string with the Java SDK or use the DSL. For example:
// query with a simple string
System.out.println("Simple string query:");
N1qlQuery airlineQuery = N1qlQuery.simple("SELECT `travel-sample`.* FROM `travel-sample` WHERE name=\"United Airlines\" AND type=\"airline\"");
N1qlQueryResult queryResult = bucket.query(airlineQuery);
for (N1qlQueryRow result: queryResult) {
System.out.println(result.value());
}
//query with a parameter using the DSL
System.out.println("Parameterized query using the DSL:");
Statement statement = select(path(i("travel-sample"), "*")).from(i("travel-sample")).where(x("name").eq(x("$airline_param")).and(x("type").eq(s("airline"))));
JsonObject placeholderValues = JsonObject.create().put("airline_param", "United Airlines");
N1qlQuery airlineQueryParameterized = N1qlQuery.parameterized(statement, placeholderValues);
N1qlQueryResult queryResultParameterized = bucket.query(airlineQueryParameterized);
for (N1qlQueryRow row : queryResultParameterized) {
System.out.println(row);
}
(I posted a full gist of this example for the imports, etc.)
See the docs for more info, but you may want to use the DSL to allow IDE code completion and Java compile time checking. When developing an interactive web application, you'll probably also want to use parameterized statements (for security) and may even want prepared statements (for performance).

How to print the retrieved statements in SPARQL after executing a query

I am quite new to Java Sesame and SPARQL. I have added statements into the Sesame repository and am now trying to retrieve some statements and simply print them. From what I understood that I need to parse the query in order to print the retrieved statements. The code below shows where I have reached so far:
String queryString = "SELECT ?subject ?object WHERE { ?subject <http://example.org/is> ?object . } LIMIT 1";
...
SPARQLParser parser = new SPARQLParser();
ParsedQuery query = parser.parseQuery(queryString, null);
StatementPatternCollector collector = new StatementPatternCollector();
query.getTupleExpr().visit(collector);
List<StatementPattern> patterns = collector.getStatementPatterns();
// To print the first statement only for example.
System.out.println(patterns.get(0));
Here is the output:
StatementPattern
Var (name=name)
Var (name=-const-1, value=http://example.org/is, anonymous)
Var (name=object)
According to the output, it does not show me the subject and object. My questions are: How can I print the results of the query as shown above. Is this code the right way to parse the query for printing the statements later. Your help would be very much appreciated.
You get the results of a query by evaluating the query, not by parsing it. What your code does is inspect the query's algebra model and retrieve patterns from that algebra. This approach will not give you query results.
Evaluating a query in Sesame is far simpler than this:
// open a connection to the Sesame repository containing your statements
RepositoryConnection conn = repository.getConnection();
try {
// create a prepared query object from your query string
TupleQuery query = conn.prepareTupleQuery(QueryLanguage.SPARQL, queryString);
// evaluate the query on the repository
TupleQueryResult result = query.evaluate();
// iterate over the results and do something with each result
while (result.hasNext()) {
BindingSet s = result.next();
Value subject = s.getValue("subject");
Value object = s.getValue("object");
System.out.println("value of ?subject: " + subject);
System.out.println("value of ?object: " + object);
}
} finally {
conn.close();
}
As Joshua pointed out, your query retrieves variable bindings (for the variables mentioned in your SELECT clause), not RDF statements. You can of course re-create the RDF statement from those variable bindings in Java, but if you really want full statements rather than just the subject and object, it's probably easier to switch to using a SPARQL CONSTRUCT query instead of a SELECT query.
For more information on how to evaluate different kinds of queries and work with the results in Sesame, see the Sesame user documentation (section 6.5 in particular) and the API Javadoc.

How to build SPARQL queries in java?

Is there a library, which is able to build SPARQL queries programmatically like the CriteriaBuilder in JPA or to build the queries like with a PreparedStatement for SQL?
Similar (for SQL): Cleanest way to build an SQL string in Java
You can build queries programmatically in Jena using two methods: syntax or algebra. There's an introduction in the jena wiki.
Using the algebra you'd do something like:
Op op;
BasicPattern pat = new BasicPattern(); // Make a pattern
pat.add(pattern); // Add our pattern match
op = new OpBGP(pat); // Make a BGP from this pattern
op = OpFilter.filter(e, op); // Filter that pattern with our expression
op = new OpProject(op, Arrays.asList(Var.alloc("s"))); // Reduce to just ?s
Query q = OpAsQuery.asQuery(op); // Convert to a query
q.setQuerySelectType(); // Make is a select query
(taken from the wiki page)
It's not CriteriaBuilder (nor was it intended to be), but is some of the way there. You OpJoin rather than AND, OpUnion when you want to OR, etc. The pain points are expressions in my experience: you probably want to parse them from a string.
The recent versions of Jena have added a StringBuilder style API for building query/update strings and parameterizing them if desired.
This class is called ParameterizedSparqlString, here's an example of using it to create a query:
ParameterizedSparqlString queryStr = new ParameterizedSparqlString();
queryStr.setNSPrefix("sw", "http://skunkworks.example.com/redacted#");
queryStr.append("SELECT ?a ?b ?c ?d");
queryStr.append("{");
queryStr.append(" ?rawHit sw:key");
queryStr.appendNode(someKey);
queryStr.append(".");
queryStr.append(" ?rawHit sw:a ?a .");
queryStr.append(" ?rawHit sw:b ?b .");
queryStr.append(" ?rawHit sw:c ?c . ");
queryStr.append(" ?rawHit sw:d ?d .");
queryStr.append("} ORDER BY DESC(d)");
Query q = queryStr.asQuery();
Disclaimer - I'm the developer who contributed this functionality to Jena
See What's the best way to parametize SPARQL queries? for more discussion on doing this across various APIs.
I implemented SPARQL Java - a kind of DSL for writing SPARQL queries in Java.
It solves the problem with IDE's auto formatting of concatenated SPARQL query strings and things like that.
As for example:
String shortQuery = Q.prefix("books", "http://example.org/books#")
.select("?book ?authorName", new where() {
{
$("?book books:author ?author");
$("?author books:authorName ?authorName");
}
}).get();
I recently started to use Sesame query builder. It looks promising except it doesn't provide much documentation and I struggled to find examples. Here is simple sample which may help you to get started:
ParsedTupleQuery query = QueryBuilderFactory
.select("pubProperty", "pubPropertyValue")
.group()
.atom(cmResource(resourceId), LinkPublicationsTransformation.REFERENCE_URI, "pubUri")
.atom("pubUri", "pubProperty", "pubPropertyValue")
.filter(isLiteral("pubPropertyValue"))
.closeGroup()
.query();
Just note that isLiteral and cmResource are my own little static helper classes. isLiteral stands for new IsLiteral(new Var("...")) for example where the latter one create URI with my heavily used prefix.
You might be then also interested in SPARQLQueryRenderer which can turn ParsedQuery into String which may be convenient for further usage.
If you end up using String(Builder) approach what I discourage you to do have at least a look on RenderUtils from sesame-queryrendered which has all the convenient methods to add < > around URIs, escape special characters etc.
The Eclipse RDF4J framework (the successor of Sesame) offers a Repository API which is somewhat similar to JDBC - it allows you to create a prepared Query object and inject variable bindings before executing it:
String query = "SELECT * WHERE {?X ?P ?Y }";
TupleQuery preparedQuery = conn.prepareQuery(QuerLanguage.SPARQL, query);
preparedQuery.setBinding("X", someValue);
...
TupleQueryResult result = preparedQuery.evaluate();
In addition, RDF4J has a SparqlBuilder (originally known as spanqit) - a Java DSL for SPARQL which allows you to create SPARQL queries in code like this:
query.prefix(foaf).select(name)
.where(x.has(foaf.iri("name"), name))
.orderBy(name)
.limit(5)
.offset(10);
I have just released a beta project to do just this, called Spanqit.
I strove for readability and an intuitive interface, for example, here is some example Spanqit syntax for creating a query:
query.prefix(foaf).select(name)
.where(x.has(foaf.iri("name"), name))
.orderBy(name)
.limit(5)
.offset(10);
Check it out, and feel free to comment and suggest improvements!
Jena provides a QueryBuilder in the Extras package.
https://jena.apache.org/documentation/extras/querybuilder/index.html
It does what you want.
You can use the Jena Semantic Framework (SPARQL documentation). Also take a look at this related question. Sadly, its syntax is closer to a SQL PreparedStatement than to the JPA.

Categories