Initial LuceneSession with callbacks

This commit is contained in:
Lassi Immonen 2010-12-23 00:44:51 +00:00
parent 12d068c5eb
commit af6f9df171
6 changed files with 352 additions and 1 deletions

View File

@ -18,7 +18,7 @@
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>3.0.0</version>
<version>3.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

@ -0,0 +1,45 @@
package com.mysema.query.lucene.session;
import java.io.IOException;
import java.util.List;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
/**
* General interface on using Lucene.
*
* @author laimw
*/
public interface LuceneSession {
/**
* Lucene query callback for querying
*
* @param clazz
* @param callback
* @return
* @throws IOException
* @throws CorruptIndexException
*/
<T> T query(QueryCallback<T> callback) throws CorruptIndexException, IOException;
/**
* Creates a new index, adds updates to it and publishes the new index to
* all readers after callback finishes.
*
* @param callback
* @throws IOException
*/
void updateNew(WriteCallback callback) throws IOException;
/**
* Updates the current index and publishes it to all readers after callback
* finishes.
*
* @param callback
* @throws IOException
*/
void update(WriteCallback callback) throws IOException;
}

View File

@ -0,0 +1,146 @@
package com.mysema.query.lucene.session;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.mysema.query.lucene.LuceneQuery;
import com.mysema.query.lucene.LuceneSerializer;
/**
* Lucene session implementation for single dto per index.
*
* @author laimw
*
*/
public class LuceneSessionImpl implements LuceneSession {
private final Logger logger = LoggerFactory.getLogger(LuceneSessionImpl.class);
private Directory directory;
private final AtomicReference<IndexSearcher> searcher = new AtomicReference<IndexSearcher>();
private LuceneSerializer serializer = new LuceneSerializer(true, true);
public LuceneSessionImpl(String indexPath) throws IOException {
File folder = new File(indexPath);
if (!folder.exists() && !folder.mkdirs()) {
throw new IOException("Could not create directory: " + folder.getAbsolutePath());
}
try {
directory = new SimpleFSDirectory(folder);
} catch (IOException e) {
logger.error("Could not create lucene directory to " + folder.getAbsolutePath());
throw e;
}
}
public LuceneSessionImpl(Directory directory) {
this.directory = directory;
}
@Override
public <T> T query(QueryCallback<T> callback) throws CorruptIndexException, IOException {
IndexSearcher is = getSearcher();
T results = null;
try {
// Incrementing the reference count
is.getIndexReader().incRef();
results = callback.query(new LuceneQuery(serializer, is));
} finally {
// Releasing the reference count
// This can be the last to actually close the reader
is.getIndexReader().decRef();
}
return results;
}
private IndexSearcher getSearcher() throws CorruptIndexException, IOException {
if (searcher.get() == null) {
createNewSearcher(null);
}
// Checking do we need to refresh the reader
IndexSearcher is = searcher.get();
if (!is.getIndexReader().isCurrent()) {
// Underlying index has changed
// Decreasing the reference counter so that
// count can go to zero either here or
// when final searcher has done it's job
is.getIndexReader().decRef();
createNewSearcher(is);
}
return searcher.get();
}
private IndexSearcher createNewSearcher(IndexSearcher expected) throws IOException {
IndexSearcher is = new IndexSearcher(directory);
if (!searcher.compareAndSet(expected, is)) {
// Some thread already created a new one so just close this
is.close();
} else {
// Incrementing the reference count first time
// We want to keep using the same reader until the index is changed
is.getIndexReader().incRef();
}
return searcher.get();
}
@Override
public void updateNew(WriteCallback callback) throws IOException {
update(callback, true);
}
@Override
public void update(WriteCallback callback) throws IOException {
update(callback, false);
}
private void update(WriteCallback callback, boolean create) throws IOException {
IndexWriter writer =
new IndexWriter(directory, new StandardAnalyzer(Version.LUCENE_CURRENT), create,
MaxFieldLength.LIMITED);
try {
callback.write(writer);
} finally {
try {
writer.close();
} catch (IOException e) {
logger.error("Writer close failed", e);
try {
if (IndexWriter.isLocked(directory)) {
IndexWriter.unlock(directory);
}
} catch (IOException e1) {
logger.error("Lock release failed", e1);
}
}
}
}
}

View File

@ -0,0 +1,12 @@
package com.mysema.query.lucene.session;
import com.mysema.query.lucene.LuceneQuery;
/**
*
* @author laimw
*
*/
public interface QueryCallback<T> {
T query(LuceneQuery query);
}

View File

@ -0,0 +1,18 @@
package com.mysema.query.lucene.session;
import java.io.IOException;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
/**
* Callback which has Lucene Writer instance.
*
* @author laimw
*
*/
public interface WriteCallback {
void write(IndexWriter writer) throws CorruptIndexException, IOException;
}

View File

@ -0,0 +1,130 @@
package com.mysema.query.lucene.session;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.IOException;
import java.util.List;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.NumericField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.junit.Before;
import org.junit.Test;
import com.mysema.query.lucene.LuceneQuery;
import com.mysema.query.lucene.LuceneSerializer;
import com.mysema.query.types.PathMetadataFactory;
import com.mysema.query.types.path.EntityPathBase;
import com.mysema.query.types.path.NumberPath;
import com.mysema.query.types.path.StringPath;
public class LuceneSessionImplTest {
public class QDocument extends EntityPathBase<Document> {
private static final long serialVersionUID = -4872833626508344081L;
public QDocument(final String var) {
super(Document.class, PathMetadataFactory.forVariable(var));
}
public final NumberPath<Integer> year = createNumber("year", Integer.class);
public final StringPath title = createString("title");
public final NumberPath<Double> gross = createNumber("gross", Double.class);
}
private LuceneSession session;
private Directory directory;
private StringPath title;
private NumberPath<Integer> year;
private NumberPath<Double> gross;
@Before
public void before() throws IOException {
directory = new RAMDirectory();
session = new LuceneSessionImpl(directory);
final QDocument entityPath = new QDocument("doc");
title = entityPath.title;
year = entityPath.year;
gross = entityPath.gross;
}
@Test
public void testCreate() throws IOException {
session.updateNew(new WriteCallback() {
public void write(IndexWriter writer) throws CorruptIndexException, IOException {
writer.addDocument(createDocument(
"Jurassic Park",
"Michael Crichton",
"It's a UNIX system! I know this!",
1990,
90.00));
writer.addDocument(createDocument(
"Nummisuutarit",
"Aleksis Kivi",
"ESKO. Ja iloitset ja riemuitset?",
1864,
10.00));
}
});
List<Document> results = session.query(new QueryCallback<List<Document>>() {
public List<Document> query(LuceneQuery query) {
return query.where(title.eq("Jurassic Park")).list();
}
});
assertEquals(1, results.size());
assertEquals("Jurassic Park", results.get(0).getField("title").stringValue());
Long count = session.query(new QueryCallback<Long>() {
public Long query(LuceneQuery query) {
//TODO Tästä tulee 0 eikä 2!!
//return query.where(title.ne("AA")).count();
return query.where(title.startsWith("Nummi")).count();
}
});
assertEquals(1, (long) count);
}
private Document createDocument(
final String docTitle,
final String docAuthor,
final String docText,
final int docYear,
final double docGross) {
final Document doc = new Document();
doc.add(new Field("title", docTitle, Store.YES, Index.ANALYZED));
doc.add(new Field("author", docAuthor, Store.YES, Index.ANALYZED));
doc.add(new Field("text", docText, Store.YES, Index.ANALYZED));
doc.add(new NumericField("year", Store.YES, true).setIntValue(docYear));
doc.add(new NumericField("gross", Store.YES, true).setDoubleValue(docGross));
return doc;
}
}