/* * Copyright 2002-2007 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.AbstractList; 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.Query; import org.apache.lucene.search.Sort; import org.springmodules.lucene.search.LuceneSearchException; import org.springmodules.lucene.search.factory.LuceneHits; import org.springmodules.lucene.search.factory.LuceneSearcher; 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 data 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 * @author Alex Burgel * @see org.springmodules.lucene.search.query.QueryCreator * @see org.springmodules.lucene.search.factory */ public class DefaultLuceneSearchTemplate implements 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 DefaultLuceneSearchTemplate() { } /** * 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 DefaultLuceneSearchTemplate(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; } /** * 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); } } /** * Build the default HitsExtractor. Used when an HitsExtractor is not provided. * @return the default HitsExtractor */ protected QueryResultCreator createDefaultQueryResultCreator() { return new QueryResultCreator() { public List createResult(LuceneHits hits, HitExtractor hitExtractor) throws IOException { List result = new ArrayList(); for(int cpt=0; cpt<hits.length(); cpt++) { result.add(hitExtractor.mapHit(hits.id(cpt), hits.doc(cpt), hits.score(cpt))); } return result; } }; } public List search(QueryCreator queryCreator, HitExtractor extractor) { return doSearch(createQuery(queryCreator), extractor, createDefaultQueryResultCreator(), null, null); } public List search(QueryCreator queryCreator, HitExtractor extractor, QueryResultCreator resultCreator) { return doSearch(createQuery(queryCreator), extractor, resultCreator, null, null); } public List search(Query query, HitExtractor extractor) { return doSearch(query, extractor, createDefaultQueryResultCreator(), null, null); } public List search(Query query, HitExtractor extractor, QueryResultCreator resultCreator) { return doSearch(query, extractor, resultCreator, null, null); } public List search(QueryCreator queryCreator, HitExtractor extractor, Filter filter) { return doSearch(createQuery(queryCreator), extractor, createDefaultQueryResultCreator(), filter, null); } public List search(QueryCreator queryCreator, HitExtractor extractor, QueryResultCreator resultCreator, Filter filter) { return doSearch(createQuery(queryCreator), extractor, resultCreator, filter, null); } public List search(Query query, HitExtractor extractor, Filter filter) { return doSearch(query, extractor, createDefaultQueryResultCreator(), filter, null); } public List search(Query query, HitExtractor extractor, QueryResultCreator resultCreator, Filter filter) { return doSearch(query, extractor, resultCreator, filter, null); } public List search(QueryCreator queryCreator, HitExtractor extractor, Sort sort) { return doSearch(createQuery(queryCreator), extractor, createDefaultQueryResultCreator(), null, sort); } public List search(QueryCreator queryCreator, HitExtractor extractor, QueryResultCreator resultCreator, Sort sort) { return doSearch(createQuery(queryCreator), extractor, resultCreator, null, sort); } public List search(Query query, HitExtractor extractor, Sort sort) { return doSearch(query, extractor, createDefaultQueryResultCreator(), null, sort); } public List search(Query query, HitExtractor extractor, QueryResultCreator resultCreator, Sort sort) { return doSearch(query, extractor, resultCreator, null, sort); } public List search(QueryCreator queryCreator, HitExtractor extractor, Filter filter, Sort sort) { return doSearch(createQuery(queryCreator), extractor, createDefaultQueryResultCreator(), filter, sort); } public List search(QueryCreator queryCreator, HitExtractor extractor, QueryResultCreator resultCreator, Filter filter, Sort sort) { return doSearch(createQuery(queryCreator), extractor, resultCreator, filter, sort); } public List search(Query query, HitExtractor extractor, Filter filter, Sort sort) { return doSearch(query, extractor, createDefaultQueryResultCreator(), filter, sort); } public List search(Query query, HitExtractor extractor, QueryResultCreator resultCreator, Filter filter, Sort sort) { return doSearch(query, extractor, resultCreator, 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 parameters 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 hitExtractor, QueryResultCreator queryResultCreator, Filter filter, Sort sort) { LuceneSearcher searcher = SearcherFactoryUtils.getSearcher(getSearcherFactory()); try { LuceneHits 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); return queryResultCreator.createResult(hits, hitExtractor); } catch (IOException ex) { throw new LuceneSearchException("Error during the search", ex); } finally { SearcherFactoryUtils.releaseSearcher(getSearcherFactory(), searcher); } } public void search(QueryCreator queryCreator, HitCollector results) { LuceneSearcher 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); } } public Object search(SearcherCallback callback) { LuceneSearcher searcher = SearcherFactoryUtils.getSearcher(getSearcherFactory()); try { return callback.doWithSearcher(searcher); } catch (ParseException ex) { throw new LuceneSearchException("Error during parsing query", ex); } catch (Exception ex) { throw new LuceneSearchException("Error during searching", ex); } finally { SearcherFactoryUtils.releaseSearcher(getSearcherFactory(),searcher); } } }