/
StandardQueryParser.cs
415 lines (382 loc) · 17.5 KB
/
StandardQueryParser.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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
using Lucene.Net.Analysis;
using Lucene.Net.Documents;
using Lucene.Net.QueryParsers.Flexible.Core;
using Lucene.Net.QueryParsers.Flexible.Core.Config;
using Lucene.Net.QueryParsers.Flexible.Standard.Builders;
using Lucene.Net.QueryParsers.Flexible.Standard.Config;
using Lucene.Net.QueryParsers.Flexible.Standard.Parser;
using Lucene.Net.QueryParsers.Flexible.Standard.Processors;
using Lucene.Net.Search;
using Lucene.Net.Support;
using System;
using System.Collections.Generic;
using System.Globalization;
using Operator = Lucene.Net.QueryParsers.Flexible.Standard.Config.StandardQueryConfigHandler.Operator;
namespace Lucene.Net.QueryParsers.Flexible.Standard
{
/*
* 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.
*/
/// <summary>
/// This class is a helper that enables users to easily use the Lucene query
/// parser.
/// <para/>
/// To construct a Query object from a query string, use the
/// <see cref="Parse(string, string)"/> method:
/// <code>
/// StandardQueryParser queryParserHelper = new StandardQueryParser();
/// Query query = queryParserHelper.Parse("a AND b", "defaultField");
/// </code>
/// <para/>
/// To change any configuration before parsing the query string do, for example:
/// <code>
/// // the query config handler returned by StandardQueryParser is a
/// // StandardQueryConfigHandler
/// queryParserHelper.QueryConfigHandler.Analyzer = new WhitespaceAnalyzer();
/// </code>
/// <para/>
/// The syntax for query strings is as follows (copied from the old QueryParser
/// javadoc):
/// <para/>
/// A Query is a series of clauses. A clause may be prefixed by:
/// <list type="bullet">
/// <item><description>
/// a plus (<c>+</c>) or a minus (<c>-</c>) sign, indicating that
/// the clause is required or prohibited respectively; or
/// </description></item>
/// <item><description>
/// a term followed by a colon, indicating the field to be searched. This
/// enables one to construct queries which search multiple fields.
/// </description></item>
/// </list>
///
/// A clause may be either:
/// <list type="bullet">
/// <item><description>
/// a term, indicating all the documents that contain this term; or
/// </description></item>
/// <item><description>
/// a nested query, enclosed in parentheses. Note that this may be used with
/// a <c>+</c>/<c>-</c> prefix to require any of a set of terms.
/// </description></item>
/// </list>
///
/// Thus, in BNF, the query grammar is:
/// <code>
/// Query ::= ( Clause )*
/// Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")" )
/// </code>
///
/// <para>
/// Examples of appropriately formatted queries can be found in the query syntax documentation.
/// </para>
/// <para>
/// The text parser used by this helper is a <see cref="StandardSyntaxParser"/>.
/// </para>
/// <para>
/// The query node processor used by this helper is a
/// <see cref="StandardQueryNodeProcessorPipeline"/>.
/// </para>
/// <para>
/// The builder used by this helper is a <see cref="StandardQueryTreeBuilder"/>.
/// </para>
/// </summary>
/// <seealso cref="StandardQueryParser"/>
/// <seealso cref="StandardQueryConfigHandler"/>
/// <seealso cref="StandardSyntaxParser"/>
/// <seealso cref="StandardQueryNodeProcessorPipeline"/>
/// <seealso cref="StandardQueryTreeBuilder"/>
public class StandardQueryParser : QueryParserHelper<Query>, ICommonQueryParserConfiguration
{
/// <summary>
/// Constructs a <see cref="StandardQueryParser"/> object.
/// </summary>
public StandardQueryParser()
: base(new StandardQueryConfigHandler(), new StandardSyntaxParser(),
new StandardQueryNodeProcessorPipeline(null),
new StandardQueryTreeBuilder())
{
EnablePositionIncrements = true;
}
/// <summary>
/// Constructs a <see cref="StandardQueryParser"/> object and sets an
/// <see cref="Analysis.Analyzer"/> to it. The same as:
/// <code>
/// StandardQueryParser qp = new StandardQueryParser();
/// qp.QueryConfigHandler.Analyzer = analyzer;
/// </code>
/// </summary>
/// <param name="analyzer">the analyzer to be used by this query parser helper</param>
public StandardQueryParser(Analyzer analyzer)
: this()
{
this.Analyzer = analyzer;
}
public override string ToString()
{
return "<StandardQueryParser config=\"" + this.QueryConfigHandler
+ "\"/>";
}
/// <summary>
/// Overrides <see cref="QueryParserHelper{TQuery}.Parse(string, string)"/> so it casts the
/// return object to <see cref="Query"/>. For more reference about this method, check
/// <see cref="QueryParserHelper{TQuery}.Parse(string, string)"/>.
/// </summary>
/// <param name="query">the query string</param>
/// <param name="defaultField">the default field used by the text parser</param>
/// <returns>the object built from the query</returns>
/// <exception cref="QueryNodeException">if something wrong happens along the three phases</exception>
public override Query Parse(string query, string defaultField)
{
return base.Parse(query, defaultField);
}
/// <summary>
/// Gets or Sets the boolean operator of the QueryParser. In default mode (
/// <see cref="Operator.OR"/>) terms without any modifiers are considered optional:
/// for example <c>capital of Hungary</c> is equal to
/// <c>capital OR of OR Hungary</c>.
/// <para/>
/// In <see cref="Operator.AND"/> mode terms are considered to be in conjunction: the
/// above mentioned query is parsed as <c>capital AND of AND Hungary</c>
/// </summary>
public virtual Operator DefaultOperator
{
get => QueryConfigHandler.Get(ConfigurationKeys.DEFAULT_OPERATOR); // LUCENENET: The default value is OR, so we just rely on the compiler if it doesn't exist
set => QueryConfigHandler.Set(ConfigurationKeys.DEFAULT_OPERATOR, value);
}
/// <summary>
/// Set to <c>true</c> to allow leading wildcard characters.
/// <para/>
/// When set, <c>*</c> or <c>?</c> are allowed as the first
/// character of a <see cref="PrefixQuery"/> and <see cref="WildcardQuery"/>. Note that this can produce
/// very slow queries on big indexes.
/// <para/>
/// Default: false.
/// </summary>
public virtual bool LowercaseExpandedTerms
{
get => QueryConfigHandler.TryGetValue(ConfigurationKeys.LOWERCASE_EXPANDED_TERMS, out bool value) ? value : true;
set => QueryConfigHandler.Set(ConfigurationKeys.LOWERCASE_EXPANDED_TERMS, value);
}
/// <summary>
/// Set to <c>true</c> to allow leading wildcard characters.
/// <para/>
/// When set, <c>*</c> or <c>?</c> are allowed as the first
/// character of a <see cref="PrefixQuery"/> and <see cref="WildcardQuery"/>. Note that this can produce
/// very slow queries on big indexes.
/// <para/>
/// Default: false.
/// </summary>
public virtual bool AllowLeadingWildcard
{
get => QueryConfigHandler.Get(ConfigurationKeys.ALLOW_LEADING_WILDCARD); // LUCENENET: The default value is false, so we just rely on the compiler if it doesn't exist
set => QueryConfigHandler.Set(ConfigurationKeys.ALLOW_LEADING_WILDCARD, value);
}
/// <summary>
/// Set to <c>true</c> to enable position increments in result query.
/// <para/>
/// When set, result phrase and multi-phrase queries will be aware of position
/// increments. Useful when e.g. a <see cref="Analysis.Core.StopFilter"/> increases the position increment
/// of the token that follows an omitted token.
/// <para/>
/// Default: false.
/// </summary>
public virtual bool EnablePositionIncrements
{
get => QueryConfigHandler.Get(ConfigurationKeys.ENABLE_POSITION_INCREMENTS); // LUCENENET: The default value is false, so we just rely on the compiler if it doesn't exist
set => QueryConfigHandler.Set(ConfigurationKeys.ENABLE_POSITION_INCREMENTS, value);
}
/// <summary>
/// By default, it uses
/// <see cref="MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT"/> when creating a
/// prefix, wildcard and range queries. This implementation is generally
/// preferable because it a) Runs faster b) Does not have the scarcity of terms
/// unduly influence score c) avoids any Exception due to too many listeners.
/// However, if your application really needs to use the
/// old-fashioned boolean queries expansion rewriting and the above points are
/// not relevant then use this change the rewrite method.
/// </summary>
public virtual MultiTermQuery.RewriteMethod MultiTermRewriteMethod
{
get => QueryConfigHandler.Get(ConfigurationKeys.MULTI_TERM_REWRITE_METHOD);
set => QueryConfigHandler.Set(ConfigurationKeys.MULTI_TERM_REWRITE_METHOD, value);
}
/// <summary>
/// Set the fields a query should be expanded to when the field is
/// <c>null</c>
/// </summary>
/// <param name="fields">the fields used to expand the query</param>
public virtual void SetMultiFields(string[] fields)
{
if (fields is null)
{
fields = Arrays.Empty<string>();
}
QueryConfigHandler.Set(ConfigurationKeys.MULTI_FIELDS, fields);
}
/// <summary>
/// Returns the fields used to expand the query when the field for a
/// certain query is <c>null</c>
/// </summary>
/// <returns>the fields used to expand the query</returns>
public virtual string[] GetMultiFields()
{
return QueryConfigHandler.Get(ConfigurationKeys.MULTI_FIELDS);
}
/// <summary>
/// Gets or Sets the prefix length for fuzzy queries. Default is 0.
/// </summary>
public virtual int FuzzyPrefixLength
{
get
{
FuzzyConfig fuzzyConfig = QueryConfigHandler.Get(ConfigurationKeys.FUZZY_CONFIG);
if (fuzzyConfig is null)
{
return FuzzyQuery.DefaultPrefixLength;
}
else
{
return fuzzyConfig.PrefixLength;
}
}
set
{
QueryConfigHandler config = QueryConfigHandler;
FuzzyConfig fuzzyConfig = config.Get(ConfigurationKeys.FUZZY_CONFIG);
if (fuzzyConfig is null)
{
fuzzyConfig = new FuzzyConfig();
config.Set(ConfigurationKeys.FUZZY_CONFIG, fuzzyConfig);
}
fuzzyConfig.PrefixLength = value;
}
}
public virtual IDictionary<string, NumericConfig> NumericConfigMap
{
get => QueryConfigHandler.Get(ConfigurationKeys.NUMERIC_CONFIG_MAP);
set => QueryConfigHandler.Set(ConfigurationKeys.NUMERIC_CONFIG_MAP, value);
}
/// <summary>
/// Gets or Sets current locale, allowing access by subclasses. Used by date range parsing
/// </summary>
public virtual CultureInfo Locale
{
get
{
var culture = QueryConfigHandler.Get(ConfigurationKeys.LOCALE);
return culture ?? CultureInfo.CurrentCulture;
}
set => QueryConfigHandler.Set(ConfigurationKeys.LOCALE, value);
}
public virtual TimeZoneInfo TimeZone
{
get => QueryConfigHandler.Get(ConfigurationKeys.TIMEZONE) ?? TimeZoneInfo.Local;
set => QueryConfigHandler.Set(ConfigurationKeys.TIMEZONE, value);
}
/// <summary>
/// Sets the default slop for phrases. If zero, then exact phrase matches are
/// required. Default value is zero.
/// </summary>
/// <param name="defaultPhraseSlop"></param>
[Obsolete("Use PhraseSlop property setter instead.")]
public virtual void SetDefaultPhraseSlop(int defaultPhraseSlop)
{
QueryConfigHandler.Set(ConfigurationKeys.PHRASE_SLOP, defaultPhraseSlop);
}
public virtual Analyzer Analyzer
{
get => QueryConfigHandler.Get(ConfigurationKeys.ANALYZER);
set => QueryConfigHandler.Set(ConfigurationKeys.ANALYZER, value);
}
/// <summary>
/// Gets or Sets the default slop for phrases. If zero, then exact phrase matches are
/// required. Default value is zero. NOTE: Setter is deprecated.
/// </summary>
public virtual int PhraseSlop
{
get => QueryConfigHandler.Get(ConfigurationKeys.PHRASE_SLOP); // LUCENENET: The default value is 0, so we just rely on the compiler if it doesn't exist
set => QueryConfigHandler.Set(ConfigurationKeys.PHRASE_SLOP, value);
}
/// <summary>
/// Gets or Sets the minimum similarity for fuzzy queries. Default is defined on
/// <see cref="FuzzyQuery.DefaultMinSimilarity"/>.
/// </summary>
public virtual float FuzzyMinSim
{
get
{
FuzzyConfig fuzzyConfig = QueryConfigHandler.Get(ConfigurationKeys.FUZZY_CONFIG);
return (fuzzyConfig != null) ? fuzzyConfig.MinSimilarity
#pragma warning disable 612, 618
: FuzzyQuery.DefaultMinSimilarity;
#pragma warning restore 612, 618
}
set
{
QueryConfigHandler config = QueryConfigHandler;
FuzzyConfig fuzzyConfig = config.Get(ConfigurationKeys.FUZZY_CONFIG);
if (fuzzyConfig is null)
{
fuzzyConfig = new FuzzyConfig();
config.Set(ConfigurationKeys.FUZZY_CONFIG, fuzzyConfig);
}
fuzzyConfig.MinSimilarity = value;
}
}
/// <summary>
/// Gets or Sets the field to boost map used to set boost for each field.
/// </summary>
public virtual IDictionary<string, float> FieldsBoost
{
get => QueryConfigHandler.Get(ConfigurationKeys.FIELD_BOOST_MAP);
set => QueryConfigHandler.Set(ConfigurationKeys.FIELD_BOOST_MAP, value);
}
/// <summary>
/// Sets the default <see cref="Documents.DateResolution"/> used for certain field when
/// no <see cref="Documents.DateResolution"/> is defined for this field.
/// </summary>
/// <param name="dateResolution">the default <see cref="Documents.DateResolution"/></param>
// LUCENENET NOTE: This method is required by the ICommonQueryParserConfiguration interface
public virtual void SetDateResolution(DateResolution dateResolution)
{
QueryConfigHandler.Set(ConfigurationKeys.DATE_RESOLUTION, dateResolution);
}
/// <summary>
/// Gets the default <see cref="Documents.DateResolution"/> used for certain field when
/// no <see cref="Documents.DateResolution"/> is defined for this field.
/// </summary>
[ExceptionToNullableEnumConvention]
public virtual DateResolution? DateResolution => QueryConfigHandler.TryGetValue(ConfigurationKeys.DATE_RESOLUTION, out DateResolution value) ? value : null;
/// <summary>
/// Sets the <see cref="Documents.DateResolution"/> used for each field
/// </summary>
/// <param name="dateRes">a collection that maps a field to its <see cref="Documents.DateResolution"/></param>
[Obsolete("Use DateResolutionMap property instead.")]
public virtual void SetDateResolution(IDictionary<string, DateResolution> dateRes)
{
DateResolutionMap = dateRes;
}
/// <summary>
/// Gets or Sets the field to <see cref="Documents.DateResolution"/> map used to normalize each date field.
/// </summary>
public virtual IDictionary<string, DateResolution> DateResolutionMap
{
get => QueryConfigHandler.Get(ConfigurationKeys.FIELD_DATE_RESOLUTION_MAP);
set => QueryConfigHandler.Set(ConfigurationKeys.FIELD_DATE_RESOLUTION_MAP, value);
}
}
}