From d34f951ef067d4293f922fae2c7257a9efe42b8e Mon Sep 17 00:00:00 2001 From: briancaruso Date: Fri, 15 Jul 2011 15:04:31 +0000 Subject: [PATCH] Adding object property statement consideration to search index updating NIHVIVO-2910 --- .../AdditionalURIsForObjectProperties.java | 144 ++++++++++++++++++ .../vitro/webapp/search/solr/SolrSetup.java | 2 + ...AdditionalURIsForObjectPropertiesTest.java | 101 ++++++++++++ 3 files changed, 247 insertions(+) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/search/indexing/AdditionalURIsForObjectProperties.java create mode 100644 webapp/test/edu/cornell/mannlib/vitro/webapp/search/indexing/AdditionalURIsForObjectPropertiesTest.java diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/indexing/AdditionalURIsForObjectProperties.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/indexing/AdditionalURIsForObjectProperties.java new file mode 100644 index 000000000..4d14dafbc --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/indexing/AdditionalURIsForObjectProperties.java @@ -0,0 +1,144 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.search.indexing; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.hp.hpl.jena.query.Query; +import com.hp.hpl.jena.query.QueryExecution; +import com.hp.hpl.jena.query.QueryExecutionFactory; +import com.hp.hpl.jena.query.QueryFactory; +import com.hp.hpl.jena.query.QuerySolution; +import com.hp.hpl.jena.query.QuerySolutionMap; +import com.hp.hpl.jena.query.ResultSet; +import com.hp.hpl.jena.query.Syntax; +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.rdf.model.RDFNode; +import com.hp.hpl.jena.rdf.model.Resource; +import com.hp.hpl.jena.rdf.model.ResourceFactory; +import com.hp.hpl.jena.rdf.model.Statement; +import com.hp.hpl.jena.shared.Lock; +import com.hp.hpl.jena.vocabulary.RDFS; + +import edu.cornell.mannlib.vitro.webapp.search.beans.AdditionalURIsToIndex; + +/** + * For a given statement, return the URIs that may need to be updated in + * the search index because of their object property relations to the resources + * in the statement. + * + * Context nodes are not handled here. They are taken care of in AdditionalURIsForContextNodex. + */ +public class AdditionalURIsForObjectProperties implements AdditionalURIsToIndex { + protected static final Log log = LogFactory.getLog(AdditionalURIsForObjectProperties.class); + + protected Model model; + + public AdditionalURIsForObjectProperties( Model model){ + this.model = model; + } + + @Override + public List findAdditionalURIsToIndex(Statement stmt) { + if( stmt == null ) + return Collections.emptyList(); + + if( stmt.getObject().isLiteral() ) + return doDataPropertyStmt( stmt ); + else + return doObjectPropertyStmt( stmt ); + } + + protected List doObjectPropertyStmt(Statement stmt) { + // Only need to consider the object since the subject + // will already be updated in search index as part of + // SearchReindexingListener. + + // Also, context nodes are not handled here. They are + // taken care of in AdditionalURIsForContextNodex. + if( stmt.getObject().isURIResource() ) + return Collections.singletonList( stmt.getObject().as(Resource.class).getURI() ); + else + return Collections.emptyList(); + } + + protected List doDataPropertyStmt(Statement stmt) { + + if( RDFS.label.equals( stmt.getPredicate() )){ + // If the property is rdfs:labe then we need to update + // all the individuals related by object properties. This + // does not need to account for context nodes as that + // is handled in AdditionalURIsForContextNodex. + if( stmt.getSubject().isURIResource() ){ + return allIndividualsRelatedByObjectPropertyStmts( stmt.getSubject().getURI() ); + }else{ + log.debug("ignored bnode"); + return Collections.emptyList(); + } + }else{ + // This class does not need to account for context nodes because that + // is handled in AdditionalURIsForContextNodex. + return Collections.emptyList(); + } + + } + + protected List allIndividualsRelatedByObjectPropertyStmts( String uri ) { + List additionalUris = new ArrayList(); + + QuerySolutionMap initialBinding = new QuerySolutionMap(); + Resource uriResource = ResourceFactory.createResource(uri); + initialBinding.add("uri", uriResource); + + Query sparqlQuery = QueryFactory.create( QUERY_FOR_RELATED ); + model.getLock().enterCriticalSection(Lock.READ); + try{ + QueryExecution qExec = QueryExecutionFactory.create(sparqlQuery, model, initialBinding); + try{ + ResultSet results = qExec.execSelect(); + while(results.hasNext()){ + QuerySolution soln = results.nextSolution(); + Iterator iter = soln.varNames() ; + while( iter.hasNext()){ + String name = iter.next(); + RDFNode node = soln.get( name ); + if( node != null ){ + if( node.isURIResource() ){ + additionalUris.add( node.as( Resource.class ).getURI() ); + }else{ + log.warn( "value from query for var " + name + " was not a URIResource, it was " + node); + } + }else{ + log.warn("value for query for var " + name + " was null"); + } + } + } + }catch(Throwable t){ + log.error(t,t); + } finally{ + qExec.close(); + } + }finally{ + model.getLock().leaveCriticalSection(); + } + return additionalUris; + } + + protected static final String prefixs = + "prefix owl: \n" + + "prefix rdf: \n" + + "prefix rdfs: \n"; + + protected final String QUERY_FOR_RELATED = + prefixs + + "SELECT ?related WHERE { \n" + + " ?uri ?p ?related \n " + + " filter( isURI( ?related ) && ?p != rdf:type ) \n" + + "}" ; +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/SolrSetup.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/SolrSetup.java index 4fb7e8571..c1bfe6453 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/SolrSetup.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/search/solr/SolrSetup.java @@ -36,6 +36,7 @@ import edu.cornell.mannlib.vitro.webapp.search.beans.FileBasedProhibitedFromSear import edu.cornell.mannlib.vitro.webapp.search.beans.IndividualProhibitedFromSearchImpl; import edu.cornell.mannlib.vitro.webapp.search.beans.ProhibitedFromSearch; import edu.cornell.mannlib.vitro.webapp.search.indexing.AdditionalURIsForContextNodes; +import edu.cornell.mannlib.vitro.webapp.search.indexing.AdditionalURIsForObjectProperties; import edu.cornell.mannlib.vitro.webapp.search.indexing.IndexBuilder; import edu.cornell.mannlib.vitro.webapp.search.indexing.SearchReindexingListener; import edu.cornell.mannlib.vitro.webapp.servlet.setup.AbortStartup; @@ -113,6 +114,7 @@ public class SolrSetup implements javax.servlet.ServletContextListener{ //make objects that will find additional URIs for context nodes etc List uriFinders = new ArrayList(); + uriFinders.add( new AdditionalURIsForObjectProperties(jenaOntModel) ); uriFinders.add( new AdditionalURIsForContextNodes(jenaOntModel) ); // set up listeners so search index builder is notified of changes to model diff --git a/webapp/test/edu/cornell/mannlib/vitro/webapp/search/indexing/AdditionalURIsForObjectPropertiesTest.java b/webapp/test/edu/cornell/mannlib/vitro/webapp/search/indexing/AdditionalURIsForObjectPropertiesTest.java new file mode 100644 index 000000000..3a5a5f6f3 --- /dev/null +++ b/webapp/test/edu/cornell/mannlib/vitro/webapp/search/indexing/AdditionalURIsForObjectPropertiesTest.java @@ -0,0 +1,101 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.search.indexing; + +import java.io.StringReader; +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.hp.hpl.jena.rdf.model.Model; +import com.hp.hpl.jena.rdf.model.ModelFactory; +import com.hp.hpl.jena.rdf.model.ResourceFactory; +import com.hp.hpl.jena.vocabulary.OWL; +import com.hp.hpl.jena.vocabulary.RDFS; + +public class AdditionalURIsForObjectPropertiesTest { + + Model model; + + String testNS = "http://example.com/test#"; + String n3 = "" + + "@prefix owl: .\n" + + "@prefix rdf: . \n" + + "@prefix rdfs: . \n" + + "@prefix test: <"+ testNS + "> . \n" + + "\n" + + "test:bob rdfs:label \"Mr Bob\" . \n" + + "test:bob test:hatsize \"8 1/2 inches\" . \n" + + "test:bob test:likes test:icecream . \n" + + "test:bob test:likes test:onions . \n" + + "test:bob test:likes test:cheese . \n" + + "test:bob a test:Person . \n" + + "test:bob a owl:Thing . \n" + + "test:bob test:likes [ rdfs:label \"this is a blank node\" ] . "; + + @Before + public void setUp() throws Exception { + model = ModelFactory.createDefaultModel(); + model.read(new StringReader(n3 ), null , "N3"); + } + + @Test + public void testChangeOfRdfsLabel() { + AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(model); + List uris = aufop.findAdditionalURIsToIndex( + ResourceFactory.createStatement( + ResourceFactory.createResource(testNS + "bob"), + RDFS.label, + ResourceFactory.createPlainLiteral("Some new label for bob"))); + + Assert.assertNotNull(uris); + Assert.assertTrue("uris was empty", uris.size() > 0 ); + + Assert.assertTrue("uris didn't not contain test:onions", uris.contains(testNS+"onions")); + Assert.assertTrue("uris didn't not contain test:cheese", uris.contains(testNS+"cheese")); + Assert.assertTrue("uris didn't not contain test:icecream", uris.contains(testNS+"icecream")); + + Assert.assertTrue("uris contained test:Person", !uris.contains(testNS+"Person")); + Assert.assertTrue("uris contained owl:Thing", !uris.contains( OWL.Thing.getURI() )); + + Assert.assertEquals(3, uris.size()); + } + + @Test + public void testChangeOfObjPropStmt() { + + AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(model); + List uris = aufop.findAdditionalURIsToIndex( + ResourceFactory.createStatement( + ResourceFactory.createResource(testNS + "bob"), + ResourceFactory.createProperty(testNS+"likes"), + ResourceFactory.createResource(testNS+"cheese"))); + + Assert.assertNotNull(uris); + Assert.assertTrue("uris was empty", uris.size() > 0 ); + + Assert.assertTrue("uris didn't not contain test:cheese", uris.contains(testNS+"cheese")); + + Assert.assertTrue("uris contained test:Person", !uris.contains(testNS+"Person")); + Assert.assertTrue("uris contained owl:Thing", !uris.contains( OWL.Thing.getURI() )); + Assert.assertTrue("uris contained test:onions", !uris.contains(testNS+"onions")); + Assert.assertTrue("uris contained test:icecream", !uris.contains(testNS+"icecream")); + + Assert.assertEquals(1, uris.size()); + } + + @Test + public void testOfDataPropChange() { + AdditionalURIsForObjectProperties aufop = new AdditionalURIsForObjectProperties(model); + List uris = aufop.findAdditionalURIsToIndex( + ResourceFactory.createStatement( + ResourceFactory.createResource(testNS + "bob"), + ResourceFactory.createProperty(testNS+"hatsize"), + ResourceFactory.createPlainLiteral("Some new hat size for bob"))); + + Assert.assertNotNull(uris); + Assert.assertTrue("uris was not empty", uris.size() == 0 ); + } +}