blob: e0fd65d8f9e2b73ad777205e4282870f3cb13f43 [file] [log] [blame]
using Lucene.Net.Diagnostics;
using System;
using System.IO;
namespace Lucene.Net.Search
{
/*
* 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.
*/
using Directory = Lucene.Net.Store.Directory;
using DirectoryReader = Lucene.Net.Index.DirectoryReader;
using IndexReader = Lucene.Net.Index.IndexReader;
using IndexWriter = Lucene.Net.Index.IndexWriter;
/// <summary>
/// Utility class to safely share <see cref="IndexSearcher"/> instances across multiple
/// threads, while periodically reopening. This class ensures each searcher is
/// disposed only once all threads have finished using it.
///
/// <para/>
/// Use <see cref="ReferenceManager{G}.Acquire()"/> to obtain the current searcher, and <see cref="ReferenceManager{G}.Release(G)"/> to
/// release it, like this:
///
/// <code>
/// IndexSearcher s = manager.Acquire();
/// try
/// {
/// // Do searching, doc retrieval, etc. with s
/// }
/// finally
/// {
/// manager.Release(s);
/// // Do not use s after this!
/// s = null;
/// }
/// </code>
///
/// <para/>
/// In addition you should periodically call <see cref="ReferenceManager{G}.MaybeRefresh()"/>. While it's
/// possible to call this just before running each query, this is discouraged
/// since it penalizes the unlucky queries that do the reopen. It's better to use
/// a separate background thread, that periodically calls <see cref="ReferenceManager{G}.MaybeRefresh()"/>. Finally,
/// be sure to call <see cref="ReferenceManager{G}.Dispose()"/> once you are done.
/// <para/>
/// @lucene.experimental
/// </summary>
/// <seealso cref="SearcherFactory"/>
public sealed class SearcherManager : ReferenceManager<IndexSearcher>
{
private readonly SearcherFactory searcherFactory;
/// <summary>
/// Creates and returns a new <see cref="SearcherManager"/> from the given
/// <see cref="IndexWriter"/>.
/// </summary>
/// <param name="writer">
/// The <see cref="IndexWriter"/> to open the <see cref="IndexReader"/> from. </param>
/// <param name="applyAllDeletes">
/// If <c>true</c>, all buffered deletes will be applied (made
/// visible) in the <see cref="IndexSearcher"/> / <see cref="DirectoryReader"/>.
/// If <c>false</c>, the deletes may or may not be applied, but
/// remain buffered (in <see cref="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 <c>false</c>. See
/// <see cref="DirectoryReader.OpenIfChanged(DirectoryReader, IndexWriter, bool)"/>. </param>
/// <param name="searcherFactory">
/// An optional <see cref="SearcherFactory"/>. Pass <c>null</c> if you
/// don't require the searcher to be warmed before going live or other
/// custom behavior.
/// </param>
/// <exception cref="IOException"> if there is a low-level I/O error </exception>
public SearcherManager(IndexWriter writer, bool applyAllDeletes, SearcherFactory searcherFactory)
{
if (searcherFactory == null)
{
searcherFactory = new SearcherFactory();
}
this.searcherFactory = searcherFactory;
Current = GetSearcher(searcherFactory, DirectoryReader.Open(writer, applyAllDeletes));
}
/// <summary>
/// Creates and returns a new <see cref="SearcherManager"/> from the given <see cref="Directory"/>. </summary>
/// <param name="dir"> The directory to open the <see cref="DirectoryReader"/> on. </param>
/// <param name="searcherFactory"> An optional <see cref="SearcherFactory"/>. Pass
/// <c>null</c> if you don't require the searcher to be warmed
/// before going live or other custom behavior.
/// </param>
/// <exception cref="IOException"> If there is a low-level I/O error </exception>
public SearcherManager(Directory dir, SearcherFactory searcherFactory)
{
if (searcherFactory == null)
{
searcherFactory = new SearcherFactory();
}
this.searcherFactory = searcherFactory;
Current = GetSearcher(searcherFactory, DirectoryReader.Open(dir));
}
protected override void DecRef(IndexSearcher reference)
{
reference.IndexReader.DecRef();
}
protected override IndexSearcher RefreshIfNeeded(IndexSearcher referenceToRefresh)
{
IndexReader r = referenceToRefresh.IndexReader;
if (Debugging.AssertsEnabled) Debugging.Assert(r is DirectoryReader,"searcher's IndexReader should be a DirectoryReader, but got {0}", r);
IndexReader newReader = DirectoryReader.OpenIfChanged((DirectoryReader)r);
if (newReader == null)
{
return null;
}
else
{
return GetSearcher(searcherFactory, newReader);
}
}
protected override bool TryIncRef(IndexSearcher reference)
{
return reference.IndexReader.TryIncRef();
}
protected override int GetRefCount(IndexSearcher reference)
{
return reference.IndexReader.RefCount;
}
/// <summary>
/// Returns <c>true</c> if no changes have occured since this searcher
/// ie. reader was opened, otherwise <c>false</c>. </summary>
/// <seealso cref="DirectoryReader.IsCurrent()"/>
public bool IsSearcherCurrent()
{
IndexSearcher searcher = Acquire();
try
{
IndexReader r = searcher.IndexReader;
if (Debugging.AssertsEnabled) Debugging.Assert(r is DirectoryReader,"searcher's IndexReader should be a DirectoryReader, but got {0}", r);
return ((DirectoryReader)r).IsCurrent();
}
finally
{
Release(searcher);
}
}
/// <summary>
/// Expert: creates a searcher from the provided
/// <see cref="IndexReader"/> using the provided
/// <see cref="SearcherFactory"/>. NOTE: this decRefs incoming reader
/// on throwing an exception.
/// </summary>
public static IndexSearcher GetSearcher(SearcherFactory searcherFactory, IndexReader reader)
{
bool success = false;
IndexSearcher searcher;
try
{
searcher = searcherFactory.NewSearcher(reader);
if (searcher.IndexReader != reader)
{
throw new InvalidOperationException("SearcherFactory must wrap exactly the provided reader (got " + searcher.IndexReader + " but expected " + reader + ")");
}
success = true;
}
finally
{
if (!success)
{
reader.DecRef();
}
}
return searcher;
}
}
}