package org.thrudb.thrudex.lucene;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.FieldSelector;
import org.apache.lucene.document.MapFieldSelector;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopFieldDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.thrudb.thrudex.Element;
import org.thrudb.thrudex.SearchQuery;
import org.thrudb.thrudex.SearchResponse;
import org.thrudb.thrudex.ThrudexException;
import org.thrudb.thrudex.ThrudexExceptionImpl;
/**
* Very straight forward implementation of lucene api
*
*/
public class SimpleLuceneIndex implements LuceneIndex {
Analyzer analyzer = new StandardAnalyzer();
IndexWriter writer;
IndexReader reader;
IndexSearcher searcher;
Directory directory;
QueryParser queryParser = new QueryParser(DOCUMENT_KEY,analyzer);
AtomicBoolean hasWrite = new AtomicBoolean(false);
Logger logger = Logger.getLogger(getClass());
public SimpleLuceneIndex(String indexRoot, String indexName) throws IOException {
File rootFile = new File(indexRoot);
if(!rootFile.isDirectory())
throw new IOException("invalid index root: " + indexRoot);
String indexLocation = indexRoot +"/" + indexName;
boolean createIndex = !IndexReader.indexExists(indexLocation);
if(createIndex){
writer = new IndexWriter(indexLocation,analyzer,createIndex,IndexWriter.MaxFieldLength.UNLIMITED);
directory = FSDirectory.getDirectory(indexLocation);
}else{
directory = FSDirectory.getDirectory(indexLocation);
if(IndexWriter.isLocked(indexLocation)){
System.err.println("Removing lock on "+indexName);
IndexWriter.unlock(directory);
}
writer = new IndexWriter(directory,analyzer,IndexWriter.MaxFieldLength.UNLIMITED);
}
//open this read only
reader = IndexReader.open(directory, true);
searcher = new IndexSearcher(reader);
}
public void put(String key, Document document, Analyzer analyzer) throws ThrudexException{
Term term = new Term(DOCUMENT_KEY,key);
try{
writer.updateDocument(term, document, analyzer);
hasWrite.set(true);
}catch(IOException e){
throw new ThrudexExceptionImpl(e.toString());
}
}
public void remove(String key) throws ThrudexException {
Term term = new Term(DOCUMENT_KEY, key);
try{
writer.deleteDocuments(term);
hasWrite.set(true);
}catch(IOException e){
throw new ThrudexExceptionImpl(e.toString());
}
}
public SearchResponse search(SearchQuery query, Analyzer analyzer) throws ThrudexException {
if(!query.isSetQuery() || query.query.trim().equals(""))
throw new ThrudexExceptionImpl("Empty Query");
//Parse Query
Query parsedQuery;
//MySearcher represents the searcher instance we'll use
//for the duration of this function call, since other threads
//may change searcher on us...
IndexSearcher mySearcher;
try{
//This section needs to be thread safe
synchronized(this){
//Commit any prev writes
if(hasWrite.getAndSet(false)){
writer.commit();
//Reopen index reader
IndexReader newReader = reader.reopen();
if(reader != newReader){
//reader.close();
searcher.close();
reader = newReader;
searcher = new IndexSearcher(reader);
}
}
mySearcher = searcher;
try{
parsedQuery = queryParser.parse(query.getQuery());
}catch(org.apache.lucene.queryParser.ParseException e){
throw new ThrudexExceptionImpl(e.toString());
}
}
//Set Sort
Sort sortBy = new Sort();
if(query.isSetSortby() && !query.sortby.trim().equals(""))
sortBy.setSort(query.getSortby() + "_sort", query.desc);
//Search
TopFieldDocs result = mySearcher.search(parsedQuery,null,query.offset + query.limit,sortBy);
SearchResponse response = new SearchResponse();
response.setTotal(result.totalHits);
FieldSelector fieldSelector;
if(query.isPayload()){
fieldSelector = new MapFieldSelector(new String[]{DOCUMENT_KEY,PAYLOAD_KEY});
}else{
fieldSelector = new MapFieldSelector(new String[]{DOCUMENT_KEY});
}
for(int i=query.offset; i<result.totalHits && i<(query.offset + query.limit); i++){
Element el = new Element();
el.setIndex(query.index);
Document d = mySearcher.doc(result.scoreDocs[i].doc,fieldSelector);
el.setKey(d.get(DOCUMENT_KEY));
if(query.isSetPayload() && query.payload)
el.setPayload(d.get(PAYLOAD_KEY));
response.addToElements(el);
}
return response;
}catch(IOException e){
throw new ThrudexException(e.toString());
}
}
public void optimize() throws ThrudexException{
try{
this.writer.optimize();
}catch(IOException e){
throw new ThrudexException(e.toString());
}
}
public void shutdown() {
}
}