| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.apache.lucene.search; |
| |
| import java.io.IOException; |
| import org.apache.lucene.index.DirectoryReader; |
| import org.apache.lucene.index.IndexReader; |
| import org.apache.lucene.index.IndexWriter; |
| import org.apache.lucene.store.Directory; |
| |
| /** |
| * Utility class to safely share {@link IndexSearcher} instances across multiple threads, while |
| * periodically reopening. This class ensures each searcher is closed only once all threads have |
| * finished using it. |
| * |
| * <p>Use {@link #acquire} to obtain the current searcher, and {@link #release} to release it, like |
| * this: |
| * |
| * <pre class="prettyprint"> |
| * IndexSearcher s = manager.acquire(); |
| * try { |
| * // Do searching, doc retrieval, etc. with s |
| * } finally { |
| * manager.release(s); |
| * } |
| * // Do not use s after this! |
| * s = null; |
| * </pre> |
| * |
| * <p>In addition you should periodically call {@link #maybeRefresh}. While it's possible to call |
| * this just before running each query, this is discouraged since it penalizes the unlucky queries |
| * that need to refresh. It's better to use a separate background thread, that periodically calls |
| * {@link #maybeRefresh}. Finally, be sure to call {@link #close} once you are done. |
| * |
| * @see SearcherFactory |
| * @lucene.experimental |
| */ |
| public final class SearcherManager extends ReferenceManager<IndexSearcher> { |
| |
| private final SearcherFactory searcherFactory; |
| |
| /** |
| * Creates and returns a new SearcherManager from the given {@link IndexWriter}. |
| * |
| * @param writer the IndexWriter to open the IndexReader from. |
| * @param searcherFactory An optional {@link SearcherFactory}. Pass <code>null</code> if you don't |
| * require the searcher to be warmed before going live or other custom behavior. |
| * @throws IOException if there is a low-level I/O error |
| */ |
| public SearcherManager(IndexWriter writer, SearcherFactory searcherFactory) throws IOException { |
| this(writer, true, false, searcherFactory); |
| } |
| |
| /** |
| * Expert: creates and returns a new SearcherManager from the given {@link IndexWriter}, |
| * controlling whether past deletions should be applied. |
| * |
| * @param writer the IndexWriter to open the IndexReader from. |
| * @param applyAllDeletes If <code>true</code>, all buffered deletes will be applied (made |
| * visible) in the {@link IndexSearcher} / {@link DirectoryReader}. If <code>false</code>, the |
| * deletes may or may not be applied, but remain buffered (in IndexWriter) so that they will |
| * be applied in the future. Applying deletes can be costly, so if your app can tolerate |
| * deleted documents being returned you might gain some performance by passing <code>false |
| * </code>. See {@link DirectoryReader#openIfChanged(DirectoryReader, IndexWriter, boolean)}. |
| * @param writeAllDeletes If <code>true</code>, new deletes will be forcefully written to index |
| * files. |
| * @param searcherFactory An optional {@link SearcherFactory}. Pass <code>null</code> if you don't |
| * require the searcher to be warmed before going live or other custom behavior. |
| * @throws IOException if there is a low-level I/O error |
| */ |
| public SearcherManager( |
| IndexWriter writer, |
| boolean applyAllDeletes, |
| boolean writeAllDeletes, |
| SearcherFactory searcherFactory) |
| throws IOException { |
| if (searcherFactory == null) { |
| searcherFactory = new SearcherFactory(); |
| } |
| this.searcherFactory = searcherFactory; |
| current = |
| getSearcher( |
| searcherFactory, DirectoryReader.open(writer, applyAllDeletes, writeAllDeletes), null); |
| } |
| |
| /** |
| * Creates and returns a new SearcherManager from the given {@link Directory}. |
| * |
| * @param dir the directory to open the DirectoryReader on. |
| * @param searcherFactory An optional {@link SearcherFactory}. Pass <code>null</code> if you don't |
| * require the searcher to be warmed before going live or other custom behavior. |
| * @throws IOException if there is a low-level I/O error |
| */ |
| public SearcherManager(Directory dir, SearcherFactory searcherFactory) throws IOException { |
| if (searcherFactory == null) { |
| searcherFactory = new SearcherFactory(); |
| } |
| this.searcherFactory = searcherFactory; |
| current = getSearcher(searcherFactory, DirectoryReader.open(dir), null); |
| } |
| |
| /** |
| * Creates and returns a new SearcherManager from an existing {@link DirectoryReader}. Note that |
| * this steals the incoming reference. |
| * |
| * @param reader the DirectoryReader. |
| * @param searcherFactory An optional {@link SearcherFactory}. Pass <code>null</code> if you don't |
| * require the searcher to be warmed before going live or other custom behavior. |
| * @throws IOException if there is a low-level I/O error |
| */ |
| public SearcherManager(DirectoryReader reader, SearcherFactory searcherFactory) |
| throws IOException { |
| if (searcherFactory == null) { |
| searcherFactory = new SearcherFactory(); |
| } |
| this.searcherFactory = searcherFactory; |
| this.current = getSearcher(searcherFactory, reader, null); |
| } |
| |
| @Override |
| protected void decRef(IndexSearcher reference) throws IOException { |
| reference.getIndexReader().decRef(); |
| } |
| |
| @Override |
| protected IndexSearcher refreshIfNeeded(IndexSearcher referenceToRefresh) throws IOException { |
| final IndexReader r = referenceToRefresh.getIndexReader(); |
| assert r instanceof DirectoryReader |
| : "searcher's IndexReader should be a DirectoryReader, but got " + r; |
| final IndexReader newReader = DirectoryReader.openIfChanged((DirectoryReader) r); |
| if (newReader == null) { |
| return null; |
| } else { |
| return getSearcher(searcherFactory, newReader, r); |
| } |
| } |
| |
| @Override |
| protected boolean tryIncRef(IndexSearcher reference) { |
| return reference.getIndexReader().tryIncRef(); |
| } |
| |
| @Override |
| protected int getRefCount(IndexSearcher reference) { |
| return reference.getIndexReader().getRefCount(); |
| } |
| |
| /** |
| * Returns <code>true</code> if no changes have occurred since this searcher ie. reader was |
| * opened, otherwise <code>false</code>. |
| * |
| * @see DirectoryReader#isCurrent() |
| */ |
| public boolean isSearcherCurrent() throws IOException { |
| final IndexSearcher searcher = acquire(); |
| try { |
| final IndexReader r = searcher.getIndexReader(); |
| assert r instanceof DirectoryReader |
| : "searcher's IndexReader should be a DirectoryReader, but got " + r; |
| return ((DirectoryReader) r).isCurrent(); |
| } finally { |
| release(searcher); |
| } |
| } |
| |
| /** |
| * Expert: creates a searcher from the provided {@link IndexReader} using the provided {@link |
| * SearcherFactory}. NOTE: this decRefs incoming reader on throwing an exception. |
| */ |
| public static IndexSearcher getSearcher( |
| SearcherFactory searcherFactory, IndexReader reader, IndexReader previousReader) |
| throws IOException { |
| boolean success = false; |
| final IndexSearcher searcher; |
| try { |
| searcher = searcherFactory.newSearcher(reader, previousReader); |
| if (searcher.getIndexReader() != reader) { |
| throw new IllegalStateException( |
| "SearcherFactory must wrap exactly the provided reader (got " |
| + searcher.getIndexReader() |
| + " but expected " |
| + reader |
| + ")"); |
| } |
| success = true; |
| } finally { |
| if (!success) { |
| reader.decRef(); |
| } |
| } |
| return searcher; |
| } |
| } |