/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2011, Red Hat, Inc. and/or its affiliates or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat, Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.search.test.embedded.depth; import java.io.Serializable; import java.util.List; import junit.framework.Assert; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.Query; import org.apache.lucene.util.ReaderUtil; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.search.FullTextSession; import org.hibernate.search.Search; import org.hibernate.search.SearchException; import org.hibernate.search.backend.LuceneWork; import org.hibernate.search.indexes.IndexReaderAccessor; import org.hibernate.search.query.dsl.QueryBuilder; import org.hibernate.search.test.SearchTestCase; import org.hibernate.search.test.util.LeakingLuceneBackend; import org.hibernate.testing.SkipForDialect; /** * <pre> * FAMILY: {@link IndexEmbedded#depth} = 2 * * 13 Philippa of Toulouse __________ * 12 William IX of Aquitaine _______|- 6 William X of Aquitaine __ * | * 14 Aimery I of Châttellerault ____ |- 3 Eleanor of Aquitaine _________________________ * 15 Dangereuse de L'Isle Bouchard _|- 7 Aenor de Châtellerault __| | * | * | * |-1 John of England * 16 Fulk IV of Anjou ______________ | * 17 Bertrade de Montfort __________|- 8 Fulk V of Anjou ________ | * 9 Ermengarde of Maine ____|- 4 Geoffrey V of Anjou _ | * 5 Empress Matilda _____|- 2 Henry II of England _| * </pre> * * <pre> * WORK: {@link IndexEmbedded#depth} = 1 * * 20 Technical Manager____________ * 21 Leasing Manager _____________|-18 Real estate director _ * | * 22 Financial Analyst ___________ |-1 John of England * 23 Internal Audit Manager ______|-19 Financial Director ___| * </pre> * @author Davide D'Alto * @author Sanne Grinovero */ @SkipForDialect(comment="looks like a database deadlock", value=org.hibernate.dialect.SybaseASE15Dialect.class, jiraKey="HSEARCH-1107") public class WorkDoneOnEntitiesTest extends SearchTestCase { private Session session = null; public void testEmployeesIndexingInDepth() throws Exception { List<WorkingPerson> result = search( session, "employees.name", "Technical Manager" ); assertEquals( "Unexpected number of results", 1, result.size() ); assertEquals( "Should be able to index field inside depth and in path", "Real estate director", result.get( 0 ).name ); checkRawIndexFields(); } public void testParentsIndexingInDepth() throws Exception { List<WorkingPerson> result = search( session, "parents.parents.name", "Empress Matilda" ); assertEquals( "Unexpected number of results", 1, result.size() ); assertEquals( "Should be able to index field inside depth and in path", "John of England", result.get( 0 ).name ); result = search( session, "parents.parents.parents.name", "Ermengarde of Maine" ); assertEquals( "Unexpected number of results", 1, result.size() ); checkRawIndexFields(); } public void testNoWorkShouldBeExecutedOnPerson() throws Exception { renamePerson( session, 17, "Montford" ); checkRawIndexFields(); assertEquals( 0, countWorksDoneOnPersonId( 1 ) ); } public void testWorkShouldBeExecutedOnPerson() throws Exception { renamePerson( session, 6, "William" ); checkRawIndexFields(); assertEquals( 1, countWorksDoneOnPersonId( 1 ) ); } public void testNoWorkShouldBeExecutedOnEmployee() throws Exception { renamePerson( session, 23, "LM" ); checkRawIndexFields(); assertEquals( 0, countWorksDoneOnPersonId( 1 ) ); } public void testWorkShouldBeExecutedOnEmployee() throws Exception { renamePerson( session, 19, "FM" ); checkRawIndexFields(); assertEquals( 1, countWorksDoneOnPersonId( 1 ) ); } public void testShouldNotIndexParentsBeyondDepth() throws Exception { try { // fails only if DSL fails: search( session, "parents.parents.parents.parents.name", "Bertrade de Montfort" ); fail( "Should not index a field if it is beyond the depth threshold" ); } catch ( SearchException e ) { } checkRawIndexFields(); } public void testShouldNotIndexBeyondMixedPathDepth() throws Exception { try { // fails only if DSL fails: search( session, "parents.employees.employees.name", "Techincal Manager" ); fail( "Should not index a field if it is beyond the depth threshold, considering minimum depth along paths" ); } catch ( SearchException e ) { } checkRawIndexFields(); } public void testShouldNotIndexEmployeesBeyondDepth() throws Exception { try { search( session, "employees.employees.name", "Techincal Manager" ); fail( "Should not index a field if it is beyond the depth threshold" ); } catch ( SearchException e ) { } checkRawIndexFields(); } private void checkRawIndexFields() { // check raw index as well: Assert.assertTrue( indexContainsField( "name" ) ); Assert.assertTrue( indexContainsField( "employees.name" ) ); Assert.assertTrue( indexContainsField( "parents.name" ) ); Assert.assertTrue( indexContainsField( "parents.parents.name" ) ); Assert.assertTrue( indexContainsField( "parents.parents.parents.name" ) ); Assert.assertTrue( indexContainsField( "parents.employees.name" ) ); Assert.assertTrue( indexContainsField( "parents.parents.employees.name" ) ); Assert.assertFalse( indexContainsField( "employees.employees.name" ) ); Assert.assertFalse( indexContainsField( "employees.parents.name" ) ); Assert.assertFalse( indexContainsField( "parents.employees.employees.name" ) ); Assert.assertFalse( indexContainsField( "parents.parents.parents.employees.name" ) ); } private boolean indexContainsField(String fieldName) { IndexReaderAccessor indexReaderAccessor = getSearchFactory().getIndexReaderAccessor(); IndexReader indexReader = indexReaderAccessor.open( WorkingPerson.class ); try { return ReaderUtil.getIndexedFields( indexReader ).contains( fieldName ); } finally { indexReaderAccessor.close( indexReader ); } } @Override public void setUp() throws Exception { super.setUp(); session = openSession(); Transaction transaction = session.beginTransaction(); WorkingPerson[] ps = new WorkingPerson[27]; // array index starting from 1 to match ids of picture at http://en.wikipedia.org/wiki/John,_King_of_England ps[1] = new WorkingPerson( 1, "John of England" ); ps[2] = new WorkingPerson( 2, "Henry II of England" ); ps[3] = new WorkingPerson( 3, "Eleanor of Aquitaine" ); ps[4] = new WorkingPerson( 4, "Geoffrey V of Anjou" ); ps[5] = new WorkingPerson( 5, "Empress Matilda" ); ps[6] = new WorkingPerson( 6, "William X of Aquitaine" ); ps[7] = new WorkingPerson( 7, "Aenor de Châtellerault" ); ps[8] = new WorkingPerson( 8, "Fulk V of Anjou" ); ps[9] = new WorkingPerson( 9, "Ermengarde of Maine" ); ps[10] = new WorkingPerson( 10, "Henry I of England" ); ps[11] = new WorkingPerson( 11, "Matilda of Scotland" ); ps[12] = new WorkingPerson( 12, "William IX of Aquitaine" ); ps[13] = new WorkingPerson( 13, "Philippa of Toulouse" ); ps[14] = new WorkingPerson( 14, "Aimery I of Châttellerault" ); ps[15] = new WorkingPerson( 15, "Dangereuse de L'Isle Bouchard" ); ps[16] = new WorkingPerson( 16, "Fulk IV of Anjou" ); ps[17] = new WorkingPerson( 17, "Bertrade de Montfort" ); ps[18] = new WorkingPerson( 18, "Real estate director" ); ps[19] = new WorkingPerson( 19, "Financial Director" ); ps[20] = new WorkingPerson( 20, "Technical Manager" ); ps[21] = new WorkingPerson( 21, "Leasing Manager" ); ps[22] = new WorkingPerson( 22, "Financial Analyst" ); ps[23] = new WorkingPerson( 23, "Internal Audit Manager" ); ps[24] = new WorkingPerson( 24, "Slave of Henry II" ); ps[25] = new WorkingPerson( 25, "Slave of Geoffrey V" ); ps[26] = new WorkingPerson( 26, "Assistant of Slave of Geoffrey V" ); ps[1].addParents( ps[2], ps[3] ); ps[2].addParents( ps[4], ps[5] ); ps[4].addParents( ps[8], ps[9] ); ps[8].addParents( ps[16], ps[17] ); ps[5].addParents( ps[10], ps[11] ); ps[3].addParents( ps[6], ps[7] ); ps[6].addParents( ps[12], ps[13] ); ps[7].addParents( ps[14], ps[15] ); ps[1].addEmployees( ps[18], ps[19] ); ps[2].addEmployees( ps[24] ); ps[5].addEmployees( ps[25] ); ps[25].addEmployees( ps[26] ); ps[18].addEmployees( ps[20], ps[21] ); ps[19].addEmployees( ps[22], ps[23] ); for ( int i = 1; i < ps.length; i++ ) { session.save( ps[i] ); } transaction.commit(); LeakingLuceneBackend.reset(); } @Override public void tearDown() throws Exception { session.clear(); deleteAll( session, WorkingPerson.class ); session.close(); LeakingLuceneBackend.reset(); super.tearDown(); } private List<WorkingPerson> search(Session s, String field, String value) { FullTextSession session = Search.getFullTextSession( s ); @SuppressWarnings("unchecked") List<WorkingPerson> result = session .createFullTextQuery( searchQuery( field, value, session ) ) .list(); return result; } private Query searchQuery(String field, String value, FullTextSession session) { QueryBuilder queryBuilder = session.getSearchFactory().buildQueryBuilder() .forEntity( WorkingPerson.class ).get(); return queryBuilder.keyword().onField( field ).matching( value ).createQuery(); } private void deleteAll(Session s, Class<?>... classes) { Transaction tx = s.beginTransaction(); for ( Class<?> each : classes ) { List<?> list = s.createCriteria( each ).list(); for ( Object object : list ) { s.delete( object ); } } tx.commit(); } private void renamePerson(Session s, Integer id, String newName) { Transaction transaction = s.beginTransaction(); WorkingPerson person = (WorkingPerson) s.load( WorkingPerson.class, id ); person.name = newName; transaction.commit(); } private int countWorksDoneOnPersonId(Integer pk) { List<LuceneWork> processedQueue = LeakingLuceneBackend.getLastProcessedQueue(); int count = 0; for ( LuceneWork luceneWork : processedQueue ) { Serializable id = luceneWork.getId(); if ( pk.equals( id ) ) { count++; } } return count; } @Override protected Class<?>[] getAnnotatedClasses() { return new Class<?>[] { WorkingPerson.class }; } protected void configure(Configuration cfg) { super.configure( cfg ); cfg.setProperty( "hibernate.search.default.worker.backend", LeakingLuceneBackend.class.getName() ); } }