/* * Copyright 2002-2005 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springmodules.lucene.search.core; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.search.Filter; import org.apache.lucene.search.HitCollector; import org.apache.lucene.search.Hits; import org.apache.lucene.search.Query; import org.apache.lucene.search.Searcher; import org.apache.lucene.search.Sort; import org.springmodules.lucene.search.LuceneSearchException; import org.springmodules.lucene.search.factory.SearcherFactory; import org.springmodules.lucene.search.factory.SearcherFactoryUtils; /** * <b>This is the central class in the lucene search core package.</b> * It simplifies the use of lucene to search documents using searcher. * It helps to avoid common errors and to manage these resource in a * flexible manner. * It executes core Lucene workflow, leaving application code to focus on * the way to create Lucene queries and extract datas from results. * * <p>This class is based on the SearcherFactory abstraction which is a * factory to create Searcher for a or several configured Directories. * They can be local or remote. So the template doesn't need to always * hold resources and you can apply different strategies for managing * index resources. * * <p>Can be used within a service implementation via direct instantiation * with a SearcherFactory reference, or get prepared in an application context * and given to services as bean reference. Note: The SearcherFactory should * always be configured as a bean in the application context, in the first case * given to the service directly, in the second case to the prepared template. * * @author Brian McCallister * @author Thierry Templier * @see org.springmodules.lucene.search.query.QueryCreator * @see org.springmodules.lucene.search.factory */ public class LuceneSearchTemplate { private SearcherFactory searcherFactory; private Analyzer analyzer; /** * Construct a new LuceneSearchTemplate for bean usage. * Note: The SearcherFactory has to be set before using the instance. * This constructor can be used to prepare a LuceneSearchTemplate via a BeanFactory, * typically setting the SearcherFactory via setSearcherFactory and the Analyzer * via setAnalyzer. * @see #setSearcherFactory * @see #setAnalyzer(Analyzer) */ public LuceneSearchTemplate() { } /** * Construct a new LuceneSearchTemplate, given an SearcherFactory and an * Analyzer to obtain a Searcher and an Analyzer to be used by the queries. * @param searcherFactory SearcherFactory to obtain Searcher * @param analyzer Lucene analyzer used by the queries */ public LuceneSearchTemplate(SearcherFactory searcherFactory,Analyzer analyzer) { setSearcherFactory(searcherFactory); setAnalyzer(analyzer); afterPropertiesSet(); } /** * Check if the searcherFactory is set. The analyzer could be not set. */ public void afterPropertiesSet() { if (getSearcherFactory() == null) { throw new IllegalArgumentException("searcherFactory is required"); } } /** * Set the SearcherFactory to obtain Searcher. */ public void setSearcherFactory(SearcherFactory factory) { searcherFactory = factory; } /** * Return the SearcherFactory used by this template. */ public SearcherFactory getSearcherFactory() { return searcherFactory; } /** * Set the default Lucene Analyzer used by the queries. */ public void setAnalyzer(Analyzer analyzer) { this.analyzer = analyzer; } /** * Return the Lucene Analyzer used by this template. */ public Analyzer getAnalyzer() { return analyzer; } /** * Method used to extract datas from hits. These datas are * the Lucene internal document identifier, the document * itself and the score. * * @param hits the hits corresponding to the search * @param extractor the extractor specified in the search method * @return the search results extracted * @throws IOException exception occuring when accessing documents */ private List extractHits(Hits hits,HitExtractor extractor) throws IOException { List list=new ArrayList(); for(int cpt=0;cpt<hits.length();cpt++) { list.add(extractor.mapHit(hits.id(cpt),hits.doc(cpt),hits.score(cpt))); } return list; } /** * Invoke the given QueryCreator, constructing Lucene query. * @param queryCreator the QueryCreator to invoke * @return the constructed Query * @see QueryCreator#createQuery(Analyzer) */ protected Query createQuery(QueryCreator queryCreator) { try { return queryCreator.createQuery(getAnalyzer()); } catch (ParseException ex) { throw new LuceneSearchException("Construction of the desired Query failed", ex); } } /** * Search the index basing a Lucene query created thanks to a callback * method defined in the QueryCreator interface. In this case, the * exceptions during the query creation are managed by the template. * @param queryConstructor the query constructor * @param extractor the extractor of hit informations * @return the search results * @see QueryCreator#createQuery(Analyzer) */ public List search(QueryCreator queryCreator,HitExtractor extractor) { return doSearch(createQuery(queryCreator),extractor,null,null); } /** * Search the index basing a Lucene query created outside the template. * In this case, the application needs to manage exceptions. * @param queryConstructor the query constructor * @param extractor the extractor of hit informations * @return the search results */ public List search(Query query,HitExtractor extractor) { return doSearch(query,extractor,null,null); } /** * Search the index basing a Lucene query created thanks to a callback * method defined in the QueryCreator interface and using a Lucene * filter. In this case, the exceptions during the query creation are * managed by the template. * @param queryConstructor the query constructor * @param extractor the extractor of hit informations * @return the search results * @see QueryCreator#createQuery(Analyzer) */ public List search(QueryCreator queryCreator,HitExtractor extractor,Filter filter) { return doSearch(createQuery(queryCreator),extractor,filter,null); } /** * Search the index basing a Lucene query created outside the template using * a Lucene filter. In this case, the application needs to manage exceptions. * @param queryConstructor the query constructor * @param extractor the extractor of hit informations * @return the search results */ public List search(Query query,HitExtractor extractor,Filter filter) { return doSearch(query,extractor,filter,null); } /** * Search the index basing a Lucene query created thanks to a callback * method defined in the QueryCreator interface and using a Lucene * sort. In this case, the exceptions during the query creation are * managed by the template. * @param queryConstructor the query constructor * @param extractor the extractor of hit informations * @return the search results * @see QueryCreator#createQuery(Analyzer) */ public List search(QueryCreator queryCreator,HitExtractor extractor,Sort sort) { return doSearch(createQuery(queryCreator),extractor,null,sort); } /** * Search the index basing a Lucene query created outside the template using * a Lucene sort. In this case, the application needs to manage exceptions. * @param queryConstructor the query constructor * @param extractor the extractor of hit informations * @return the search results */ public List search(Query query,HitExtractor extractor,Sort sort) { return doSearch(query,extractor,null,sort); } /** * Search the index basing a Lucene query created thanks to a callback * method defined in the QueryCreator interface and using a Lucene * filter and sort. In this case, the exceptions during the query creation are * managed by the template. * @param queryConstructor the query constructor * @param extractor the extractor of hit informations * @return the search results * @see QueryCreator#createQuery(Analyzer) */ public List search(QueryCreator queryCreator,HitExtractor extractor,Filter filter,Sort sort) { return doSearch(createQuery(queryCreator),extractor,filter,sort); } /** * Search the index basing a Lucene query created outside the template using * a Lucene filter and sort. In this case, the application needs to manage * exceptions. * @param queryConstructor the query constructor * @param extractor the extractor of hit informations * @return the search results */ public List search(Query query,HitExtractor extractor,Filter filter,Sort sort) { return doSearch(query,extractor,filter,sort); } /** * Internal method to search the index basing a Lucene query created * thanks to a callback method defined in the QueryCreator interface. * In this case, the exceptions during the query creation are managed * by the template. * This method uses sort and/or filter paramaters as Searcher search * method parameters if they are not null. * @param query the query used * @param extractor the extractor of hit informations * @param filter the query filter * @param sort the query sorter * @return the search results */ private List doSearch(Query query,HitExtractor extractor,Filter filter,Sort sort) { Searcher searcher=SearcherFactoryUtils.getSearcher(getSearcherFactory()); try { Hits hits=null; if( filter!=null && sort!=null ) { hits=searcher.search(query,filter,sort); } else if( filter!=null ) { hits=searcher.search(query,filter); } else if( sort!=null ) { hits=searcher.search(query,sort); } else { hits=searcher.search(query); } return extractHits(hits,extractor); } catch (IOException ex) { throw new LuceneSearchException("Error during the search",ex); } finally { SearcherFactoryUtils.releaseSearcher(getSearcherFactory(),searcher); } } /** * Search the index basing a Lucene query created thanks to a callback * method defined in the QueryCreator interface. In this case, the * exceptions during the query creation are managed by the template. * The results are collecting with the Lucene HitCollector parameter. * @param queryConstructor the query constructor * @param results the Lucene hit collector * @see QueryCreator#createQuery(Analyzer) * @see org.apache.lucene.search.HitCollector */ public void search(QueryCreator queryCreator,HitCollector results) { Searcher searcher=SearcherFactoryUtils.getSearcher(getSearcherFactory()); try { searcher.search(queryCreator.createQuery(getAnalyzer()),results); } catch (IOException ex) { throw new LuceneSearchException("Error during the search",ex); } catch (ParseException ex) { throw new LuceneSearchException("Error during the parse of the query",ex); } finally { SearcherFactoryUtils.releaseSearcher(getSearcherFactory(),searcher); } } /** * Execute the action specified by the given action object within a * Lucene Searcher. * Note: if you share resources across several calls, the Searcher * provides to the callback is the shared instance. A new one is not * created. * @param callback the callback object that exposes the Searcher * @see SearcherCallback#doWithSearcher(Searcher) */ public Object search(SearcherCallback callback) { Searcher searcher=SearcherFactoryUtils.getSearcher(getSearcherFactory()); try { return callback.doWithSearcher(searcher); } catch (IOException ex) { throw new LuceneSearchException("Error during searching",ex); } catch (ParseException ex) { throw new LuceneSearchException("Error during parsing query",ex); } finally { SearcherFactoryUtils.releaseSearcher(getSearcherFactory(),searcher); } } }