RDFStarSupportTest.java
/*******************************************************************************
* Copyright (c) 2020 Eclipse RDF4J contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
package org.eclipse.rdf4j.testsuite.repository;
import static org.assertj.core.api.Assertions.assertThat;
import java.math.BigInteger;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.eclipse.rdf4j.model.BNode;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Triple;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.model.vocabulary.FOAF;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.GraphQuery;
import org.eclipse.rdf4j.query.GraphQueryResult;
import org.eclipse.rdf4j.query.QueryResults;
import org.eclipse.rdf4j.query.TupleQuery;
import org.eclipse.rdf4j.query.TupleQueryResult;
import org.eclipse.rdf4j.repository.Repository;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.repository.RepositoryException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
/**
* Test cases for RDF-star support in the Repository.
*
* @author Jeen Broekstra
*/
@Timeout(value = 10, unit = TimeUnit.MINUTES)
public abstract class RDFStarSupportTest {
private Repository testRepository;
protected RepositoryConnection testCon;
private ValueFactory vf;
private BNode bob;
private BNode alice;
private BNode alexander;
private Literal nameAlice;
private Literal nameBob;
private Literal mboxAlice;
private Literal mboxBob;
private IRI context1;
private IRI context2;
@BeforeEach
public void setUp() {
testRepository = createRepository();
testCon = testRepository.getConnection();
testCon.clear();
testCon.clearNamespaces();
vf = testRepository.getValueFactory();
// Initialize values
bob = vf.createBNode();
alice = vf.createBNode();
alexander = vf.createBNode();
nameAlice = vf.createLiteral("Alice");
nameBob = vf.createLiteral("Bob");
mboxAlice = vf.createLiteral("alice@example.org");
mboxBob = vf.createLiteral("bob@example.org");
context1 = vf.createIRI("urn:x-local:graph1");
context2 = vf.createIRI("urn:x-local:graph2");
}
@AfterEach
public void tearDown() {
try {
testCon.close();
} finally {
testRepository.shutDown();
}
}
@Test
public void testAddRDFStarSubject() {
Triple rdfStarTriple = vf.createTriple(bob, FOAF.NAME, nameBob);
testCon.add(rdfStarTriple, RDF.TYPE, RDF.ALT);
assertThat(testCon.hasStatement(rdfStarTriple, RDF.TYPE, RDF.ALT, false)).isTrue();
}
@Test
public void testAddRDFStarObject() {
Triple rdfStarTriple = vf.createTriple(bob, FOAF.NAME, nameBob);
testCon.add(RDF.ALT, RDF.TYPE, rdfStarTriple);
assertThat(testCon.hasStatement(RDF.ALT, RDF.TYPE, rdfStarTriple, false)).isTrue();
}
@Test
public void testAddRDFStarContext() {
Triple rdfStarTriple = vf.createTriple(bob, FOAF.NAME, nameBob);
try {
testCon.add(RDF.ALT, RDF.TYPE, RDF.ALT, rdfStarTriple);
Assertions.fail("RDF-star triple value should not be allowed by store as context identifier");
} catch (RepositoryException e) {
// fall through, expected behavior
testCon.rollback();
}
}
@Test
public void testSparqlStar() {
Triple rdfStarTriple = vf.createTriple(bob, FOAF.NAME, nameBob);
testCon.add(rdfStarTriple, RDF.TYPE, RDF.ALT);
String query = "PREFIX foaf: <" + FOAF.NAMESPACE + ">\n" +
"SELECT DISTINCT * WHERE { <<?s foaf:name ?o>> ?b ?c. }";
List<BindingSet> result = QueryResults.asList(testCon.prepareTupleQuery(query).evaluate());
assertThat(result).hasSize(1);
BindingSet bs = result.get(0);
assertThat(bs.getValue("s")).isEqualTo(bob);
assertThat(bs.getValue("o")).isEqualTo(nameBob);
assertThat(bs.getValue("b")).isEqualTo(RDF.TYPE);
assertThat(bs.getValue("c")).isEqualTo(RDF.ALT);
}
@Test
public void testSparqlStarUpdate() {
Triple rdfStarTriple = vf.createTriple(bob, FOAF.NAME, nameBob);
testCon.add(rdfStarTriple, RDF.TYPE, RDF.ALT);
String update = "PREFIX foaf: <" + FOAF.NAMESPACE + ">\n" +
"INSERT { ?s foaf:age 23 } WHERE { <<?s foaf:name ?o>> ?b ?c .}";
testCon.prepareUpdate(update).execute();
Assertions.assertTrue(testCon.hasStatement(bob, FOAF.AGE, vf.createLiteral(BigInteger.valueOf(23)), false));
}
@Test
public void testRdfStarAddAndRetrieveSparql() {
Triple insertedTriple = vf.createTriple(RDF.SUBJECT, RDF.PREDICATE, RDF.OBJECT);
Literal literal = vf.createLiteral("I am a triple ;-D");
testCon.begin();
testCon.add(insertedTriple, RDF.TYPE, literal);
TupleQuery query = testCon.prepareTupleQuery(
"SELECT * WHERE { << <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> >> ?a ?b}");
Assertions.assertTrue(testCon.prepareBooleanQuery("ASK { ?t a 'I am a triple ;-D'}").evaluate());
Assertions.assertEquals(1, getCount(query));
testCon.commit();
}
@Test
public void testRdfStarAddAndRetrieveSparqlSeparateTransaction() {
Triple insertedTriple = vf.createTriple(RDF.SUBJECT, RDF.PREDICATE, RDF.OBJECT);
Literal literal = vf.createLiteral("I am a triple ;-D");
testCon.begin();
testCon.add(insertedTriple, RDF.TYPE, literal);
testCon.commit();
testCon.begin();
Assertions.assertTrue(testCon.prepareBooleanQuery("ASK { ?t a 'I am a triple ;-D'}").evaluate());
TupleQuery tupleQuery = testCon.prepareTupleQuery(
"SELECT * WHERE { << <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> >> ?a ?b}");
Assertions.assertEquals(1, getCount(tupleQuery));
testCon.commit();
}
private static long getCount(TupleQuery tupleQuery) {
try (TupleQueryResult evaluate = tupleQuery.evaluate()) {
return evaluate.stream().count();
}
}
@Test
public void testRdfStarAddAndRetrieve() {
Triple insertedTriple = vf.createTriple(RDF.SUBJECT, RDF.PREDICATE, RDF.OBJECT);
Triple copyOfInsertedTriple = vf.createTriple(RDF.SUBJECT, RDF.PREDICATE, RDF.OBJECT);
Literal literal = vf.createLiteral("I am a triple ;-D");
testCon.begin();
testCon.add(insertedTriple, RDF.TYPE, literal);
Assertions.assertEquals(1, testCon.getStatements(null, RDF.TYPE, literal, false).stream().count());
Assertions.assertEquals(1, testCon.getStatements(copyOfInsertedTriple, null, null, false).stream().count());
testCon.commit();
}
@Test
public void testMemoryStore_RDFstar() {
String queryString = "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n"
+ "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
+ "PREFIX person: <http://example.com/person/>\n"
+ "PREFIX org: <http://example.com/org/>\n"
+ "PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n"
+ "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n"
+ "CONSTRUCT {\n"
+ " ?s ?p ?o.\n"
+ " << ?s ?p ?o >> <http://example.com/certainty> ?cert.\n"
+ " << ?s ?p ?o >> <http://example.com/certaintyDeviation> ?certDiv.\n"
+ " << person:alice foaf:knows person:bob >> <http://example.com/observedBy> person:mike.\n"
+ "}\n"
+ "WHERE {\n"
+ " {\n"
+ " SELECT ?s ?p ?o ?cert ?certDiv WHERE {\n"
+ " VALUES (?s ?p ?o ?cert ?certDiv) {\n"
+ " (person:alice foaf:knows person:bob \"1.0\"^^xsd:decimal 0 )\n"
+ " (person:alice foaf:knows person:carol \"0.3\"^^xsd:decimal \"0.1\"^^xsd:decimal)\n"
+ " (person:carol foaf:knows person:mike \"0.7\"^^xsd:decimal \"0.2\"^^xsd:decimal)\n"
+ " (person:mike foaf:knows person:carol \"0.1\"^^xsd:decimal UNDEF)\n"
+ " (person:bob foaf:knows person:carol \"0.8\"^^xsd:decimal UNDEF)\n"
+ " (person:alice foaf:knows person:mike \"0.6\"^^xsd:decimal \"0.1\"^^xsd:decimal)\n"
+ " (person:alice foaf:member org:W3C UNDEF UNDEF)\n"
+ " (person:mike foaf:member org:W3C UNDEF UNDEF)\n"
+ " (person:alice rdf:type foaf:Person UNDEF UNDEF)\n"
+ " (person:carol rdf:type foaf:Person UNDEF UNDEF)\n"
+ " (person:mike rdf:type foaf:Person UNDEF UNDEF)\n"
+ " (person:bob rdf:type foaf:Person UNDEF UNDEF)\n"
+ " (org:W3C rdf:type foaf:Organization UNDEF UNDEF)\n"
+ " (person:alice rdfs:label \"Alice\" UNDEF UNDEF)\n"
+ " (person:alice foaf:birthday \"1990-01-01\" UNDEF UNDEF)\n"
+ " (person:bob rdfs:label \"Bob\" UNDEF UNDEF)\n"
+ " (person:carol rdfs:label \"Carol\" UNDEF UNDEF)\n"
+ " (person:mike rdfs:label \"Mike\" UNDEF UNDEF)\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}";
try (RepositoryConnection con = testRepository.getConnection()) {
GraphQuery graphQuery = con.prepareGraphQuery(queryString);
try (GraphQueryResult result = graphQuery.evaluate()) {
List<Statement> statements = QueryResults.asList(result);
Assertions.assertEquals(29, statements.size());
}
}
}
@Test
public void testSparqlStarInObjectPosition() {
testCon.add(Values.bnode(), FOAF.KNOWS, Values.triple(Values.bnode(), FOAF.NAME, Values.literal("John Doe")));
TupleQuery tupleQuery = testCon.prepareTupleQuery(
"PREFIX foaf: <" + FOAF.NAMESPACE + ">\n" +
"SELECT * WHERE { ?a ?b <<?s foaf:name ?o>>. }");
try (TupleQueryResult evaluate = tupleQuery.evaluate()) {
List<BindingSet> collect = evaluate.stream().collect(Collectors.toList());
Assertions.assertEquals(1, collect.size());
}
}
protected abstract Repository createRepository();
}