Author: epbernard
Date: 2007-09-21 12:34:06 -0400 (Fri, 21 Sep 2007)
New Revision: 14020
Added:
search/trunk/src/java/org/hibernate/search/annotations/ClassBridges.java
search/trunk/src/test/org/hibernate/search/test/bridge/CatDeptsFieldsClassBridge.java
search/trunk/src/test/org/hibernate/search/test/bridge/Departments.java
search/trunk/src/test/org/hibernate/search/test/bridge/EquipmentType.java
Modified:
search/trunk/build.xml
search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
search/trunk/src/test/org/hibernate/search/test/bridge/ClassBridgeTest.java
Log:
HSEARCH-118 @ClassBridges
Modified: search/trunk/build.xml
===================================================================
--- search/trunk/build.xml 2007-09-21 16:26:29 UTC (rev 14019)
+++ search/trunk/build.xml 2007-09-21 16:34:06 UTC (rev 14020)
@@ -17,7 +17,7 @@
<!-- Name of project and version, used to create filenames -->
<property name="Name" value="Hibernate Search"/>
<property name="name" value="hibernate-search"/>
- <property name="version" value="3.0.0.CR1"/>
+ <property name="version" value="3.0.0.CR1.HSEARCH-116"/>
<property name="javadoc.packagenames"
value="org.hibernate.search.*"/>
<property name="copy.test" value="true"/>
<property name="javac.source" value="1.5"/>
Added: search/trunk/src/java/org/hibernate/search/annotations/ClassBridges.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/ClassBridges.java
(rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/ClassBridges.java 2007-09-21
16:34:06 UTC (rev 14020)
@@ -0,0 +1,22 @@
+package org.hibernate.search.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author John Griffin
+ */
+@Retention( RetentionPolicy.RUNTIME )
+@Target( ElementType.TYPE )
+@Documented
+public @interface ClassBridges {
+ /**
+ * An array of ClassBridge annotations each of
+ * which is to be applied to the class containing
+ * this annotation.
+ */
+ ClassBridge[] value() default {};
+}
\ No newline at end of file
Modified: search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java 2007-09-21
16:26:29 UTC (rev 14019)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentBuilder.java 2007-09-21
16:34:06 UTC (rev 14020)
@@ -34,6 +34,7 @@
import org.hibernate.search.annotations.IndexedEmbedded;
import org.hibernate.search.annotations.Store;
import org.hibernate.search.annotations.ClassBridge;
+import org.hibernate.search.annotations.ClassBridges;
import org.hibernate.search.backend.AddLuceneWork;
import org.hibernate.search.backend.DeleteLuceneWork;
import org.hibernate.search.backend.LuceneWork;
@@ -140,11 +141,21 @@
if ( analyzer != null ) {
propertiesMetadata.analyzer = analyzer;
}
+ // Check for any ClassBridges annotation.
+ ClassBridges classBridgesAnn = currClass.getAnnotation( ClassBridges.class);
+ if (classBridgesAnn != null) {
+ ClassBridge[] cbs = classBridgesAnn.value();
+ for (ClassBridge cb : cbs) {
+ bindClassAnnotation(prefix, propertiesMetadata, cb);
+ }
+ }
+
// Check for any ClassBridge style of annotations.
ClassBridge classBridgeAnn = currClass.getAnnotation(ClassBridge.class);
if (classBridgeAnn != null) {
bindClassAnnotation(prefix, propertiesMetadata, classBridgeAnn);
}
+
//rejecting non properties because the object is loaded from Hibernate, so indexing a
non property does not make sense
List<XProperty> methods = currClass.getDeclaredProperties(
XClass.ACCESS_PROPERTY );
for (XProperty method : methods) {
Added:
search/trunk/src/test/org/hibernate/search/test/bridge/CatDeptsFieldsClassBridge.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/bridge/CatDeptsFieldsClassBridge.java
(rev 0)
+++
search/trunk/src/test/org/hibernate/search/test/bridge/CatDeptsFieldsClassBridge.java 2007-09-21
16:34:06 UTC (rev 14020)
@@ -0,0 +1,40 @@
+package org.hibernate.search.test.bridge;
+
+import java.util.Map;
+
+import org.hibernate.search.bridge.FieldBridge;
+import org.hibernate.search.bridge.ParameterizedBridge;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+
+/**
+ * @author John Griffin
+ */
+public class CatDeptsFieldsClassBridge implements FieldBridge, ParameterizedBridge {
+
+ private String sepChar;
+
+ public void setParameterValues(Map parameters) {
+ this.sepChar = (String) parameters.get( "sepChar" );
+ }
+
+ public void set(String name, Object value, Document document, Field.Store store,
Field.Index index, Float boost) {
+ // In this particular class the name of the new field was passed
+ // from the name field of the ClassBridge Annotation. This is not
+ // a requirement. It just works that way in this instance. The
+ // actual name could be supplied by hard coding it below.
+ Departments dep = (Departments) value;
+ String fieldValue1 = dep.getBranch();
+ if ( fieldValue1 == null ) {
+ fieldValue1 = "";
+ }
+ String fieldValue2 = dep.getNetwork();
+ if ( fieldValue2 == null ) {
+ fieldValue2 = "";
+ }
+ String fieldValue = fieldValue1 + sepChar + fieldValue2;
+ Field field = new Field( name, fieldValue, store, index );
+ if ( boost != null ) field.setBoost( boost );
+ document.add( field );
+ }
+}
Modified: search/trunk/src/test/org/hibernate/search/test/bridge/ClassBridgeTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/bridge/ClassBridgeTest.java 2007-09-21
16:26:29 UTC (rev 14019)
+++ search/trunk/src/test/org/hibernate/search/test/bridge/ClassBridgeTest.java 2007-09-21
16:34:06 UTC (rev 14020)
@@ -1,22 +1,162 @@
package org.hibernate.search.test.bridge;
import java.util.List;
+import java.io.Serializable;
import org.apache.lucene.analysis.SimpleAnalyzer;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Query;
+import org.apache.lucene.document.Document;
import org.hibernate.Transaction;
+import org.hibernate.ScrollableResults;
import org.hibernate.cfg.Configuration;
import org.hibernate.search.Environment;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
+import org.hibernate.search.FullTextQuery;
import org.hibernate.search.test.SearchTestCase;
+import org.hibernate.search.test.query.Employee;
/**
* @author John Griffin
*/
public class ClassBridgeTest extends SearchTestCase {
/**
+ * This tests that a field created by a user-supplied
+ * EquipmentType class has been created and is a translation
+ * from an identifier to a manufacturer name.
+ *
+ * @throws Exception
+ */
+ public void testClassBridges() throws Exception {
+ org.hibernate.Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ s.persist( getDepts1() );
+ s.persist( getDepts2() );
+ s.persist( getDepts3() );
+ s.persist( getDepts4() );
+ s.flush();
+ tx.commit();
+
+ tx = s.beginTransaction();
+ FullTextSession session = Search.createFullTextSession( s );
+
+ // The equipment field is the manufacturer field in the
+ // Departments entity after being massaged by passing it
+ // through the EquipmentType class. This field is in
+ // the Lucene document but not in the Department entity itself.
+ QueryParser parser = new QueryParser( "equipment", new SimpleAnalyzer() );
+
+ // Check the second ClassBridge annotation
+ Query query = parser.parse( "equiptype:Cisco" );
+ org.hibernate.search.FullTextQuery hibQuery = session.createFullTextQuery( query,
Departments.class );
+ List<Departments> result = hibQuery.list();
+ assertNotNull( result );
+ assertEquals( "incorrect number of results returned", 2, result.size() );
+ for (Departments d : result) {
+ assertEquals("incorrect manufacturer", "C", d.getManufacturer());
+ }
+
+ // No data cross-ups.
+ query = parser.parse( "branchnetwork:Kent Lewin" );
+ hibQuery = session.createFullTextQuery( query, Departments.class );
+ result = hibQuery.list();
+ assertNotNull( result );
+ assertTrue( "problem with field cross-ups", result.size() == 0 );
+
+ // Non-ClassBridge field.
+ parser = new QueryParser( "branchHead", new SimpleAnalyzer() );
+ query = parser.parse( "branchHead:Kent Lewin" );
+ hibQuery = session.createFullTextQuery( query, Departments.class );
+ result = hibQuery.list();
+ assertNotNull( result );
+ assertTrue( "incorrect entity returned, wrong branch head", result.size() ==
1 );
+ assertEquals("incorrect entity returned", "Kent Lewin", (
result.get( 0 ) ).getBranchHead());
+
+ // Check other ClassBridge annotation.
+ parser = new QueryParser( "branchnetwork", new SimpleAnalyzer() );
+ query = parser.parse( "branchnetwork:st. george 1D" );
+ hibQuery = session.createFullTextQuery( query, Departments.class );
+ result = hibQuery.list();
+ assertNotNull( result );
+ assertEquals( "incorrect entity returned, wrong network", "1D", (
result.get( 0 ) ).getNetwork() );
+ assertEquals( "incorrect entity returned, wrong branch", "St.
George", ( result.get( 0 ) ).getBranch() );
+ assertEquals( "incorrect number of results returned", 1, result.size() );
+
+ //cleanup
+ for (Object element : s.createQuery( "from " + Departments.class.getName()
).list()) s.delete( element );
+ tx.commit();
+ s.close();
+ }
+
+ /**
+ * This is the same test as above with a projection query
+ * to show the presence of the ClassBridge impl built fields
+ * just in case you don't believe us.
+ *
+ * @throws Exception
+ */
+ public void testClassBridgesWithProjection() throws Exception {
+ org.hibernate.Session s = openSession();
+ Transaction tx = s.beginTransaction();
+ s.persist( getDepts1() );
+ s.persist( getDepts2() );
+ s.persist( getDepts3() );
+ s.persist( getDepts4() );
+ s.flush();
+ tx.commit();
+
+ tx = s.beginTransaction();
+ FullTextSession session = Search.createFullTextSession( s );
+
+ // The equipment field is the manufacturer field in the
+ // Departments entity after being massaged by passing it
+ // through the EquipmentType class. This field is in
+ // the Lucene document but not in the Department entity itself.
+ QueryParser parser = new QueryParser( "equipment", new SimpleAnalyzer() );
+
+ // Check the second ClassBridge annotation
+ Query query = parser.parse( "equiptype:Cisco" );
+ org.hibernate.search.FullTextQuery hibQuery = session.createFullTextQuery( query,
Departments.class );
+
+ hibQuery.setProjection( FullTextQuery.THIS, FullTextQuery.DOCUMENT );
+
+ ScrollableResults projections = hibQuery.scroll();
+ assertNotNull( projections );
+
+ projections.beforeFirst();
+ projections.next();
+ Object[] projection = projections.get();
+
+ assertTrue( "DOCUMENT incorrect", projection[0] instanceof Departments );
+ assertEquals( "id incorrect", 1, ((Departments)projection[0]).getId() );
+ assertTrue( "DOCUMENT incorrect", projection[1] instanceof Document );
+ assertEquals( "DOCUMENT size incorrect", 8, ( (Document) projection[1]
).getFields().size() );
+ assertNotNull( "equiptype is null", ( (Document) projection[1]
).getField("equiptype") );
+ assertEquals( "equiptype incorrect", "Cisco", ( (Document)
projection[1] ).getField("equiptype" ).stringValue() );
+ assertNotNull( "branchnetwork is null", ( (Document) projection[1]
).getField("branchnetwork") );
+ assertEquals( "branchnetwork incorrect", "Salt Lake City 1A", (
(Document) projection[1] ).getField("branchnetwork" ).stringValue() );
+
+ projections.next();
+ projection = projections.get();
+
+ assertTrue( "DOCUMENT incorrect", projection[0] instanceof Departments );
+ assertEquals( "id incorrect", 4, ((Departments)projection[0]).getId() );
+ assertTrue( "DOCUMENT incorrect", projection[1] instanceof Document );
+ assertEquals( "DOCUMENT size incorrect", 8, ( (Document) projection[1]
).getFields().size() );
+ assertNotNull( "equiptype is null", ( (Document) projection[1]
).getField("equiptype") );
+ assertEquals( "equiptype incorrect", "Cisco", ( (Document)
projection[1] ).getField("equiptype" ).stringValue() );
+ assertNotNull( "branchnetwork is null", ( (Document) projection[1]
).getField("branchnetwork") );
+ assertEquals( "branchnetwork incorrect", "St. George 1D", (
(Document) projection[1] ).getField("branchnetwork" ).stringValue() );
+
+ assertTrue("incorrect result count returned", projections.isLast());
+ //cleanup
+ for (Object element : s.createQuery( "from " + Departments.class.getName()
).list()) s.delete( element );
+ tx.commit();
+ s.close();
+ }
+
+ /**
* This test checks for two fields being concatentated by the user-supplied
* CatFieldsClassBridge class which is specified as the implementation class
* in the ClassBridge annotation of the Department class.
@@ -83,19 +223,16 @@
private Department getDept1() {
Department dept = new Department();
-// dept.setId( 1000 );
dept.setBranch( "Salt Lake City" );
dept.setBranchHead( "Kent Lewin" );
dept.setMaxEmployees( 100 );
dept.setNetwork( "1A" );
-
return dept;
}
private Department getDept2() {
Department dept = new Department();
-// dept.setId( 1001 );
dept.setBranch( "Layton" );
dept.setBranchHead( "Terry Poperszky" );
dept.setMaxEmployees( 20 );
@@ -107,7 +244,6 @@
private Department getDept3() {
Department dept = new Department();
-// dept.setId( 1002 );
dept.setBranch( "West Valley" );
dept.setBranchHead( "Pat Kelley" );
dept.setMaxEmployees( 15 );
@@ -116,9 +252,57 @@
return dept;
}
+ private Departments getDepts1() {
+ Departments depts = new Departments();
+
+ depts.setBranch( "Salt Lake City" );
+ depts.setBranchHead( "Kent Lewin" );
+ depts.setMaxEmployees( 100 );
+ depts.setNetwork( "1A" );
+ depts.setManufacturer( "C" );
+
+ return depts;
+ }
+
+ private Departments getDepts2() {
+ Departments depts = new Departments();
+
+ depts.setBranch( "Layton" );
+ depts.setBranchHead( "Terry Poperszky" );
+ depts.setMaxEmployees( 20 );
+ depts.setNetwork( "2B" );
+ depts.setManufacturer( "3" );
+
+ return depts;
+ }
+
+ private Departments getDepts3() {
+ Departments depts = new Departments();
+
+ depts.setBranch( "West Valley" );
+ depts.setBranchHead( "Pat Kelley" );
+ depts.setMaxEmployees( 15 );
+ depts.setNetwork( "3C" );
+ depts.setManufacturer( "D" );
+
+ return depts;
+ }
+
+ private Departments getDepts4() {
+ Departments depts = new Departments();
+
+ depts.setBranch( "St. George" );
+ depts.setBranchHead( "Spencer Stajskal" );
+ depts.setMaxEmployees( 10 );
+ depts.setNetwork( "1D" );
+ depts.setManufacturer( "C" );
+ return depts;
+ }
+
protected Class[] getMappings() {
return new Class[] {
- Department.class
+ Department.class,
+ Departments.class
};
}
Added: search/trunk/src/test/org/hibernate/search/test/bridge/Departments.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/bridge/Departments.java
(rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/bridge/Departments.java 2007-09-21
16:34:06 UTC (rev 14020)
@@ -0,0 +1,102 @@
+package org.hibernate.search.test.bridge;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.ClassBridge;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.Store;
+import org.hibernate.search.annotations.Parameter;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.ClassBridges;
+
+/**
+ * This is just a simple copy of the Department entity to allow
+ * separation of the tests for ClassBridge and ClassBridges.
+ *
+ * @author John Griffin
+ */
+@Entity
+@Indexed
+@ClassBridges ( {
+ @ClassBridge(name="branchnetwork",
+ index= Index.TOKENIZED,
+ store= Store.YES,
+ impl = CatDeptsFieldsClassBridge.class,
+ params = @Parameter( name="sepChar", value=" " ) ),
+ @ClassBridge(name="equiptype",
+ index= Index.TOKENIZED,
+ store= Store.YES,
+ impl = EquipmentType.class,
+ params = {@Parameter( name="C", value="Cisco" ),
+ @Parameter( name="D", value="D-Link" ),
+ @Parameter( name="K", value="Kingston" ),
+ @Parameter( name="3", value="3Com" )
+ })
+})
+public class Departments {
+ private int id;
+ private String network;
+ private String manufacturer;
+ private String branchHead;
+ private String branch;
+ private Integer maxEmployees;
+
+ @Id
+ @GeneratedValue
+ @DocumentId
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ @Field(index=Index.TOKENIZED, store=Store.YES)
+ public String getBranchHead() {
+ return branchHead;
+ }
+
+ public void setBranchHead(String branchHead) {
+ this.branchHead = branchHead;
+ }
+
+ @Field(index=Index.TOKENIZED, store=Store.YES)
+ public String getNetwork() {
+ return network;
+ }
+
+ public void setNetwork(String network) {
+ this.network = network;
+ }
+
+ @Field(index=Index.TOKENIZED, store=Store.YES)
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ @Field(index=Index.UN_TOKENIZED, store=Store.YES)
+ public Integer getMaxEmployees() {
+ return maxEmployees;
+ }
+
+ public void setMaxEmployees(Integer maxEmployees) {
+ this.maxEmployees = maxEmployees;
+ }
+
+ public String getManufacturer() {
+ return manufacturer;
+ }
+
+ public void setManufacturer(String manufacturer) {
+ this.manufacturer = manufacturer;
+ }
+}
Added: search/trunk/src/test/org/hibernate/search/test/bridge/EquipmentType.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/bridge/EquipmentType.java
(rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/bridge/EquipmentType.java 2007-09-21
16:34:06 UTC (rev 14020)
@@ -0,0 +1,40 @@
+package org.hibernate.search.test.bridge;
+
+import java.util.Map;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.hibernate.search.bridge.FieldBridge;
+import org.hibernate.search.bridge.ParameterizedBridge;
+
+/**
+ * @author John Griffin
+ */
+public class EquipmentType implements FieldBridge, ParameterizedBridge {
+ private Map equips;
+
+ public void setParameterValues(Map parameters) {
+ // This map was defined by the parameters of the ClassBridge annotation.
+ this.equips = parameters;
+ }
+
+ public void set(String name, Object value, Document document, Field.Store store,
Field.Index index, Float boost) {
+ // In this particular class the name of the new field was passed
+ // from the name field of the ClassBridge Annotation. This is not
+ // a requirement. It just works that way in this instance. The
+ // actual name could be supplied by hard coding it below.
+ Departments deps = (Departments) value;
+ Field field = null;
+ String fieldValue1 = deps.getManufacturer();
+
+ if ( fieldValue1 == null ) {
+ fieldValue1 = "";
+ }
+ else {
+ String fieldValue = (String)equips.get( fieldValue1);
+ field = new Field( name, fieldValue, store, index );
+ if ( boost != null ) field.setBoost( boost );
+ }
+ document.add( field );
+ }
+}