From e6ceb1ec1224d9c11cb221321186c30ebe6e854a Mon Sep 17 00:00:00 2001 From: Martin Ledvinka Date: Mon, 22 Jul 2024 08:46:56 +0200 Subject: [PATCH] [Enhancement #245] Flush reasoner before executing query to purge possibly stale reasoner cache. --- .../test/query/runner/TypedQueryRunner.java | 93 ++++++++++--------- .../query/LiveOntologyStatementExecutor.java | 4 +- .../owlapi/query/StatementExecutor.java | 2 +- .../query/TransactionalStatementExecutor.java | 4 +- 4 files changed, 55 insertions(+), 48 deletions(-) diff --git a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/query/runner/TypedQueryRunner.java b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/query/runner/TypedQueryRunner.java index 935644fb1..c8932f54c 100644 --- a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/query/runner/TypedQueryRunner.java +++ b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/query/runner/TypedQueryRunner.java @@ -99,8 +99,8 @@ void testSelectByTypeAndDataPropertyValue() { "?stringAtt ?bString . }"; final TypedQuery q = getEntityManager().createNativeQuery(query, OWLClassB.class); q.setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_B)) - .setParameter("stringAtt", URI.create(Vocabulary.P_B_STRING_ATTRIBUTE)) - .setParameter("bString", b.getStringAttribute(), "en"); + .setParameter("stringAtt", URI.create(Vocabulary.P_B_STRING_ATTRIBUTE)) + .setParameter("bString", b.getStringAttribute(), "en"); final OWLClassB res = q.getSingleResult(); assertNotNull(res); assertEquals(b.getUri(), res.getUri()); @@ -113,10 +113,9 @@ void testSelectByObjectProperty() { final List ds = new ArrayList<>(QueryTestEnvironment.getData(OWLClassD.class)); final OWLClassA a = ds.get(Generators.randomPositiveInt(2, ds.size())).getOwlClassA(); final TypedQuery q = getEntityManager().createNativeQuery(query, OWLClassD.class) - .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_D)) - .setParameter("hasA", - URI.create(Vocabulary.P_HAS_OWL_CLASS_A)) - .setParameter("y", a.getUri()); + .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_D)) + .setParameter("hasA", URI.create(Vocabulary.P_HAS_OWL_CLASS_A)) + .setParameter("y", a.getUri()); final List expected = ds.stream().filter(d -> d.getOwlClassA().getUri().equals(a.getUri())).toList(); final List res = q.getResultList(); @@ -126,7 +125,7 @@ void testSelectByObjectProperty() { @Test void testSetMaxResults() { final TypedQuery q = getEntityManager().createNativeQuery(SELECT_BY_TYPE, OWLClassE.class) - .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_E)); + .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_E)); final int max = 5; assertTrue(max < QueryTestEnvironment.getData(OWLClassE.class).size()); assertEquals(Integer.MAX_VALUE, q.getMaxResults()); @@ -141,14 +140,14 @@ void testSetMaxResults() { @Test void testSetMaxResultsNegative() { final TypedQuery q = getEntityManager().createNativeQuery(SELECT_BY_TYPE, OWLClassE.class) - .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_E)); + .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_E)); assertThrows(IllegalArgumentException.class, () -> q.setMaxResults(-1)); } @Test void testSetMaxResultsZero() { final TypedQuery q = getEntityManager().createNativeQuery(SELECT_BY_TYPE, OWLClassE.class) - .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_E)); + .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_E)); q.setMaxResults(0); final List res = q.getResultList(); assertNotNull(res); @@ -162,7 +161,7 @@ void testGetSingleResult() { "SELECT ?x WHERE { ?x ?stringAtt ?aString .}"; final TypedQuery q = getEntityManager().createNativeQuery(query, OWLClassA.class); q.setParameter("stringAtt", URI.create(Vocabulary.P_A_STRING_ATTRIBUTE)) - .setParameter("aString", a.getStringAttribute(), "en"); + .setParameter("aString", a.getStringAttribute(), "en"); final OWLClassA res = q.getSingleResult(); assertNotNull(res); assertEquals(a.getUri(), res.getUri()); @@ -171,7 +170,7 @@ void testGetSingleResult() { @Test void testGetSingleResultMultiples() { final TypedQuery q = getEntityManager().createNativeQuery(SELECT_BY_TYPE, OWLClassE.class) - .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_E)); + .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_E)); assertThrows(NoUniqueResultException.class, q::getSingleResult); } @@ -199,7 +198,7 @@ void testCreateQueryNullClass() { void askQueryReturnsTrue() { final String query = "ASK { ?x a ?type . }"; final TypedQuery q = getEntityManager().createNativeQuery(query, Boolean.class) - .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_A)); + .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_A)); final Boolean res = q.getSingleResult(); assertNotNull(res); assertTrue(res); @@ -217,17 +216,19 @@ void askQueryReturnsFalse() { @Test public void askQueryAgainstTransactionalOntologyContainsUncommittedChangesAsWell() { - final OWLClassE e = new OWLClassE(); + final OWLClassD d = QueryTestEnvironment.getData(OWLClassD.class).get(0); + final OWLClassA a = QueryTestEnvironment.getData(OWLClassA.class).get(1); getEntityManager().getTransaction().begin(); try { - getEntityManager().persist(e); - final TypedQuery query = getEntityManager().createNativeQuery( - "ASK { ?individual a ?type . }", - Boolean.class).setParameter("individual", e.getUri()).setParameter("type", - URI.create(Vocabulary.C_OWL_CLASS_E)); + final OWLClassD update = getEntityManager().find(OWLClassD.class, d.getUri()); + final OWLClassA toAssign = getEntityManager().find(OWLClassA.class, a.getUri()); + update.setOwlClassA(toAssign); + final TypedQuery query = getEntityManager().createNativeQuery("SELECT ?a WHERE { ?d ?hasA ?a . }", URI.class) + .setParameter("d", update.getUri()) + .setParameter("hasA", URI.create(Vocabulary.P_HAS_OWL_CLASS_A)); query.setHint(QueryHints.TARGET_ONTOLOGY, Statement.StatementOntology.TRANSACTIONAL.toString()); - final Boolean res = query.getSingleResult(); - assertTrue(res); + final URI res = query.getSingleResult(); + assertEquals(a.getUri(), res); } finally { getEntityManager().getTransaction().rollback(); } @@ -238,7 +239,7 @@ void askQueryWithPositionParameter() { final String query = "ASK { ?x a $1 . }"; final URI paramValue = URI.create(OWLClassA.class.getAnnotation(OWLClass.class).iri()); final TypedQuery q = getEntityManager().createNativeQuery(query, Boolean.class) - .setParameter(1, paramValue); + .setParameter(1, paramValue); final Boolean res = q.getSingleResult(); assertNotNull(res); assertTrue(res); @@ -249,7 +250,7 @@ void testCreateTypedNamedNativeQuery() { final List expected = QueryTestEnvironment.getData(OWLClassA.class); final List uris = expected.stream().map(OWLClassA::getUri).toList(); final List res = getEntityManager().createNamedQuery("OWLClassA.findAll", OWLClassA.class) - .getResultList(); + .getResultList(); assertEquals(expected.size(), res.size()); res.forEach(a -> assertTrue(uris.contains(a.getUri()))); } @@ -261,7 +262,7 @@ void usingDescriptorAllowsToCustomizeQueryResults() { final Descriptor descriptor = new EntityDescriptor(); descriptor.setLanguage("cs"); final List result = getEntityManager().createNamedQuery("OWLClassA.findAll", OWLClassA.class) - .setDescriptor(descriptor).getResultList(); + .setDescriptor(descriptor).getResultList(); assertEquals(expected.size(), result.size()); result.forEach(a -> assertNull(a.getStringAttribute())); // Because the data has @en language tag } @@ -271,9 +272,9 @@ public void usingUntypedQueryAllowsToSpecifyLimitInQuery() { final List expected = QueryTestEnvironment.getData(OWLClassA.class); final int size = expected.size() / 2; final List result = getEntityManager().createNativeQuery("SELECT ?x WHERE {" + - "?x a ?classA . } LIMIT ?limit", OWLClassA.class) - .setParameter("classA", URI.create(Vocabulary.C_OWL_CLASS_A)) - .setUntypedParameter("limit", size).getResultList(); + "?x a ?classA . } LIMIT ?limit", OWLClassA.class) + .setParameter("classA", URI.create(Vocabulary.C_OWL_CLASS_A)) + .setUntypedParameter("limit", size).getResultList(); assertEquals(size, result.size()); } @@ -283,7 +284,7 @@ public void setFirstResultCanBeUsedToOffsetFirstQueryResult() { expected.sort(Comparator.comparing(OWLClassA::getUri)); final int offset = expected.size() / 2; final List result = getEntityManager().createNamedQuery("OWLClassA.findAll", OWLClassA.class) - .setFirstResult(offset).getResultList(); + .setFirstResult(offset).getResultList(); assertEquals(expected.size() - offset, result.size()); for (int i = 0; i < result.size(); i++) { assertEquals(expected.get(i + offset).getUri(), result.get(i).getUri()); @@ -308,14 +309,14 @@ void selectionByObjectPropertySupportsEntityAsQueryParameter() { final List ds = new ArrayList<>(QueryTestEnvironment.getData(OWLClassD.class)); final OWLClassA a = ds.get(Generators.randomPositiveInt(2, ds.size())).getOwlClassA(); final TypedQuery q = getEntityManager().createNativeQuery(query, OWLClassD.class) - .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_D)) - .setParameter("hasA", - URI.create(Vocabulary.P_HAS_OWL_CLASS_A)) - .setParameter("y", a); + .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_D)) + .setParameter("hasA", + URI.create(Vocabulary.P_HAS_OWL_CLASS_A)) + .setParameter("y", a); final List expected = ds.stream().filter(d -> d.getOwlClassA().getUri().equals(a.getUri())) - .sorted(Comparator.comparing(OWLClassD::getUri)) - .toList(); + .sorted(Comparator.comparing(OWLClassD::getUri)) + .toList(); final List res = q.getResultList(); res.sort(Comparator.comparing(OWLClassD::getUri)); assertEquals(expected.size(), res.size()); @@ -329,12 +330,12 @@ void selectionByObjectPropertySupportsEntityAsQueryParameter() { protected void querySupportsCollectionParameters() { final String query = "SELECT ?x WHERE { ?x a ?type . FILTER (?x IN (?values)) }"; final List as = QueryTestEnvironment.getData(OWLClassA.class).stream() - .filter(a -> Generators.randomBoolean()) - .toList(); + .filter(a -> Generators.randomBoolean()) + .toList(); final TypedQuery q = getEntityManager().createNativeQuery(query, OWLClassA.class) - .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_A)) - .setParameter("values", as.stream().map(OWLClassA::getUri) - .toList()); + .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_A)) + .setParameter("values", as.stream().map(OWLClassA::getUri) + .toList()); final List result = q.getResultList(); assertEquals(as.size(), result.size()); for (OWLClassA exp : as) { @@ -357,11 +358,13 @@ protected void querySupportsSelectionByDate() { getEntityManager().getTransaction().commit(); final LocalDateTime param = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS).minusHours(3); final List matching = mInstances.stream() - .filter(m -> m.getDateAttribute().toInstant().atOffset(ZoneOffset.UTC).isBefore(param.atOffset(ZoneOffset.UTC))) - .toList(); + .filter(m -> m.getDateAttribute().toInstant() + .atOffset(ZoneOffset.UTC) + .isBefore(param.atOffset(ZoneOffset.UTC))) + .toList(); try { final List result = getEntityManager().createQuery("SELECT m FROM OWLClassM m WHERE m.dateAttribute < :date", OWLClassM.class) - .setParameter("date", param).getResultList(); + .setParameter("date", param).getResultList(); assertEquals(matching.size(), result.size()); matching.forEach(m -> assertTrue(result.stream().anyMatch(rm -> rm.getKey().equals(m.getKey())))); } finally { @@ -372,7 +375,7 @@ protected void querySupportsSelectionByDate() { private void cleanupTestData(String type) { getEntityManager().getTransaction().begin(); getEntityManager().createNativeQuery("DELETE WHERE { ?x a ?type . ?x ?y ?z . }") - .setParameter("type", URI.create(type)).executeUpdate(); + .setParameter("type", URI.create(type)).executeUpdate(); getEntityManager().getTransaction().commit(); } @@ -380,9 +383,9 @@ private void cleanupTestData(String type) { protected void querySupportsSelectionByEntityIdentifier() { final OWLClassA entity = Generators.getRandomItem(QueryTestEnvironment.getData(OWLClassA.class)); final OWLClassA result = getEntityManager().createNativeQuery("SELECT ?x WHERE { ?x a ?type . }", OWLClassA.class) - .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_A)) - .setParameter("x", entity.getUri()) - .getSingleResult(); + .setParameter("type", URI.create(Vocabulary.C_OWL_CLASS_A)) + .setParameter("x", entity.getUri()) + .getSingleResult(); assertEquals(entity.getUri(), result.getUri()); } } diff --git a/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/LiveOntologyStatementExecutor.java b/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/LiveOntologyStatementExecutor.java index 820c0bfbf..186766c1b 100644 --- a/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/LiveOntologyStatementExecutor.java +++ b/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/LiveOntologyStatementExecutor.java @@ -28,7 +28,7 @@ import org.semanticweb.owlapi.model.OWLObject; import org.semanticweb.owlapi.reasoner.OWLReasoner; -public class LiveOntologyStatementExecutor implements StatementExecutor { +class LiveOntologyStatementExecutor implements StatementExecutor { private final Connector connector; @@ -55,6 +55,8 @@ private QueryResult execute(QuerySpecification query, OntologySnapsho } final OWLReasoner reasonerToUse = query.isDisableInference() ? getNoInferenceReasoner(snapshot) : snapshot.getReasoner(); + // Flush the reasoner to have the latest ontology state for query execution + reasonerToUse.flush(); final OWLAPIv3OWL2Ontology ont = new OWLAPIv3OWL2Ontology(snapshot.getOntologyManager(), snapshot.getOntology(), reasonerToUse); diff --git a/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/StatementExecutor.java b/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/StatementExecutor.java index 660168f73..229d5c23d 100644 --- a/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/StatementExecutor.java +++ b/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/StatementExecutor.java @@ -20,7 +20,7 @@ import cz.cvut.kbss.ontodriver.ResultSet; import cz.cvut.kbss.ontodriver.owlapi.exception.OwlapiDriverException; -public interface StatementExecutor { +interface StatementExecutor { /** * Executes the specified read-only query. diff --git a/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/TransactionalStatementExecutor.java b/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/TransactionalStatementExecutor.java index 7e95d2ca7..2afb0f460 100644 --- a/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/TransactionalStatementExecutor.java +++ b/ontodriver-owlapi/src/main/java/cz/cvut/kbss/ontodriver/owlapi/query/TransactionalStatementExecutor.java @@ -29,7 +29,7 @@ import org.semanticweb.owlapi.model.OWLOntologyManager; import org.semanticweb.owlapi.reasoner.OWLReasoner; -public class TransactionalStatementExecutor implements StatementExecutor { +class TransactionalStatementExecutor implements StatementExecutor { private final OWLOntology ontology; private final OWLOntologyManager ontologyManager; @@ -52,6 +52,8 @@ private QueryResult execute(QuerySpecification query) throws OwlapiDr throw new ReasonerNotAvailableException("Cannot execute query without a reasoner."); } final OWLReasoner reasonerToUse = query.isDisableInference() ? getNoInferenceReasoner() : reasoner; + // Flush the reasoner to have the latest ontology state for query execution + reasoner.flush(); final OWLAPIv3OWL2Ontology ont = new OWLAPIv3OWL2Ontology(ontologyManager, ontology, reasonerToUse); final QueryResult res = OWL2QueryEngine.exec(query.getQuery(), ont);