/**
* Copyright 2008 - 2009 Pro-Netics S.P.A.
*
* 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 it.pronetics.madstore.repository.index.impl;
import it.pronetics.madstore.repository.index.PropertyPath;
import it.pronetics.madstore.repository.index.SearchResult;
import it.pronetics.madstore.repository.support.AtomIndexingException;
import it.pronetics.madstore.repository.util.PagingList;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.MapFieldSelector;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopFieldDocs;
import org.apache.lucene.store.Directory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Low-level Lucene-based class for searching Atom entries.
*
* @author Salvatore Incandela
* @author Sergio Bossa
*/
public class LuceneSearcher {
private static final Logger LOG = LoggerFactory.getLogger(LuceneSearcher.class);
private ThreadLocalSearcher threadLocalSeacher = new ThreadLocalSearcher();
private Directory directory;
private List<PropertyPath> indexedProperties;
public LuceneSearcher(Directory directory, List<PropertyPath> indexedProperties) {
this.directory = directory;
this.indexedProperties = indexedProperties;
}
public List<SearchResult> searchCollectionByFullText(String collectionKey, List<String> terms) {
return searchCollectionByFullText(collectionKey, terms, 0, 0);
}
public PagingList<SearchResult> searchCollectionByFullText(String collectionKey, List<String> terms, int offset, int max) {
try {
Query query = makeQueryFor(collectionKey, terms);
return doSearch(query, offset, max);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return null;
}
private Query makeQueryFor(String collectionKey, List<String> terms) {
BooleanQuery collectionKeyQuery = new BooleanQuery();
collectionKeyQuery.add(new TermQuery(new Term(LuceneIndexManager.INDEX_COLLECTION_KEY, collectionKey)), BooleanClause.Occur.MUST);
BooleanQuery termsQuery = new BooleanQuery();
for (String term : terms) {
BooleanQuery fieldQuery = new BooleanQuery();
for (PropertyPath property : indexedProperties) {
String field = property.getName();
String lowerCaseTerm = term.toLowerCase();
fieldQuery.add(new TermQuery(new Term(field, lowerCaseTerm)), BooleanClause.Occur.SHOULD);
}
termsQuery.add(fieldQuery, BooleanClause.Occur.MUST);
}
BooleanQuery result = new BooleanQuery();
result.add(collectionKeyQuery, BooleanClause.Occur.MUST);
result.add(termsQuery, BooleanClause.Occur.MUST);
return result;
}
private PagingList<SearchResult> doSearch(Query query, int offset, int max) throws Exception {
if (max == 0) {
max = LuceneIndexManager.DEFAULT_MAX_SEARCH_RESULTS;
}
int limit = offset + max;
IndexSearcher searcher = threadLocalSeacher.get();
TopFieldDocs topFieldDocs = searcher.search(query, null, limit, new Sort(SortField.FIELD_SCORE));
PagingList<SearchResult> entryItems = new PagingList<SearchResult>(
new ArrayList<SearchResult>(),
offset,
max,
topFieldDocs.totalHits);
for (int i = offset; i < (offset + max) && i < topFieldDocs.totalHits; i++) {
Document doc = searcher.doc(topFieldDocs.scoreDocs[i].doc, new MapFieldSelector(new String[]{LuceneIndexManager.INDEX_COLLECTION_KEY, LuceneIndexManager.INDEX_ENTRY_KEY}));
String collectionKey = doc.get(LuceneIndexManager.INDEX_COLLECTION_KEY);
String entryKey = doc.get(LuceneIndexManager.INDEX_ENTRY_KEY);
if (collectionKey != null && entryKey != null) {
entryItems.add(new SearchResult(collectionKey, entryKey));
} else {
LOG.warn("Found an entry with missing collection ({}) or entry ({}) key.", collectionKey, entryKey);
}
}
return entryItems;
}
private class ThreadLocalSearcher extends ThreadLocal<IndexSearcher> {
@Override
protected IndexSearcher initialValue() {
try {
return new IndexSearcher(directory);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
return null;
}
}
@Override
public IndexSearcher get() {
try {
IndexSearcher currentSearcher = super.get();
boolean isCurrent = currentSearcher.getIndexReader().isCurrent();
if (isCurrent) {
return currentSearcher;
} else {
currentSearcher.close();
IndexSearcher newSearcher = new IndexSearcher(directory);
super.set(newSearcher);
return newSearcher;
}
} catch (Exception e) {
throw new AtomIndexingException(e.getMessage(), e);
}
}
}
}