Skip to content

Commit 3b20b53

Browse files
author
Andrew Woods
committed
Create SPARQL datasource
- Starting from 'tdb' example Resolves: LinkedDataFragments#57
1 parent cb5c8d7 commit 3b20b53

File tree

4 files changed

+377
-0
lines changed

4 files changed

+377
-0
lines changed
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package org.linkeddatafragments.datasource.sparql;
2+
3+
import java.io.File;
4+
import org.apache.jena.query.Dataset;
5+
import org.apache.jena.query.Query;
6+
import org.apache.jena.query.QueryExecution;
7+
import org.apache.jena.query.QueryExecutionFactory;
8+
import org.apache.jena.query.QueryFactory;
9+
import org.apache.jena.query.QuerySolution;
10+
import org.apache.jena.query.QuerySolutionMap;
11+
import org.apache.jena.query.ResultSet;
12+
import org.apache.jena.query.Syntax;
13+
import org.apache.jena.rdf.model.Literal;
14+
import org.apache.jena.rdf.model.Model;
15+
import org.apache.jena.rdf.model.ModelFactory;
16+
import org.apache.jena.rdf.model.RDFNode;
17+
import org.apache.jena.tdb.TDBFactory;
18+
19+
import org.linkeddatafragments.datasource.AbstractRequestProcessorForTriplePatterns;
20+
import org.linkeddatafragments.datasource.IFragmentRequestProcessor;
21+
import org.linkeddatafragments.fragments.ILinkedDataFragment;
22+
import org.linkeddatafragments.fragments.tpf.ITriplePatternElement;
23+
import org.linkeddatafragments.fragments.tpf.ITriplePatternFragmentRequest;
24+
25+
/**
26+
* Implementation of {@link IFragmentRequestProcessor} that processes
27+
* {@link ITriplePatternFragmentRequest}s over data stored in Jena TDB.
28+
*
29+
* @author <a href="mailto:[email protected]">Bart Hanssens</a>
30+
* @author <a href="http://olafhartig.de">Olaf Hartig</a>
31+
*/
32+
public class SparqlBasedRequestProcessorForTPFs
33+
extends AbstractRequestProcessorForTriplePatterns<RDFNode,String,String>
34+
{
35+
private final Dataset tdb;
36+
private final String defaultGraph;
37+
private final String sparql = "CONSTRUCT WHERE { ?s ?p ?o } " +
38+
"ORDER BY ?s ?p ?o";
39+
40+
private final String count = "SELECT (COUNT(?s) AS ?count) WHERE { ?s ?p ?o }";
41+
42+
private final Query query = QueryFactory.create(sparql, Syntax.syntaxSPARQL_11);
43+
private final Query countQuery = QueryFactory.create(count, Syntax.syntaxSPARQL_11);
44+
45+
/**
46+
*
47+
* @param request
48+
* @return
49+
* @throws IllegalArgumentException
50+
*/
51+
@Override
52+
protected Worker getTPFSpecificWorker(
53+
final ITriplePatternFragmentRequest<RDFNode,String,String> request )
54+
throws IllegalArgumentException
55+
{
56+
return new Worker( request );
57+
}
58+
59+
/**
60+
*
61+
*/
62+
protected class Worker
63+
extends AbstractRequestProcessorForTriplePatterns.Worker<RDFNode,String,String>
64+
{
65+
66+
/**
67+
*
68+
* @param req
69+
*/
70+
public Worker(
71+
final ITriplePatternFragmentRequest<RDFNode,String,String> req )
72+
{
73+
super( req );
74+
}
75+
76+
/**
77+
*
78+
* @param subject
79+
* @param predicate
80+
* @param object
81+
* @param offset
82+
* @param limit
83+
* @return
84+
*/
85+
@Override
86+
protected ILinkedDataFragment createFragment(
87+
final ITriplePatternElement<RDFNode,String,String> subject,
88+
final ITriplePatternElement<RDFNode,String,String> predicate,
89+
final ITriplePatternElement<RDFNode,String,String> object,
90+
final long offset,
91+
final long limit )
92+
{
93+
// FIXME: The following algorithm is incorrect for cases in which
94+
// the requested triple pattern contains a specific variable
95+
// multiple times;
96+
// e.g., (?x foaf:knows ?x ) or (_:bn foaf:knows _:bn)
97+
// see https://github.com/LinkedDataFragments/Server.Java/issues/24
98+
99+
Model model;
100+
if (defaultGraph == null) {
101+
model = tdb.getDefaultModel();
102+
} else {
103+
model = tdb.getNamedModel(defaultGraph);
104+
}
105+
106+
QuerySolutionMap map = new QuerySolutionMap();
107+
if ( ! subject.isVariable() ) {
108+
map.add("s", subject.asConstantTerm());
109+
}
110+
if ( ! predicate.isVariable() ) {
111+
map.add("p", predicate.asConstantTerm());
112+
}
113+
if ( ! object.isVariable() ) {
114+
map.add("o", object.asConstantTerm());
115+
}
116+
117+
query.setOffset(offset);
118+
query.setLimit(limit);
119+
120+
Model triples = ModelFactory.createDefaultModel();
121+
122+
try (QueryExecution qexec = QueryExecutionFactory.create(query, model, map)) {
123+
qexec.execConstruct(triples);
124+
}
125+
126+
if (triples.isEmpty()) {
127+
return createEmptyTriplePatternFragment();
128+
}
129+
130+
// Try to get an estimate
131+
long size = triples.size();
132+
long estimate = -1;
133+
134+
try (QueryExecution qexec = QueryExecutionFactory.create(countQuery, model, map)) {
135+
ResultSet results = qexec.execSelect();
136+
if (results.hasNext()) {
137+
QuerySolution soln = results.nextSolution() ;
138+
Literal literal = soln.getLiteral("count");
139+
estimate = literal.getLong();
140+
}
141+
}
142+
143+
/*GraphStatisticsHandler stats = model.getGraph().getStatisticsHandler();
144+
if (stats != null) {
145+
Node s = (subject != null) ? subject.asNode() : null;
146+
Node p = (predicate != null) ? predicate.asNode() : null;
147+
Node o = (object != null) ? object.asNode() : null;
148+
estimate = stats.getStatistic(s, p, o);
149+
}*/
150+
151+
// No estimate or incorrect
152+
if (estimate < offset + size) {
153+
estimate = (size == limit) ? offset + size + 1 : offset + size;
154+
}
155+
156+
// create the fragment
157+
final boolean isLastPage = ( estimate < offset + limit );
158+
return createTriplePatternFragment( triples, estimate, isLastPage );
159+
}
160+
161+
} // end of class Worker
162+
163+
164+
/**
165+
* Constructor
166+
*
167+
* @param tdbdir directory used for TDB backing
168+
*/
169+
public SparqlBasedRequestProcessorForTPFs(File tdbdir) {
170+
this(tdbdir, null);
171+
}
172+
173+
public SparqlBasedRequestProcessorForTPFs(File tdbdir, String defaultGraph) {
174+
this.defaultGraph = defaultGraph;
175+
this.tdb = TDBFactory.createDataset(tdbdir.getAbsolutePath());
176+
}
177+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package org.linkeddatafragments.datasource.sparql;
2+
3+
import java.io.File;
4+
5+
import org.linkeddatafragments.datasource.DataSourceBase;
6+
import org.linkeddatafragments.datasource.IFragmentRequestProcessor;
7+
import org.linkeddatafragments.fragments.IFragmentRequestParser;
8+
import org.linkeddatafragments.fragments.tpf.TPFRequestParserForJenaBackends;
9+
10+
/**
11+
* Experimental Jena TDB-backed data source of Basic Linked Data Fragments.
12+
*
13+
* @author <a href="mailto:[email protected]">Bart Hanssens</a>
14+
* @author <a href="http://olafhartig.de">Olaf Hartig</a>
15+
*/
16+
public class SparqlDataSource extends DataSourceBase {
17+
18+
/**
19+
* The request processor
20+
*
21+
*/
22+
protected final SparqlBasedRequestProcessorForTPFs requestProcessor;
23+
24+
@Override
25+
public IFragmentRequestParser getRequestParser()
26+
{
27+
return TPFRequestParserForJenaBackends.getInstance();
28+
}
29+
30+
@Override
31+
public IFragmentRequestProcessor getRequestProcessor()
32+
{
33+
return requestProcessor;
34+
}
35+
36+
/**
37+
* Constructor
38+
*
39+
* @param title
40+
* @param description
41+
* @param tdbdir directory used for TDB backing
42+
*/
43+
public SparqlDataSource(String title, String description, File tdbdir, String graph) {
44+
super(title, description);
45+
requestProcessor = new SparqlBasedRequestProcessorForTPFs( tdbdir , graph);
46+
}
47+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.linkeddatafragments.datasource.sparql;
2+
3+
import java.io.File;
4+
5+
import org.linkeddatafragments.datasource.IDataSource;
6+
import org.linkeddatafragments.datasource.IDataSourceType;
7+
import org.linkeddatafragments.exceptions.DataSourceCreationException;
8+
9+
import com.google.gson.JsonObject;
10+
11+
/**
12+
* The type of Triple Pattern Fragment data sources that are backed by
13+
* a Jena TDB instance.
14+
*
15+
* @author <a href="http://olafhartig.de">Olaf Hartig</a>
16+
*/
17+
public class SparqlDataSourceType implements IDataSourceType
18+
{
19+
@Override
20+
public IDataSource createDataSource( final String title,
21+
final String description,
22+
final JsonObject settings )
23+
throws DataSourceCreationException
24+
{
25+
final String dname = settings.getAsJsonPrimitive("directory").getAsString();
26+
final File dir = new File( dname );
27+
28+
// Set the defaultGraph, if provided
29+
String graph = null;
30+
if (settings.has("graph")) {
31+
graph = settings.getAsJsonPrimitive("graph").getAsString();
32+
}
33+
34+
try {
35+
return new SparqlDataSource(title, description, dir, graph);
36+
} catch (Exception ex) {
37+
throw new DataSourceCreationException(ex);
38+
}
39+
}
40+
41+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package org.linkeddatafragments.test.datasource;
2+
3+
import com.google.gson.JsonObject;
4+
5+
import org.apache.jena.query.Dataset;
6+
import org.apache.jena.rdf.model.Model;
7+
import org.apache.jena.tdb.TDBFactory;
8+
9+
import java.io.File;
10+
import java.io.InputStream;
11+
import org.apache.jena.rdf.model.RDFNode;
12+
13+
import org.apache.jena.riot.Lang;
14+
import org.apache.jena.riot.RDFDataMgr;
15+
16+
import org.junit.After;
17+
import org.junit.AfterClass;
18+
19+
import org.junit.Before;
20+
import org.junit.BeforeClass;
21+
22+
import org.linkeddatafragments.datasource.DataSourceFactory;
23+
import org.linkeddatafragments.datasource.DataSourceTypesRegistry;
24+
import org.linkeddatafragments.datasource.sparql.SparqlDataSourceType;
25+
import org.linkeddatafragments.datasource.tdb.JenaTDBDataSourceType;
26+
import org.linkeddatafragments.util.TriplePatternElementParser;
27+
import org.linkeddatafragments.util.TriplePatternElementParserForJena;
28+
29+
/**
30+
*
31+
* @author <a href="mailto:[email protected]">Bart Hanssens</a>
32+
*/
33+
public class SparqlDataSourceTest extends DataSourceTest<RDFNode,String,String>
34+
{
35+
private static File jena;
36+
private static Dataset dataset;
37+
38+
/**
39+
*
40+
* @return
41+
*/
42+
@Override
43+
protected TriplePatternElementParser<RDFNode,String,String>
44+
getTriplePatternElementParser()
45+
{
46+
return TriplePatternElementParserForJena.getInstance();
47+
}
48+
49+
/**
50+
*
51+
* @throws Exception
52+
*/
53+
@BeforeClass
54+
public static void setUpClass() throws Exception {
55+
final String typeName = "SparqlSourceType";
56+
if ( ! DataSourceTypesRegistry.isRegistered(typeName) ) {
57+
DataSourceTypesRegistry.register( typeName,
58+
new SparqlDataSourceType() );
59+
}
60+
61+
String tmpdir = System.getProperty("java.io.tmpdir");
62+
jena = new File(tmpdir, "ldf-jena-test");
63+
jena.mkdir();
64+
65+
dataset = TDBFactory.createDataset(jena.getAbsolutePath());
66+
67+
Model model = dataset.getDefaultModel();
68+
InputStream in = ClassLoader.getSystemResourceAsStream("demo.nt");
69+
RDFDataMgr.read(model, in, Lang.NTRIPLES);
70+
71+
// Everything is in place, now create the LDF datasource
72+
JsonObject config = createConfig("jena tdb test", "jena tdb test",
73+
typeName);
74+
75+
JsonObject settings = new JsonObject();
76+
settings.addProperty("directory", jena.getAbsolutePath());
77+
config.add("settings", settings);
78+
79+
setDatasource(DataSourceFactory.create(config));
80+
}
81+
82+
/**
83+
*
84+
* @throws Exception
85+
*/
86+
@AfterClass
87+
public static void tearDownClass() throws Exception {
88+
TDBFactory.release(dataset);
89+
File[] files = jena.listFiles();
90+
for (File f : files) {
91+
f.delete();
92+
}
93+
jena.delete();
94+
95+
}
96+
97+
/**
98+
*
99+
* @throws Exception
100+
*/
101+
@Before
102+
public void setUp() throws Exception {
103+
}
104+
105+
/**
106+
*
107+
* @throws Exception
108+
*/
109+
@After
110+
public void tearDown() throws Exception {
111+
}
112+
}

0 commit comments

Comments
 (0)