[hibernate-commits] Hibernate SVN: r17833 - in search/trunk/src/main/java/org/hibernate/search: sandbox and 1 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Sun Oct 25 14:30:03 EDT 2009


Author: epbernard
Date: 2009-10-25 14:30:03 -0400 (Sun, 25 Oct 2009)
New Revision: 17833

Added:
   search/trunk/src/main/java/org/hibernate/search/sandbox/
   search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/
   search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/FullTextManager.java
   search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/FullTextQuery.java
   search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/InstanceTransactionContext.java
   search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/LuceneFullTextManager.java
Log:
HSEARCH-407 Initial PoC start on a pure Lucene storage (ie not relying on Hibernate Core

Added: search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/FullTextManager.java
===================================================================
--- search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/FullTextManager.java	                        (rev 0)
+++ search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/FullTextManager.java	2009-10-25 18:30:03 UTC (rev 17833)
@@ -0,0 +1,82 @@
+package org.hibernate.search.sandbox.standalone;
+
+import org.hibernate.search.*;
+
+import java.io.Serializable;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface FullTextManager {
+
+	/**
+	 * Returns the entity instance of a given type and id value  
+	 */
+	public <T> T get(Class<T> entityType, Serializable id);
+
+	/**
+	 * Create a fulltext query on top of a native Lucene query returning the matching objects
+	 * of type <code>entities</code> and their respective subclasses.
+	 *
+	 * @param luceneQuery The native Lucene query to be rn against the Lucene index.
+	 * @param entities List of classes for type filtering. The query result will only return entities of
+	 * the specified types and their respective subtype. If no class is specified no type filtering will take place.
+	 *
+	 * @return A <code>FullTextQuery</code> wrapping around the native Lucene wuery.
+	 *
+	 * @throws IllegalArgumentException if entityType is <code>null</code> or not a class or superclass annotated with <code>@Indexed</code>.
+	 */
+	FullTextQuery createFullTextQuery(org.apache.lucene.search.Query luceneQuery, Class<?>... entities);
+
+	/**
+	 * Force the (re)indexing of a given <b>managed</b> object.
+	 * Indexation is batched per transaction: if a transaction is active, the operation
+	 * will not affect the index at least until commit.
+	 *
+	 * @param entity The entity to index - must not be <code>null</code>.
+	 *
+	 * @throws IllegalArgumentException if entity is null or not an @Indexed entity
+	 */
+	<T> void index(T entity);
+
+	/**
+	 * @return the <code>SearchFactory</code> instance.
+	 */
+	SearchFactory getSearchFactory();
+
+	/**
+	 * Remove the entity with the type <code>entityType</code> and the identifier <code>id</code> from the index.
+	 * If <code>id == null</code> all indexed entities of this type and its indexed subclasses are deleted. In this
+	 * case this method behaves like {@link #purgeAll(Class)}.
+	 *
+	 * @param entityType The type of the entity to delete.
+	 * @param id The id of the entity to delete.
+	 *
+	 * @throws IllegalArgumentException if entityType is <code>null</code> or not a class or superclass annotated with <code>@Indexed</code>.
+	 */
+	public <T> void purge(Class<T> entityType, Serializable id);
+
+	/**
+	 * Remove all entities from of particular class and all its subclasses from the index.
+	 *
+	 * @param entityType The class of the entities to remove.
+	 *
+	 * @throws IllegalArgumentException if entityType is <code>null</code> or not a class or superclass annotated with <code>@Indexed</code>.
+	 */
+	public <T> void purgeAll(Class<T> entityType);
+
+	/**
+	 * Flush all index changes forcing Hibernate Search to apply all changes to the index not waiting for the batch limit.
+	 */
+	public void flushToIndexes();
+
+	//FIXME add support for mass indexer: does it even makes sense?
+	/**
+	 * Creates a MassIndexer to rebuild the indexes of some
+	 * or all indexed entity types.
+	 * Instances cannot be reused.
+	 * @param types optionally restrict the operation to selected types
+	 * @return
+	 */
+	//public MassIndexer createIndexer(Class<?>... types);
+}

Added: search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/FullTextQuery.java
===================================================================
--- search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/FullTextQuery.java	                        (rev 0)
+++ search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/FullTextQuery.java	2009-10-25 18:30:03 UTC (rev 17833)
@@ -0,0 +1,10 @@
+package org.hibernate.search.sandbox.standalone;
+
+import java.util.List;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public interface FullTextQuery {
+	List<?> list();
+}

Added: search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/InstanceTransactionContext.java
===================================================================
--- search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/InstanceTransactionContext.java	                        (rev 0)
+++ search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/InstanceTransactionContext.java	2009-10-25 18:30:03 UTC (rev 17833)
@@ -0,0 +1,80 @@
+package org.hibernate.search.sandbox.standalone;
+
+import org.hibernate.search.backend.TransactionContext;
+
+import javax.transaction.Synchronization;
+import javax.transaction.Status;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Transaction context that contains transaction boundaries methods.
+ * While not "transactional" it allows to call Synchronization elements
+ * 
+ * @author Emmanuel Bernard
+ */
+public class InstanceTransactionContext implements TransactionContext {
+	private State transactionState = State.NO_TRANSACTION;
+	private final List<Synchronization> synchronizations = new ArrayList<Synchronization>(5);
+
+	public void beginTransaction() {
+		if (transactionState != State.NO_TRANSACTION) {
+			throw new IllegalStateException( "Transaction context already started: " + transactionState);
+		}
+		transactionState = State.IN_TRANSACTION;
+	}
+
+	public void commit() {
+		if ( transactionState != State.IN_TRANSACTION ) {
+			throw new IllegalStateException( "Transaction context not in active state: " + transactionState);
+		}
+		try {
+			for (Synchronization sync : synchronizations) {
+				sync.beforeCompletion();
+			}
+			for (Synchronization sync : synchronizations) {
+				sync.afterCompletion( Status.STATUS_COMMITTED );
+			}
+		}
+		finally {
+			synchronizations.clear();
+			transactionState = State.TRANSACTION_CLOSED;
+		}
+	}
+
+	public void rollback() {
+		if ( transactionState != State.IN_TRANSACTION ) {
+			throw new IllegalStateException( "Transaction context not in active state: " + transactionState);
+		}
+		try {
+			for (Synchronization sync : synchronizations) {
+				sync.beforeCompletion();
+			}
+			for (Synchronization sync : synchronizations) {
+				sync.afterCompletion( Status.STATUS_ROLLEDBACK );
+			}
+		}
+		finally {
+			synchronizations.clear();
+			transactionState = State.TRANSACTION_CLOSED;
+		}
+	}
+
+	public boolean isTransactionInProgress() {
+		return transactionState == State.IN_TRANSACTION;
+	}
+
+	public Object getTransactionIdentifier() {
+		return this;
+	}
+
+	public void registerSynchronization(Synchronization synchronization) {
+		synchronizations.add( synchronization );
+	}
+
+	private static enum State {
+		NO_TRANSACTION,
+		IN_TRANSACTION,
+		TRANSACTION_CLOSED
+	}
+}

Added: search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/LuceneFullTextManager.java
===================================================================
--- search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/LuceneFullTextManager.java	                        (rev 0)
+++ search/trunk/src/main/java/org/hibernate/search/sandbox/standalone/LuceneFullTextManager.java	2009-10-25 18:30:03 UTC (rev 17833)
@@ -0,0 +1,140 @@
+package org.hibernate.search.sandbox.standalone;
+
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.index.Term;
+import org.hibernate.search.MassIndexer;
+import org.hibernate.search.SearchFactory;
+import org.hibernate.search.ProjectionConstants;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.backend.Work;
+import org.hibernate.search.backend.WorkType;
+import org.hibernate.search.engine.DocumentBuilderIndexedEntity;
+import org.hibernate.search.engine.SearchFactoryImplementor;
+
+import java.io.Serializable;
+import java.util.Set;
+import java.util.List;
+
+/**
+ * Implements a standalone full text service.
+ * Data is stored in Lucene
+ *
+ * @author Emmanuel Bernard
+ */
+public class LuceneFullTextManager implements FullTextManager {
+	private final SearchFactoryImplementor searchFactory;
+	private final InstanceTransactionContext transactionContext;
+
+	LuceneFullTextManager(SearchFactoryImplementor sfi) {
+		this.searchFactory = sfi;
+		this.transactionContext = new InstanceTransactionContext();
+		transactionContext.beginTransaction();
+	}
+
+	public <T> T get(Class<T> entityType, Serializable id) {
+		final DocumentBuilderIndexedEntity<?> docBuilder = searchFactory.getDocumentBuilderIndexedEntity( entityType );
+		if ( docBuilder == null ) {
+			String msg = "Entity to retrueve is not an @Indexed entity: " + entityType.getClass().getName();
+			throw new IllegalArgumentException( msg );
+		}
+		if (id == null) {
+			throw new IllegalArgumentException( "Identifier cannot be null" );
+		}
+		Query luceneQuery = new TermQuery( docBuilder.getTerm( id ) );
+		FullTextQuery searchQuery = createFullTextQuery( luceneQuery, entityType );
+		List<?> results = searchQuery.list();
+		if (results.size() > 1) {
+			//TODO find correct exception
+			throw new SearchException("Several entities with he same id found: " + entityType + "#" + id);
+		}
+		@SuppressWarnings( "unchecked" )
+		final T result = (T) ( results.size() == 0 ? null : results.get( 0 ) );
+		return result;
+	}
+
+	public FullTextQuery createFullTextQuery(Query luceneQuery, Class<?>... entities) {
+		return null;  //To change body of implemented methods use File | Settings | File Templates.
+	}
+
+
+
+	/**
+	 * (Re-)index an entity.
+	 * The entity must be associated with the session and non indexable entities are ignored.
+	 *
+	 * @param entity The entity to index - must not be <code>null</code>.
+	 *
+	 * @throws IllegalArgumentException if entity is null or not an @Indexed entity
+	 */
+	public <T> void index(T entity) {
+		if ( entity == null ) {
+			throw new IllegalArgumentException( "Entity to index should not be null" );
+		}
+
+		Class<?> clazz = getClass( entity );
+		//TODO cache that at the FTSession level
+		//not strictly necessary but a small optimization
+		final DocumentBuilderIndexedEntity<?> docBuilder = searchFactory.getDocumentBuilderIndexedEntity( clazz );
+		if ( docBuilder == null ) {
+			String msg = "Entity to index is not an @Indexed entity: " + entity.getClass().getName();
+			throw new IllegalArgumentException( msg );
+		}
+		Serializable id = docBuilder.getId( entity );
+		Work<T> work = new Work<T>( entity, id, WorkType.INDEX );
+		searchFactory.getWorker().performWork( work, transactionContext );
+
+		//TODO
+		//need to add elements in a queue kept at the Session level
+		//the queue will be processed by a Lucene(Auto)FlushEventListener
+		//note that we could keep this queue somewhere in the event listener in the mean time but that requires
+		//a synchronized hashmap holding this queue on a per session basis plus some session house keeping (yuk)
+		//another solution would be to subclass SessionImpl instead of having this LuceneSession delegation model
+		//this is an open discussion
+	}
+
+	private Class<?> getClass(Object entity) {
+		return entity.getClass();
+	}
+
+	public SearchFactory getSearchFactory() {
+		return searchFactory;
+	}
+
+	public <T> void purge(Class<T> entityType, Serializable id) {
+		if ( entityType == null ) {
+			return;
+		}
+
+		Set<Class<?>> targetedClasses = searchFactory.getIndexedTypesPolymorphic( new Class[] {entityType} );
+		if ( targetedClasses.isEmpty() ) {
+			String msg = entityType.getName() + " is not an indexed entity or a subclass of an indexed entity";
+			throw new IllegalArgumentException( msg );
+		}
+
+		for ( Class<?> clazz : targetedClasses ) {
+			if ( id == null ) {
+				createAndPerformWork( clazz, null, WorkType.PURGE_ALL );
+			}
+			else {
+				createAndPerformWork( clazz, id, WorkType.PURGE );
+			}
+		}
+	}
+
+	private <T> void createAndPerformWork(Class<T> clazz, Serializable id, WorkType workType) {
+		Work<T> work;
+		work = new Work<T>( clazz, id, workType );
+		searchFactory.getWorker().performWork( work, transactionContext );
+	}
+
+	public <T> void purgeAll(Class<T> entityType) {
+		purge( entityType, null );
+	}
+
+	public void flushToIndexes() {
+		searchFactory.getWorker().flushWorks( transactionContext );
+	}
+}



More information about the hibernate-commits mailing list