/* * Copyright 2004-2009 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.compass.core.lucene.engine.transaction.support; import java.io.IOException; import java.util.ArrayList; import org.apache.commons.logging.Log; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.MultiReader; import org.apache.lucene.search.Filter; import org.apache.lucene.search.Hits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MultiSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.Sort; import org.compass.core.engine.SearchEngineException; import org.compass.core.lucene.LuceneEnvironment; import org.compass.core.lucene.engine.LuceneSearchEngine; import org.compass.core.lucene.engine.LuceneSearchEngineFactory; import org.compass.core.lucene.engine.LuceneSearchEngineInternalSearch; import org.compass.core.lucene.engine.LuceneSearchEngineQuery; import org.compass.core.lucene.engine.manager.LuceneIndexHolder; import org.compass.core.lucene.engine.manager.LuceneSearchEngineIndexManager; import org.compass.core.lucene.engine.transaction.TransactionProcessor; import org.compass.core.lucene.search.CacheableMultiReader; import org.compass.core.mapping.CompassMapping; import org.compass.core.mapping.ResourceMapping; /** * A base class for all Lucene based transactions. Provides helper methods for * Lucene index transaction management, and default state management for the * transcational operations. * * @author kimchy */ public abstract class AbstractTransactionProcessor implements TransactionProcessor { protected final Log logger; protected final LuceneSearchEngine searchEngine; protected final LuceneSearchEngineFactory searchEngineFactory; protected final LuceneSearchEngineIndexManager indexManager; protected final CompassMapping mapping; protected AbstractTransactionProcessor(Log logger, LuceneSearchEngine searchEngine) { this.logger = logger; this.searchEngine = searchEngine; this.searchEngineFactory = searchEngine.getSearchEngineFactory(); this.indexManager = searchEngineFactory.getLuceneIndexManager(); this.mapping = searchEngine.getSearchEngineFactory().getMapping(); } protected ResourceMapping getResourceMapping(String alias) { return mapping.getRootMappingByAlias(alias); } protected LuceneSearchEngineInternalSearch buildInternalSearch(String[] subIndexes, String[] aliases, boolean useFieldCache) throws SearchEngineException { ArrayList<LuceneIndexHolder> indexHoldersToClose = new ArrayList<LuceneIndexHolder>(); try { String[] calcSubIndexes = indexManager.getStore().calcSubIndexes(subIndexes, aliases); // currenly we disable search by multireader and do it with multisearcher so field cache // will work correclty (as we won't create a new "outer" reader each time, which will cause // the field cache to invalidate each time if (!useFieldCache) { ArrayList<IndexReader> readers = new ArrayList<IndexReader>(calcSubIndexes.length); LuceneIndexHolder lastNonEmptyIndexHolder = null; for (String subIndex : calcSubIndexes) { LuceneIndexHolder indexHolder = indexManager.getIndexHoldersCache().getHolder(subIndex); indexHoldersToClose.add(indexHolder); if (indexHolder.getIndexReader().numDocs() > 0) { readers.add(indexHolder.getIndexReader()); lastNonEmptyIndexHolder = indexHolder; } } if (readers.size() == 0) { return new LuceneSearchEngineInternalSearch(searchEngine, indexHoldersToClose); } // if we have just one reader, no need to create a multi reader on top of it if (readers.size() == 1) { return new LuceneSearchEngineInternalSearch(searchEngine, lastNonEmptyIndexHolder, indexHoldersToClose); } MultiReader reader = new CacheableMultiReader(readers.toArray(new IndexReader[readers.size()]), false); IndexSearcher searcher = indexManager.openIndexSearcher(reader); return new LuceneSearchEngineInternalSearch(searchEngine, reader, searcher, indexHoldersToClose); } else { ArrayList<IndexSearcher> searchers = new ArrayList<IndexSearcher>(calcSubIndexes.length); LuceneIndexHolder lastNonEmptyIndexHolder = null; for (String subIndex : calcSubIndexes) { LuceneIndexHolder indexHolder = indexManager.getIndexHoldersCache().getHolder(subIndex); indexHoldersToClose.add(indexHolder); if (indexHolder.getIndexReader().numDocs() > 0) { searchers.add(indexHolder.getIndexSearcher()); lastNonEmptyIndexHolder = indexHolder; } } if (searchers.size() == 0) { return new LuceneSearchEngineInternalSearch(searchEngine, indexHoldersToClose); } // if we have just one reader, no need to create a multi reader on top of it if (searchers.size() == 1) { return new LuceneSearchEngineInternalSearch(searchEngine, lastNonEmptyIndexHolder, indexHoldersToClose); } MultiSearcher searcher = indexManager.openMultiSearcher(searchers.toArray(new IndexSearcher[searchers.size()])); return new LuceneSearchEngineInternalSearch(searchEngine, searcher, indexHoldersToClose); } } catch (Exception e) { for (LuceneIndexHolder indexHolder : indexHoldersToClose) { indexHolder.release(); } throw new SearchEngineException("Failed to open Lucene reader/searcher", e); } } protected Hits findByQuery(LuceneSearchEngineInternalSearch internalSearch, LuceneSearchEngineQuery searchEngineQuery, Filter filter) throws SearchEngineException { Query query = searchEngineQuery.getQuery(); if (searchEngineQuery.isRewrite()) { try { query = query.rewrite(internalSearch.getReader()); } catch (IOException e) { throw new SearchEngineException("Failed to rewrite query [" + query.toString() + "]", e); } } Sort sort = searchEngineQuery.getSort(); Hits hits; try { if (filter == null) { hits = internalSearch.getSearcher().search(query, sort); } else { hits = internalSearch.getSearcher().search(query, filter, sort); } } catch (IOException e) { throw new SearchEngineException("Failed to search with query [" + query + "]", e); } return hits; } protected boolean isInvalidateCacheOnCommit() { return searchEngine.getSettings().getSettingAsBoolean(LuceneEnvironment.Transaction.CLEAR_CACHE_ON_COMMIT, true); } /** * Returns the concatanation of {@link org.compass.core.lucene.LuceneEnvironment.Transaction.Processor#PREFIX} + * {@link #getName()} + "." + <code>settingName</code>. */ protected final String getSettingName(String settingName) { return LuceneEnvironment.Transaction.Processor.PREFIX + getName() + "." + settingName; } }