/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* File: $Source: /cvsroot/slrp/common/com.ibm.adtech.indexer.lucene/src/com/ibm/adtech/indexer/lucene/LuceneSearch.java,v $
* Created by: Wing Yung ( <a href="mailto:wingyung@us.ibm.com">wingyung@us.ibm.com </a>)
* Created on: 10/11/2005
* Revision: $Id: LuceneSearch.java 169 2007-07-31 14:11:15Z mroy $
*
* Contributors:
* IBM Corporation - initial API and implementation
* Cambridge Semantics Incorporated - Fork to Anzo
*******************************************************************************/
package org.openanzo.indexer.lucene;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.store.FSDirectory;
import org.openanzo.exceptions.AnzoException;
import org.openanzo.exceptions.AnzoRuntimeException;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.exceptions.LogUtils;
import org.openanzo.indexer.IQuery;
import org.openanzo.indexer.IResult;
import org.openanzo.indexer.ISearch;
import org.openanzo.indexer.IndexerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Search implementation based on Lucene.
*
* @author Wing Yung ( <a href="mailto:wingyung@us.ibm.com">wingyung@us.ibm.com </a>)
*/
public class LuceneSearch implements ISearch {
private static final Logger log = LoggerFactory.getLogger(LuceneSearch.class);
protected String location;
protected IndexSearcher searcher;
int pageSize = 5;
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* Close search
*
* @throws AnzoException
*/
public void close() throws AnzoException {
try {
searcher.close();
} catch (IOException ioe) {
throw new AnzoException(ExceptionConstants.INDEX.FAILED_INDEX_CLOSE, ioe);
}
}
/**
* Initializes the Lucene index. Reads the index directory location out of the properties object.
*
* The properties object is expected to contain the location of the directory containing the index (org.openanzo.indexer.lucene.indexLocation).
*
* @param properties
* configuration properties for search
* @throws IndexerException
* {@link ExceptionConstants.INDEX#INDEX_CONFIG_PARAM_MISSING} if the {@link LuceneProperties#KEY_LUCENE_INDEX_LOCATION} property is missing
*/
public void initialize(Properties properties) throws IndexerException {
location = LuceneProperties.getIndexLocation(properties);
if (location == null) {
throw new IndexerException(ExceptionConstants.INDEX.INDEX_CONFIG_PARAM_MISSING, LuceneProperties.KEY_LUCENE_INDEX_LOCATION);
}
location = LuceneIndexerBase.getAbsoluteIndexLocation(location, LuceneProperties.getIndexerHome(properties));
if (searcher == null) {
try {
searcher = new IndexSearcher(FSDirectory.open(new File(location)), true);
} catch (IOException e) {
throw new IndexerException(ExceptionConstants.INDEX.FAILED_INIT_SEARCH, e, location);
}
}
}
/**
* Initializes the Lucene index.
*
* @param location
* the location of the directory containing the index
* @throws IndexerException
* {@link ExceptionConstants.INDEX#INDEX_CONFIG_PARAM_MISSING} if the {@link LuceneProperties#KEY_LUCENE_INDEX_LOCATION} property is missing
*/
public void initialize(String location) throws IndexerException {
if (location == null) {
throw new IndexerException(ExceptionConstants.INDEX.INDEX_CONFIG_PARAM_MISSING, LuceneProperties.KEY_LUCENE_INDEX_LOCATION);
}
this.location = location;
if (searcher == null) {
try {
searcher = new IndexSearcher(FSDirectory.open(new File(location)), true);
} catch (IOException e) {
throw new IndexerException(ExceptionConstants.INDEX.FAILED_INIT_SEARCH, e, location);
}
}
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public List<IResult> executeQuery(IQuery query) throws IndexerException {
preQuery();
if (log.isDebugEnabled()) {
if (query instanceof LuceneQuery) {
LuceneQuery luceneQuery = (LuceneQuery) query;
log.debug(LogUtils.DATASOURCE_MARKER, "executeQuery - filter:'{}' query:'{}' # of query terms: " + (luceneQuery.getTerms() == null ? 0 : luceneQuery.getTerms().size()), (luceneQuery.getFilter() == null ? "null" : luceneQuery.getFilter().toString()), (luceneQuery.getQuery() == null ? "null" : luceneQuery.getQuery().toString()));
}
}
readWriteLock.readLock().lock();
try {
if (searcher != null) {
try {
if (query instanceof LuceneQuery) {
LuceneQuery lQueryWrapper = (LuceneQuery) query;
Query lQuery = lQueryWrapper.getQuery();
Filter filter = lQueryWrapper.getFilter();
final List<IResult> results = new ArrayList<IResult>();
log.debug(LogUtils.DATASOURCE_MARKER, "executeQuery - About to invoke index searcher.");
if (filter != null) {
searcher.search(lQuery, filter, new Collector() {
private int docBase;
@Override
public void setScorer(Scorer arg0) throws IOException {
}
@Override
public void setNextReader(IndexReader arg0, int arg1) throws IOException {
this.docBase = arg1;
}
@Override
public void collect(int doc) throws IOException {
results.add(new LuceneResult(searcher.doc(doc + docBase)));
}
@Override
public boolean acceptsDocsOutOfOrder() {
return true;
}
});
} else {
searcher.search(lQuery, new Collector() {
private int docBase;
@Override
public void setScorer(Scorer arg0) throws IOException {
}
@Override
public void setNextReader(IndexReader arg0, int arg1) throws IOException {
this.docBase = arg1;
}
@Override
public void collect(int doc) throws IOException {
results.add(new LuceneResult(searcher.doc(doc + docBase)));
}
@Override
public boolean acceptsDocsOutOfOrder() {
return true;
}
});
}
log.debug(LogUtils.DATASOURCE_MARKER, "executeQuery - Index search complete. # of results: {}", results.size());
return results;
} else {
throw new IndexerException(ExceptionConstants.INDEX.WRONG_QUERY_TYPE, LuceneQuery.class.toString());
}
} catch (IOException e) {
throw new IndexerException(ExceptionConstants.INDEX.FAILED_INDEX_QUERY, e);
} catch (AnzoRuntimeException e) {
throw new IndexerException(e.getErrorCode(), e.getCause(), e.getArgs());
}
} else {
throw new IndexerException(ExceptionConstants.INDEX.NO_SEARCH_OBJECT);
}
} finally {
readWriteLock.readLock().unlock();
}
}
/**
* run before the query
*
* @throws IndexerException
*/
public void preQuery() throws IndexerException {
try {
readWriteLock.readLock().lock();
boolean indexOk = false;
try {
indexOk = searcher.getIndexReader().isCurrent();
} finally {
readWriteLock.readLock().unlock();
}
if (!indexOk) {
readWriteLock.writeLock().lock();
try {
log.info(LogUtils.DATASOURCE_MARKER, "Index is not current, closing and re-opening searcher to refresh.");
searcher.close();
searcher = new IndexSearcher(FSDirectory.open(new File(location)), true);
} finally {
readWriteLock.writeLock().unlock();
}
}
} catch (IOException e) {
throw new IndexerException(ExceptionConstants.INDEX.FAILED_INIT_SEARCH, e, location);
}
}
}