[hibernate-commits] Hibernate SVN: r17533 - in search/trunk/src: test/java/org/hibernate/search/test/embedded and 1 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Sep 23 12:12:50 EDT 2009


Author: hardy.ferentschik
Date: 2009-09-23 12:12:46 -0400 (Wed, 23 Sep 2009)
New Revision: 17533

Added:
   search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/
   search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Address.java
   search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Attribute.java
   search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/AttributeValue.java
   search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/NestedEmbeddedTest.java
   search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Person.java
   search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Place.java
   search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Product.java
Modified:
   search/trunk/src/main/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java
Log:
HSEARCH-391 - HSEARCH-391 - made sure the top level indexed entity gets found by recusively calling processContainedInInstances.
Refactored the code by splitting out more methods


Modified: search/trunk/src/main/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java
===================================================================
--- search/trunk/src/main/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java	2009-09-23 10:21:55 UTC (rev 17532)
+++ search/trunk/src/main/java/org/hibernate/search/engine/DocumentBuilderContainedEntity.java	2009-09-23 16:12:46 UTC (rev 17533)
@@ -66,7 +66,7 @@
 	protected final PropertiesMetadata metadata = new PropertiesMetadata();
 	protected final XClass beanClass;
 	protected Set<Class<?>> mappedSubclasses = new HashSet<Class<?>>();
-	protected ReflectionManager reflectionManager; //available only during initializationa and post-initialization
+	protected ReflectionManager reflectionManager; //available only during initialization and post-initialization
 	protected int level = 0;
 	protected int maxLevel = Integer.MAX_VALUE;
 	protected final ScopedAnalyzer analyzer = new ScopedAnalyzer();
@@ -122,28 +122,28 @@
 	private void initializeClass(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix,
 								 Set<XClass> processedClasses, InitContext context) {
 		List<XClass> hierarchy = new ArrayList<XClass>();
-		for ( XClass currClass = clazz; currClass != null; currClass = currClass.getSuperclass() ) {
-			hierarchy.add( currClass );
+		for ( XClass currentClass = clazz; currentClass != null; currentClass = currentClass.getSuperclass() ) {
+			hierarchy.add( currentClass );
 		}
 
 		/*
 		* Iterate the class hierarchy top down. This allows to override the default analyzer for the properties if the class holds one
 		*/
 		for ( int index = hierarchy.size() - 1; index >= 0; index-- ) {
-			XClass currClass = hierarchy.get( index );
+			XClass currentClass = hierarchy.get( index );
 
-			initalizeClassLevelAnnotations( currClass, propertiesMetadata, isRoot, prefix, context );
+			initializeClassLevelAnnotations( currentClass, propertiesMetadata, isRoot, prefix, context );
 
 			// rejecting non properties (ie regular methods) because the object is loaded from Hibernate,
 			// so indexing a non property does not make sense
-			List<XProperty> methods = currClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
+			List<XProperty> methods = currentClass.getDeclaredProperties( XClass.ACCESS_PROPERTY );
 			for ( XProperty method : methods ) {
 				initializeMemberLevelAnnotations(
 						method, propertiesMetadata, isRoot, prefix, processedClasses, context
 				);
 			}
 
-			List<XProperty> fields = currClass.getDeclaredProperties( XClass.ACCESS_FIELD );
+			List<XProperty> fields = currentClass.getDeclaredProperties( XClass.ACCESS_FIELD );
 			for ( XProperty field : fields ) {
 				initializeMemberLevelAnnotations(
 						field, propertiesMetadata, isRoot, prefix, processedClasses, context
@@ -162,7 +162,7 @@
 	 * @param prefix The current prefix used for the <code>Document</code> field names.
 	 * @param context Handle to default configuration settings.
 	 */
-	private void initalizeClassLevelAnnotations(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix, InitContext context) {
+	private void initializeClassLevelAnnotations(XClass clazz, PropertiesMetadata propertiesMetadata, boolean isRoot, String prefix, InitContext context) {
 
 		// check for a class level specified analyzer
 		Analyzer analyzer = getAnalyzer( clazz, context );
@@ -176,8 +176,8 @@
 		// Check for any ClassBridges annotation.
 		ClassBridges classBridgesAnn = clazz.getAnnotation( ClassBridges.class );
 		if ( classBridgesAnn != null ) {
-			ClassBridge[] cbs = classBridgesAnn.value();
-			for ( ClassBridge cb : cbs ) {
+			ClassBridge[] classBridges = classBridgesAnn.value();
+			for ( ClassBridge cb : classBridges ) {
 				bindClassBridgeAnnotation( prefix, propertiesMetadata, cb, context );
 			}
 		}
@@ -256,15 +256,15 @@
 	}
 
 	private void checkForAnalyzerDiscriminator(XAnnotatedElement annotatedElement, PropertiesMetadata propertiesMetadata) {
-		AnalyzerDiscriminator discriminiatorAnn = annotatedElement.getAnnotation( AnalyzerDiscriminator.class );
-		if ( discriminiatorAnn != null ) {
+		AnalyzerDiscriminator discriminatorAnn = annotatedElement.getAnnotation( AnalyzerDiscriminator.class );
+		if ( discriminatorAnn != null ) {
 			if ( propertiesMetadata.discriminator != null ) {
 				throw new SearchException(
 						"Multiple AnalyzerDiscriminator defined in the same class hierarchy: " + beanClass.getName()
 				);
 			}
 
-			Class<? extends Discriminator> discriminatorClass = discriminiatorAnn.impl();
+			Class<? extends Discriminator> discriminatorClass = discriminatorAnn.impl();
 			try {
 				propertiesMetadata.discriminator = discriminatorClass.newInstance();
 			}
@@ -372,7 +372,7 @@
 				String localPrefix = buildEmbeddedPrefix( prefix, embeddedAnn, member );
 				initializeClass( elementClass, metadata, false, localPrefix, processedClasses, context );
 				/**
-				 * We will only index the "expected" type but that's OK, HQL cannot do downcasting either
+				 * We will only index the "expected" type but that's OK, HQL cannot do down-casting either
 				 */
 				if ( member.isArray() ) {
 					propertiesMetadata.embeddedContainers.add( PropertiesMetadata.Container.ARRAY );
@@ -596,8 +596,6 @@
 		return strategy;
 	}
 
-
-	//TODO could we use T instead of EntityClass?
 	public void addWorkToQueue(Class<T> entityClass, T entity, Serializable id, WorkType workType, List<LuceneWork> queue, SearchFactoryImplementor searchFactoryImplementor) {
 		/**
 		 * When references are changed, either null or another one, we expect dirty checking to be triggered (both sides
@@ -605,75 +603,101 @@
 		 * When the internal object is changed, we apply the {Add|Update}Work on containedIns
 		 */
 		if ( workType.searchForContainers() ) {
-			processContainedIn( entity, queue, metadata, searchFactoryImplementor );
+			processContainedInInstances( entity, queue, metadata, searchFactoryImplementor );
 		}
 	}
 
-	private void processContainedIn(Object instance, List<LuceneWork> queue, PropertiesMetadata metadata, SearchFactoryImplementor searchFactoryImplementor) {
+	/**
+	 * If we have a work instance we have to check whether the intance to be indexed is contained in any other indexed entities.
+	 *
+	 * @param instance The instance to be indexed
+	 * @param queue the current work queue
+	 * @param metadata metadata
+	 * @param searchFactoryImplementor the current session
+	 */
+	private <T> void processContainedInInstances(Object instance, List<LuceneWork> queue, PropertiesMetadata metadata, SearchFactoryImplementor searchFactoryImplementor) {
 		for ( int i = 0; i < metadata.containedInGetters.size(); i++ ) {
 			XMember member = metadata.containedInGetters.get( i );
 			Object value = ReflectionHelper.getMemberValue( instance, member );
+
 			if ( value == null ) {
 				continue;
 			}
 
 			if ( member.isArray() ) {
-				for ( Object arrayValue : ( Object[] ) value ) {
-					//highly inneficient but safe wrt the actual targeted class
-					Class<?> valueClass = Hibernate.getClass( arrayValue );
-					DocumentBuilderIndexedEntity<?> builderIndexedEntity = searchFactoryImplementor.getDocumentBuilderIndexedEntity(
-							valueClass
-					);
-					if ( builderIndexedEntity == null ) {
-						continue;
-					}
-					processContainedInValue(
-							arrayValue, queue, valueClass,
-							builderIndexedEntity, searchFactoryImplementor
-					);
+				@SuppressWarnings("unchecked")
+				T[] array = ( T[] ) value;
+				for ( T arrayValue : array ) {
+					processSingleContainedInInstance( queue, searchFactoryImplementor, arrayValue );
 				}
 			}
 			else if ( member.isCollection() ) {
-				Collection collection;
-				if ( Map.class.equals( member.getCollectionClass() ) ) {
-					//hum
-					collection = ( ( Map ) value ).values();
+				Collection<T> collection = getActualCollection( member, value );
+				for ( T collectionValue : collection ) {
+					processSingleContainedInInstance( queue, searchFactoryImplementor, collectionValue );
 				}
-				else {
-					collection = ( Collection ) value;
-				}
-				for ( Object collectionValue : collection ) {
-					//highly inneficient but safe wrt the actual targeted class
-					Class<?> valueClass = Hibernate.getClass( collectionValue );
-					DocumentBuilderIndexedEntity<?> builderIndexedEntity = searchFactoryImplementor.getDocumentBuilderIndexedEntity(
-							valueClass
-					);
-					if ( builderIndexedEntity == null ) {
-						continue;
-					}
-					processContainedInValue(
-							collectionValue, queue, valueClass,
-							builderIndexedEntity, searchFactoryImplementor
-					);
-				}
 			}
 			else {
-				Class<?> valueClass = Hibernate.getClass( value );
-				DocumentBuilderIndexedEntity<?> builderIndexedEntity = searchFactoryImplementor.getDocumentBuilderIndexedEntity(
-						valueClass
-				);
-				if ( builderIndexedEntity == null ) {
-					continue;
-				}
-				processContainedInValue( value, queue, valueClass, builderIndexedEntity, searchFactoryImplementor );
+				processSingleContainedInInstance( queue, searchFactoryImplementor, value );
 			}
 		}
-		//an embedded cannot have a useful @ContainedIn (no shared reference)
-		//do not walk through them
 	}
 
-	private void processContainedInValue(Object value, List<LuceneWork> queue, Class<?> valueClass,
-										 DocumentBuilderIndexedEntity builderIndexedEntity, SearchFactoryImplementor searchFactoryImplementor) {
+	/**
+	 * A {@code XMember } instance treats a map as a collection as well in which case the map values are returned as
+	 * collection.
+	 *
+	 * @param member The member instance
+	 * @param value The value
+	 *
+	 * @return The {@code value} casted to collection or in case of {@code value} being a map the map values as collection.
+	 */
+	private <T> Collection<T> getActualCollection(XMember member, Object value) {
+		Collection<T> collection;
+		if ( Map.class.equals( member.getCollectionClass() ) ) {
+			//hum
+			@SuppressWarnings("unchecked")
+			Collection<T> tmpCollection = ( ( Map<?, T> ) value ).values();
+			collection = tmpCollection;
+		}
+		else {
+			@SuppressWarnings("unchecked")
+			Collection<T> tmpCollection = ( Collection<T> ) value;
+			collection = tmpCollection;
+		}
+		return collection;
+	}
+
+	private <T> void processSingleContainedInInstance(List<LuceneWork> queue, SearchFactoryImplementor searchFactoryImplementor, T value) {
+		@SuppressWarnings("unchecked")
+		Class<T> valueClass = Hibernate.getClass( value );
+		DocumentBuilderIndexedEntity<T> builderIndexedEntity =
+				searchFactoryImplementor.getDocumentBuilderIndexedEntity( valueClass );
+
+		// it could be we have a nested @IndexedEmbedded chain in which case we have to find the top level @Indexed entities
+		if ( builderIndexedEntity == null ) {
+			DocumentBuilderContainedEntity<T> builderContainedEntity =
+					searchFactoryImplementor.getDocumentBuilderContainedEntity( valueClass );
+			if ( builderContainedEntity != null ) {
+				processContainedInInstances( value, queue, builderContainedEntity.metadata, searchFactoryImplementor );
+			}
+		}
+		else {
+			addWorkForEmbeddedValue( value, queue, valueClass, builderIndexedEntity, searchFactoryImplementor );
+		}
+	}
+
+	/**
+	 * Create a {@code LuceneWork} instance of the entity which needs updating due to the embedded instance change.
+	 *
+	 * @param value The value to index
+	 * @param queue The current (Lucene) work queue
+	 * @param valueClass The class of the value
+	 * @param builderIndexedEntity the document builder for the entity which needs updating due to a update event of the embedded instance
+	 * @param searchFactoryImplementor the search factory.
+	 */
+	private <T> void addWorkForEmbeddedValue(T value, List<LuceneWork> queue, Class<T> valueClass,
+											 DocumentBuilderIndexedEntity<T> builderIndexedEntity, SearchFactoryImplementor searchFactoryImplementor) {
 		Serializable id = ( Serializable ) ReflectionHelper.getMemberValue( value, builderIndexedEntity.idGetter );
 		builderIndexedEntity.addWorkToQueue( valueClass, value, id, WorkType.UPDATE, queue, searchFactoryImplementor );
 	}
@@ -727,7 +751,7 @@
 		public Discriminator discriminator;
 		public XMember discriminatorGetter;
 		public BoostStrategy classBoostStrategy;
-		
+
 		public final List<String> fieldNames = new ArrayList<String>();
 		public final List<XMember> fieldGetters = new ArrayList<XMember>();
 		public final List<FieldBridge> fieldBridges = new ArrayList<FieldBridge>();

Added: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Address.java
===================================================================
--- search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Address.java	                        (rev 0)
+++ search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Address.java	2009-09-23 16:12:46 UTC (rev 17533)
@@ -0,0 +1,87 @@
+// $Id:$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed 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.hibernate.search.test.embedded.nested;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+
+import org.hibernate.search.annotations.ContainedIn;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+
+/**
+ * @author Hardy Ferentschik
+ */
+ at Entity
+public class Address {
+	@Id
+	@GeneratedValue
+	private Long id;
+
+	@Field(index = Index.TOKENIZED)
+	private String street;
+
+	@Field(index = Index.TOKENIZED)
+	private String city;
+
+	@ContainedIn
+	@OneToMany(mappedBy = "address")
+	private Set<Place> places;
+
+	public Address(String street, String city) {
+		this();
+		this.street = street;
+		this.city = city;
+	}
+
+	private Address() {
+		places = new HashSet<Place>();
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public String getStreet() {
+		return street;
+	}
+
+	public String getCity() {
+		return city;
+	}
+
+	public Set<Place> getPlaces() {
+		return places;
+	}
+
+	public void addPlace(Place place) {
+		places.add( place );
+	}
+
+	public void setStreet(String street) {
+		this.street = street;
+	}
+
+	public void setCity(String city) {
+		this.city = city;
+	}
+}


Property changes on: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Address.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Attribute.java
===================================================================
--- search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Attribute.java	                        (rev 0)
+++ search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Attribute.java	2009-09-23 16:12:46 UTC (rev 17533)
@@ -0,0 +1,79 @@
+// $Id:$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed 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.hibernate.search.test.embedded.nested;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+
+import org.hibernate.search.annotations.ContainedIn;
+import org.hibernate.search.annotations.IndexedEmbedded;
+
+/**
+ * @author Hardy Ferentschik
+ */
+ at Entity
+public class Attribute {
+
+	@Id
+	@GeneratedValue
+	private long id;
+
+	@ManyToOne
+	@ContainedIn
+	private Product product;
+
+	@OneToMany(mappedBy = "attribute", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
+	@IndexedEmbedded
+	private List<AttributeValue> values;
+
+	private Attribute() {
+		values = new ArrayList<AttributeValue>();
+	}
+
+	public Attribute(Product product) {
+		this.product = product;
+		values = new ArrayList<AttributeValue>();
+	}
+
+	public long getId() {
+		return id;
+	}
+
+	public Product getProduct() {
+		return product;
+	}
+
+	public void setProduct(Product product) {
+		this.product = product;
+	}
+
+	public List<AttributeValue> getValues() {
+		return values;
+	}
+
+	public void setValue(AttributeValue value) {
+		values.add( value );
+	}
+}


Property changes on: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Attribute.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/AttributeValue.java
===================================================================
--- search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/AttributeValue.java	                        (rev 0)
+++ search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/AttributeValue.java	2009-09-23 16:12:46 UTC (rev 17533)
@@ -0,0 +1,77 @@
+// $Id:$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed 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.hibernate.search.test.embedded.nested;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+
+import org.hibernate.search.annotations.ContainedIn;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.Store;
+
+/**
+ * @author Hardy Ferentschik
+ */
+ at Entity
+public class AttributeValue {
+
+	@Id
+	@GeneratedValue
+	private long id;
+
+	@ManyToOne(targetEntity = Attribute.class, fetch = FetchType.EAGER)
+	@ContainedIn
+	private Attribute attribute;
+
+	@Column(name = "_value")
+	@Field(index = Index.TOKENIZED, store = Store.YES)
+	private String value;
+
+	private AttributeValue() {
+	}
+
+	public AttributeValue(Attribute attribute, String value) {
+		this.attribute = attribute;
+		this.value = value;
+	}
+
+	public long getId() {
+		return id;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public void setValue(String value) {
+		this.value = value;
+	}
+
+	public Attribute getAttribute() {
+		return attribute;
+	}
+
+	public void setAttribute(Attribute attribute) {
+		this.attribute = attribute;
+	}
+}
\ No newline at end of file


Property changes on: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/AttributeValue.java
___________________________________________________________________
Name: svn:keywords
   + Id

Copied: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/NestedEmbeddedTest.java (from rev 17482, search/trunk/src/test/java/org/hibernate/search/test/embedded/EmbeddedTest.java)
===================================================================
--- search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/NestedEmbeddedTest.java	                        (rev 0)
+++ search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/NestedEmbeddedTest.java	2009-09-23 16:12:46 UTC (rev 17533)
@@ -0,0 +1,138 @@
+//$Id$
+package org.hibernate.search.test.embedded.nested;
+
+import java.util.List;
+
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.Query;
+
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.test.SearchTestCase;
+
+
+/**
+ * @author Emmanuel Bernard
+ * @author Hardy Ferentschik
+ */
+public class NestedEmbeddedTest extends SearchTestCase {
+
+	/**
+	 * HSEARCH-391
+	 *
+	 * @throws Exception in case the tests fails
+	 */
+	public void testNestedEmbeddedIndexing() throws Exception {
+		Product product = new Product();
+		Attribute attribute = new Attribute( product );
+		product.setAttribute( attribute );
+		AttributeValue value = new AttributeValue( attribute, "foo" );
+		attribute.setValue( value );
+
+		Session s = openSession();
+		Transaction tx = s.beginTransaction();
+		s.persist( product );
+		tx.commit();
+
+		FullTextSession session = Search.getFullTextSession( s );
+		QueryParser parser = new QueryParser( "attributes.values.value", new StandardAnalyzer() );
+		Query query;
+		List<?> result;
+
+
+		query = parser.parse( "foo" );
+		result = session.createFullTextQuery( query ).list();
+		assertEquals( "unable to find property in attribute value", 1, result.size() );
+
+
+		s.clear();
+		tx = s.beginTransaction();
+
+		product = ( Product ) s.get( Product.class, product.getId() );
+		product.getAttributes().get( 0 ).getValues().get( 0 ).setValue( "bar" );
+		tx.commit();
+
+		s.clear();
+
+		session = Search.getFullTextSession( s );
+
+		query = parser.parse( "foo" );
+		result = session.createFullTextQuery( query, Product.class ).list();
+		assertEquals( "change in embedded not reflected in root index", 0, result.size() );
+
+		query = parser.parse( "bar" );
+		result = session.createFullTextQuery( query, Product.class ).list();
+		assertEquals( "change in embedded not reflected in root index", 1, result.size() );
+
+		s.close();
+	}
+
+
+	/**
+	 * HSEARCH-391
+	 *
+	 * @throws Exception in case the tests fails
+	 */
+	public void testNestedEmbeddedIndexingWithContainedInOnCollection() throws Exception {
+		Person john = new Person( "John Doe" );
+		Place eiffelTower = new Place( "Eiffel Tower" );
+		Address addressEiffel = new Address( "Avenue Gustave Eiffel", "London" );
+		addressEiffel.addPlace( eiffelTower );
+		eiffelTower.setAddress( addressEiffel );
+		john.addPlaceVisited( eiffelTower );
+		eiffelTower.visitedBy( john );
+
+
+		Session s = openSession();
+		Transaction tx = s.beginTransaction();
+		s.persist( john );
+		tx.commit();
+
+		FullTextSession session = Search.getFullTextSession( s );
+		QueryParser parser = new QueryParser( "placesVisited.address.city", new StandardAnalyzer() );
+		Query query;
+		List<?> result;
+
+
+		query = parser.parse( "London" );
+		result = session.createFullTextQuery( query ).list();
+		assertEquals( "unable to find nested indexed value", 1, result.size() );
+
+
+		s.clear();
+		tx = s.beginTransaction();
+
+		john = ( Person ) s.get( Person.class, john.getId() );
+		john.getPlacesVisited().get( 0 ).getAddress().setCity( "Paris" );
+		tx.commit();
+
+		s.clear();
+
+		john = ( Person ) s.get( Person.class, john.getId() );
+
+		session = Search.getFullTextSession( s );
+
+		query = parser.parse( "London" );
+		result = session.createFullTextQuery( query, Person.class ).list();
+		assertEquals( "change in embedded not reflected in root index", 0, result.size() );
+
+		query = parser.parse( "Paris" );
+		result = session.createFullTextQuery( query, Person.class ).list();
+		assertEquals( "change in embedded not reflected in root index", 1, result.size() );
+
+		s.close();
+	}
+
+	protected void configure(org.hibernate.cfg.Configuration cfg) {
+		super.configure( cfg );
+	}
+
+	protected Class<?>[] getMappings() {
+		return new Class[] {
+				Product.class, Attribute.class, AttributeValue.class, Person.class, Place.class, Address.class
+		};
+	}
+}
\ No newline at end of file

Added: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Person.java
===================================================================
--- search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Person.java	                        (rev 0)
+++ search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Person.java	2009-09-23 16:12:46 UTC (rev 17533)
@@ -0,0 +1,71 @@
+// $Id:$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed 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.hibernate.search.test.embedded.nested;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.IndexedEmbedded;
+
+/**
+ * @author Hardy Ferentschik
+ */
+ at Entity
+ at Indexed
+public class Person {
+	@Id
+	@GeneratedValue
+	private long id;
+
+	String name;
+
+	@IndexedEmbedded
+	@ManyToMany(cascade = { CascadeType.ALL })
+	private List<Place> placesVisited;
+
+	private Person() {
+		placesVisited = new ArrayList<Place>( 0 );
+	}
+
+	public Person(String name) {
+		this();
+		this.name = name;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public List<Place> getPlacesVisited() {
+		return placesVisited;
+	}
+
+	public void addPlaceVisited(Place place) {
+		placesVisited.add( place );
+	}
+
+	public long getId() {
+		return id;
+	}
+}


Property changes on: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Person.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Place.java
===================================================================
--- search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Place.java	                        (rev 0)
+++ search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Place.java	2009-09-23 16:12:46 UTC (rev 17533)
@@ -0,0 +1,87 @@
+// $Id:$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed 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.hibernate.search.test.embedded.nested;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.ManyToMany;
+import javax.persistence.OneToOne;
+
+import org.hibernate.search.annotations.ContainedIn;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.IndexedEmbedded;
+
+/**
+ * @author Hardy Ferentschik
+ */
+ at Entity
+public class Place {
+	@Id
+	@GeneratedValue
+	private Long id;
+
+	@Field(index = Index.TOKENIZED)
+	private String name;
+
+	@OneToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
+	@IndexedEmbedded
+	private Address address;
+
+	@ContainedIn
+	@ManyToMany(cascade = { CascadeType.ALL }, mappedBy = "placesVisited")
+	private Set<Person> visitedBy;
+
+	private Place() {
+		this.visitedBy = new HashSet<Person>();
+	}
+
+	public Place(String name) {
+		this();
+		this.name = name;
+	}
+
+	public Address getAddress() {
+		return address;
+	}
+
+	public String getName() {
+
+		return name;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setAddress(Address address) {
+		this.address = address;
+	}
+
+	public void visitedBy(Person person) {
+		visitedBy.add( person );
+	}
+
+	public Set<Person> getVisitedBy() {
+		return visitedBy;
+	}
+}


Property changes on: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Place.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Product.java
===================================================================
--- search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Product.java	                        (rev 0)
+++ search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Product.java	2009-09-23 16:12:46 UTC (rev 17533)
@@ -0,0 +1,61 @@
+// $Id:$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed 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.hibernate.search.test.embedded.nested;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.CascadeType;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.IndexedEmbedded;
+
+/**
+ * @author Hardy Ferentschik
+ */
+ at Entity
+ at Indexed
+public class Product {
+	@Id
+	@GeneratedValue
+	private long id;
+
+	@OneToMany(mappedBy = "product", fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
+	@IndexedEmbedded
+	private List<Attribute> attributes;
+
+	public Product() {
+		attributes = new ArrayList<Attribute>();
+	}
+
+	public long getId() {
+		return id;
+	}
+
+	public List<Attribute> getAttributes() {
+		return attributes;
+	}
+
+	public void setAttribute(Attribute attribute) {
+		attributes.add( attribute );
+	}
+}


Property changes on: search/trunk/src/test/java/org/hibernate/search/test/embedded/nested/Product.java
___________________________________________________________________
Name: svn:keywords
   + Id



More information about the hibernate-commits mailing list