/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.sparql.api;
import java.util.Iterator;
import java.util.Set ;
import org.apache.jena.atlas.iterator.Iter ;
import org.apache.jena.atlas.junit.BaseTest;
import org.apache.jena.graph.Graph ;
import org.apache.jena.graph.Node ;
import org.apache.jena.graph.Triple;
import org.apache.jena.query.* ;
import org.apache.jena.rdf.model.* ;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.DatasetGraphFactory;
import org.apache.jena.sparql.core.Quad;
import org.apache.jena.sparql.graph.GraphFactory;
import org.apache.jena.sparql.util.IsoMatcher;
import org.apache.jena.sparql.util.ModelUtils;
import org.apache.jena.vocabulary.OWL;
import org.apache.jena.vocabulary.RDF;
import org.junit.Test;
public class TestAPI extends BaseTest
{
private static final String ns = "http://example/ns#" ;
static Model m = GraphFactory.makeJenaDefaultModel() ;
static Resource r1 = m.createResource() ;
static Property p1 = m.createProperty(ns+"p1") ;
static Property p2 = m.createProperty(ns+"p2") ;
static Property p3 = m.createProperty(ns+"p3") ;
static Model dft = GraphFactory.makeJenaDefaultModel() ;
static Resource s = dft.createResource(ns+"s") ;
static Property p = dft.createProperty(ns+"p") ;
static Resource o = dft.createResource(ns+"o") ;
static Resource g1 = dft.createResource(ns+"g1") ;
static Dataset d = null;
static {
m.add(r1, p1, "x1") ;
m.add(r1, p2, "X2") ; // NB Capital
m.add(r1, p3, "y1") ;
dft.add(s, p, o) ;
d = DatasetFactory.create(dft);
d.addNamedModel(g1.getURI(), m);
}
@Test public void testInitialBindingsConstruct1()
{
try(QueryExecution qExec = makeQExec("CONSTRUCT {?s ?p ?z} {?s ?p 'x1'}")) {
QuerySolutionMap init = new QuerySolutionMap() ;
init.add("z", m.createLiteral("zzz"));
qExec.setInitialBinding(init) ;
Model r = qExec.execConstruct() ;
assertTrue("Empty model", r.size() > 0 ) ;
Property p1 = m.createProperty(ns+"p1") ;
assertTrue("Empty model", r.contains(null,p1, init.get("z"))) ;
}
}
@Test public void testInitialBindingsConstruct2()
{
try(QueryExecution qExec = makeQExec("CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }")) {
QuerySolutionMap init = new QuerySolutionMap() ;
init.add("o", m.createLiteral("x1"));
qExec.setInitialBinding(init) ;
Model r = qExec.execConstruct() ;
assertTrue("Empty model", r.size() > 0 ) ;
Property p1 = m.createProperty(ns+"p1") ;
assertTrue("Empty model", r.contains(null, p1, init.get("x1"))) ;
}
}
// The original test (see commented out "assertSame) is test is now bogus.
// DatasetImpl no longer caches the default model as that caused problems.
//
// This is testing that the model for the resource in the result is the
// same object as the model supplied to the query.
// "Same" here means "same contents" includign blank nodes.
//
// it used to be that this tested whether they were the same object.
// That is dubious and no longer true even for DatasetImpl (teh default mode
// is not cached but recreated on demand so theer are no problems with
// transaction boundaries).
//
// Left as an active test so the assumption is tested (it has been true for
// many years).
//
// Using the Resource.getXXX and Resource.listXXX operations is dubious if
// there are named graphs and that has always been the case.
@Test public void test_API1()
{
try(QueryExecution qExec = makeQExec("SELECT * {?s ?p ?o}")) {
ResultSet rs = qExec.execSelect() ;
assertTrue("No results", rs.hasNext()) ;
QuerySolution qs = rs.nextSolution() ;
Resource qr = qs.getResource("s") ;
//assertSame("Not the same model as queried", qr.getModel(), m) ;
Set<Statement> s1 = qr.getModel().listStatements().toSet() ;
Set<Statement> s2 = m.listStatements().toSet() ;
assertEquals(s1,s2) ;
}
}
@Test public void testInitialBindings0()
{
QuerySolutionMap smap1 = new QuerySolutionMap() ;
QuerySolutionMap smap2 = new QuerySolutionMap() ;
smap1.add("o", m.createLiteral("y1"));
smap2.addAll(smap1) ;
assertTrue(smap2.contains("o")) ;
smap2.clear() ;
assertFalse(smap2.contains("o")) ;
assertTrue(smap1.contains("o")) ;
QuerySolutionMap smap3 = new QuerySolutionMap() ;
smap2.addAll((QuerySolution)smap1) ;
assertTrue(smap2.contains("o")) ;
}
@Test public void testInitialBindings1()
{
QueryExecution qExec = makeQExec("SELECT * {?s ?p ?o}") ;
QuerySolutionMap init = new QuerySolutionMap() ;
init.add("o", m.createLiteral("y1"));
qExec.setInitialBinding(init) ;
int count = queryAndCount(qExec) ;
assertEquals("Initial binding didn't restrict query properly", 1, count) ;
}
@Test public void testInitialBindings2()
{
QueryExecution qExec = makeQExec("SELECT * {?s ?p ?o}") ;
QuerySolutionMap init = new QuerySolutionMap() ;
init.add("z", m.createLiteral("zzz"));
qExec.setInitialBinding(init) ;
int count = queryAndCount(qExec) ;
assertEquals("Initial binding restricted query improperly", 3, count) ;
}
@Test public void testInitialBindings3()
{
try(QueryExecution qExec = makeQExec("SELECT * {?s ?p 'x1'}")) {
QuerySolutionMap init = new QuerySolutionMap() ;
init.add("z", m.createLiteral("zzz"));
qExec.setInitialBinding(init) ;
ResultSet rs = qExec.execSelect() ;
QuerySolution qs = rs.nextSolution() ;
assertTrue("Initial setting not set correctly now", qs.getLiteral("z").getLexicalForm().equals("zzz")) ;
}
}
@Test public void testInitialBindings4()
{
// Test derived from report by Holger Knublauch
String queryString =
"PREFIX : <"+ns+">\n" +
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> \n" +
"SELECT * \n" +
"WHERE { \n" +
" ?x :p1 ?z ." +
" NOT EXISTS { \n" +
" ?x rdfs:label ?z . \n" +
" }\n" +
"}";
Query query = QueryFactory.create(queryString, Syntax.syntaxARQ);
try(QueryExecution qexec = QueryExecutionFactory.create(query, m)) {
QuerySolutionMap map = new QuerySolutionMap();
map.add("this", OWL.Thing);
qexec.setInitialBinding(map);
ResultSet rs = qexec.execSelect();
while(rs.hasNext()) {
QuerySolution qs = rs.nextSolution();
//System.out.println("Result: " + qs);
}
}
}
/**
* Initial binding substitution happens before optimization so initial bindings can make a semantically always false query into one that can return true
*/
@Test public void testInitialBindings5() {
// From JENA-500
Query query = QueryFactory.create(
"ASK\n" +
"WHERE {\n" +
" FILTER (?a = <http://constant>) .\n" +
"}");
//System.out.println(Algebra.optimize(Algebra.compile(query)).toString());
Model model = ModelFactory.createDefaultModel();
model.add(OWL.Thing, RDF.type, OWL.Class);
QuerySolutionMap initialBinding = new QuerySolutionMap();
initialBinding.add("a", ResourceFactory.createResource("http://constant"));
QueryExecution qexec = QueryExecutionFactory.create(query, model, initialBinding);
boolean result = qexec.execAsk();
assertTrue(result);
}
/**
* Initial binding substitution happens before optimization so initial bindings can make a semantically always false query into one that can return true
*/
@Test public void testInitialBindings6() {
// From JENA-500
Query query = QueryFactory.create(
"ASK\n" +
"WHERE {\n" +
" FILTER (?a = ?b) .\n" +
"}");
//System.out.println(Algebra.optimize(Algebra.compile(query)).toString());
Model model = ModelFactory.createDefaultModel();
model.add(OWL.Thing, RDF.type, OWL.Class);
QuerySolutionMap initialBinding = new QuerySolutionMap();
initialBinding.add("a", ResourceFactory.createTypedLiteral(Boolean.TRUE));
initialBinding.add("b", ResourceFactory.createTypedLiteral(Boolean.TRUE));
QueryExecution qexec = QueryExecutionFactory.create(query, model, initialBinding);
boolean result = qexec.execAsk();
assertTrue(result);
}
@Test public void testReuseQueryObject1()
{
String queryString = "SELECT * {?s ?p ?o}";
Query q = QueryFactory.create(queryString) ;
QueryExecution qExec = QueryExecutionFactory.create(q, m) ;
int count = queryAndCount(qExec) ;
assertEquals(3, count) ;
qExec = QueryExecutionFactory.create(q, m) ;
count = queryAndCount(qExec) ;
assertEquals(3, count) ;
}
@Test public void testReuseQueryObject2()
{
String queryString = "SELECT (count(?o) AS ?c) {?s ?p ?o} GROUP BY ?s";
Query q = QueryFactory.create(queryString) ;
try(QueryExecution qExec = QueryExecutionFactory.create(q, m)) {
ResultSet rs = qExec.execSelect() ;
QuerySolution qs = rs.nextSolution() ;
assertEquals(3, qs.getLiteral("c").getInt()) ;
}
try(QueryExecution qExec = QueryExecutionFactory.create(q, m)) {
ResultSet rs = qExec.execSelect() ;
QuerySolution qs = rs.nextSolution() ;
assertEquals(3, qs.getLiteral("c").getInt()) ;
}
}
@Test public void testConstructRejectsBadTriples1()
{
String queryString = "CONSTRUCT { ?s ?p ?o } WHERE { ?o ?p ?s }";
Query q = QueryFactory.create(queryString);
QueryExecution qExec = QueryExecutionFactory.create(q, m);
Model resultModel = qExec.execConstruct();
assertEquals(0, resultModel.size());
}
@Test public void testConstructRejectsBadTriples2()
{
String queryString = "CONSTRUCT { ?s ?p ?o } WHERE { ?o ?p ?s }";
Query q = QueryFactory.create(queryString);
QueryExecution qExec = QueryExecutionFactory.create(q, m);
Iterator<Triple> ts = qExec.execConstructTriples();
long count = 0;
while (ts.hasNext()) {
count++;
ts.next();
}
assertEquals(0, count);
}
// // Execute a test both with and without regex optimization enabled
// // Check the number of results
// private void XexecRegexTest(int expected, String queryString)
// {
// Object b = ARQ.getContext().get(ARQ.enableRegexConstraintsOpt) ;
// try {
// ARQ.getContext().set(ARQ.enableRegexConstraintsOpt, "false") ;
// int count1 = queryAndCount(queryString) ;
// ARQ.getContext().set(ARQ.enableRegexConstraintsOpt, "true") ;
// int count2 = queryAndCount(queryString) ;
// assertEquals("Different number of results", count1, count2) ;
// if ( expected >= 0 )
// assertEquals("Unexpected number of results", expected, count1) ;
// } finally {
// ARQ.getContext().set(ARQ.enableRegexConstraintsOpt, b) ;
// }
// }
// ARQ Construct Quad Tests:
// Two types of query strings: a) construct triple string; b) construct quad string;
// Two kinds of query methods: 1) execTriples(); 2) execQuads();
// Test a)+1)
@Test public void testARQConstructQuad_a_1() {
String queryString = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH ?g { ?s ?p ?o } }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Iterator<Triple> ts = qExec.execConstructTriples();
Model result = ModelFactory.createDefaultModel();
while (ts.hasNext()) {
Triple t = ts.next();
Statement stmt = ModelUtils.tripleToStatement(result, t);
if ( stmt != null )
result.add(stmt);
}
assertEquals(3, result.size());
assertTrue(m.isIsomorphicWith(result));
}
// Test b)+2)
@Test public void testARQConstructQuad_b_2() {
String queryString = "CONSTRUCT { GRAPH ?g1 {?s ?p ?o} } WHERE { ?s ?p ?o. GRAPH ?g1 {?s1 ?p1 'x1'} }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Iterator<Quad> ts = qExec.execConstructQuads();
DatasetGraph result = DatasetGraphFactory.create();
long count = 0;
while (ts.hasNext()) {
count++;
Quad qd = ts.next();
result.add(qd);
}
DatasetGraph expected = DatasetGraphFactory.create();
expected.add(g1.asNode(), s.asNode(), p.asNode(), o.asNode());
assertEquals(1, count);
assertTrue(IsoMatcher.isomorphic( expected, result) );
}
// Test a)+2): Quads constructed in the default graph
@Test public void testARQConstructQuad_a_2() {
String queryString = "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Iterator<Quad> ts = qExec.execConstructQuads();
DatasetGraph result = DatasetGraphFactory.create();
long count = 0;
while (ts.hasNext()) {
count++;
result.add( ts.next() );
}
DatasetGraph expected = DatasetGraphFactory.create();
expected.add(Quad.defaultGraphNodeGenerated, s.asNode(), p.asNode(), o.asNode());
assertEquals(1, count);
assertTrue(IsoMatcher.isomorphic( expected, result) );
}
// Test b)+1): Projection on default graph, ignoring constructing named graphs
@Test public void testARQConstructQuad_b_1() {
String queryString = "CONSTRUCT { ?s ?p ?o GRAPH ?g1 { ?s1 ?p1 ?o1 } } WHERE { ?s ?p ?o. GRAPH ?g1 { ?s1 ?p1 ?o1 } }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Iterator<Triple> ts = qExec.execConstructTriples();
Model result = ModelFactory.createDefaultModel();
while (ts.hasNext()) {
Triple t = ts.next();
Statement stmt = ModelUtils.tripleToStatement(result, t);
if ( stmt != null )
result.add(stmt);
}
assertEquals(1, result.size());
assertTrue(dft.isIsomorphicWith(result));
}
@Test public void testARQConstructQuad_bnodes() {
String queryString = "PREFIX : <http://example/> CONSTRUCT { :s :p :o GRAPH _:a { :s :p :o1 } } WHERE { }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Dataset ds = qExec.execConstructDataset() ;
assertEquals(1, Iter.count(ds.asDatasetGraph().listGraphNodes())) ;
Node n = ds.asDatasetGraph().listGraphNodes().next();
assertTrue(n.isBlank());
Graph g = ds.asDatasetGraph().getGraph(n) ;
assertNotNull(g) ;
assertFalse(g.isEmpty()) ;
}
// Allow duplicated quads in execConstructQuads()
@Test public void testARQConstructQuad_Duplicate_1() {
String queryString = "CONSTRUCT { GRAPH ?g1 {?s ?p ?o} } WHERE { ?s ?p ?o. GRAPH ?g1 {?s1 ?p1 ?o1} }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Iterator<Quad> ts = qExec.execConstructQuads();
long count = 0;
Quad expected = Quad.create( g1.asNode(), s.asNode(), p.asNode(), o.asNode());
while (ts.hasNext()) {
count++;
Quad qd = ts.next();
assertEquals(expected, qd);
}
assertEquals(3, count); // 3 duplicated quads
}
// No duplicated quads in execConstructDataset()
@Test public void testARQConstructQuad_Duplicate_2() {
String queryString = "CONSTRUCT { GRAPH ?g1 {?s ?p ?o} } WHERE { ?s ?p ?o. GRAPH ?g1 {?s1 ?p1 ?o1} }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Dataset result = qExec.execConstructDataset();
DatasetGraph expected = DatasetGraphFactory.create();
expected.add(g1.asNode(), s.asNode(), p.asNode(), o.asNode());
assertEquals(1, result.asDatasetGraph().size());
assertTrue(IsoMatcher.isomorphic( expected, result.asDatasetGraph()) );
}
// Allow duplicated template quads in execConstructQuads()
@Test public void testARQConstructQuad_Duplicate_3() {
String queryString = "CONSTRUCT { GRAPH ?g1 {?s ?p ?o} GRAPH ?g1 {?s ?p ?o} } WHERE { ?s ?p ?o. GRAPH ?g1 {?s1 ?p1 ?o1} }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Iterator<Quad> ts = qExec.execConstructQuads();
long count = 0;
Quad expected = Quad.create( g1.asNode(), s.asNode(), p.asNode(), o.asNode());
while (ts.hasNext()) {
count++;
Quad qd = ts.next();
assertEquals(expected, qd);
}
assertEquals(6, count); // 6 duplicated quads
}
// Allow duplicated template quads in execConstructQuads()
@Test public void testARQConstructQuad_Prefix() {
String queryString = "PREFIX : <http://example/ns#> CONSTRUCT { GRAPH :g1 { ?s :p ?o} } WHERE { ?s ?p ?o }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Iterator<Quad> quads = qExec.execConstructQuads();
DatasetGraph result = DatasetGraphFactory.create();
long count = 0;
while (quads.hasNext()) {
count++;
Quad qd = quads.next();
result.add(qd);
}
DatasetGraph expected = DatasetGraphFactory.create();
expected.add(g1.asNode(), s.asNode(), p.asNode(), o.asNode());
assertEquals(1, count);
assertTrue(IsoMatcher.isomorphic( expected, result) );
}
// Test construct triple short form:
@Test public void testARQConstructQuad_ShortForm_1() {
String queryString = "CONSTRUCT WHERE {?s ?p ?o }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Model result = ModelFactory.createDefaultModel();
qExec.execConstruct(result);
assertEquals(1, result.size());
assertTrue(dft.isIsomorphicWith(result));
}
// Test construct quad short form:
@Test public void testARQConstructQuad_ShortForm_2() {
String queryString = "CONSTRUCT WHERE { GRAPH ?g {?s ?p ?o} }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Dataset result = qExec.execConstructDataset();
Dataset expected = DatasetFactory.createTxnMem();
expected.addNamedModel(g1.getURI(), m);
assertTrue(IsoMatcher.isomorphic( expected.asDatasetGraph(), result.asDatasetGraph()) );
}
// Test construct triple and quad short form:
@Test public void testARQConstructQuad_ShortForm_3() {
String queryString = "CONSTRUCT WHERE { ?s ?p ?o. GRAPH ?g1 {?s1 ?p1 ?o1} }";
Query q = QueryFactory.create(queryString, Syntax.syntaxARQ);
QueryExecution qExec = QueryExecutionFactory.create(q, d);
Dataset result = qExec.execConstructDataset();
assertTrue(IsoMatcher.isomorphic( d.asDatasetGraph(), result.asDatasetGraph()) );
}
// Test bad construct quad short form:
@Test public void testARQConstructQuad_ShortForm_bad() {
String queryString = "CONSTRUCT WHERE { GRAPH ?g {?s ?p ?o. FILTER isIRI(?o)} }";
try {
QueryFactory.create(queryString, Syntax.syntaxARQ);
}catch (QueryParseException e){
return;
}
fail("Short form of construct quad MUST be simple graph patterns!");
}
private QueryExecution makeQExec(String queryString)
{
Query q = QueryFactory.create(queryString) ;
QueryExecution qExec = QueryExecutionFactory.create(q, m) ;
return qExec ;
}
private int queryAndCount(String queryString)
{
QueryExecution qExec = makeQExec(queryString) ;
return queryAndCount(qExec) ;
}
private int queryAndCount(QueryExecution qExec)
{
try {
ResultSet rs = qExec.execSelect() ;
return ResultSetFormatter.consume(rs) ;
} finally { qExec.close() ; }
}
}