-
Notifications
You must be signed in to change notification settings - Fork 623
/
SearcherManager.cs
193 lines (182 loc) · 8.07 KB
/
SearcherManager.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
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 IllegalStateException.Create("SearcherFactory must wrap exactly the provided reader (got " + searcher.IndexReader + " but expected " + reader + ")");
}
success = true;
}
finally
{
if (!success)
{
reader.DecRef();
}
}
return searcher;
}
}
}