blob: c3fbe29fb133ac30ff012d5e5dfb71cbdd3fc6f5 [file] [log] [blame]
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 ::= [&quot;+&quot;, &quot;-&quot;] [&lt;TERM&gt; &quot;:&quot;] ( &lt;TERM&gt; | &quot;(&quot; Query &quot;)&quot; )
/// </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);
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.Get(ConfigurationKeys.LOWERCASE_EXPANDED_TERMS) ?? 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) ?? false;
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) ?? false;
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 == 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 == null)
{
return FuzzyQuery.DefaultPrefixLength;
}
else
{
return fuzzyConfig.PrefixLength;
}
}
set
{
QueryConfigHandler config = QueryConfigHandler;
FuzzyConfig fuzzyConfig = config.Get(ConfigurationKeys.FUZZY_CONFIG);
if (fuzzyConfig == 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) ?? 0;
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 == 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="DateTools.Resolution"/> used for certain field when
/// no <see cref="DateTools.Resolution"/> is defined for this field.
/// </summary>
/// <param name="dateResolution">the default <see cref="DateTools.Resolution"/></param>
// LUCENENET NOTE: This method is required by the ICommonQueryParserConfiguration interface
public virtual void SetDateResolution(DateTools.Resolution dateResolution)
{
QueryConfigHandler.Set(ConfigurationKeys.DATE_RESOLUTION, dateResolution);
}
/// <summary>
/// Gets the default <see cref="DateTools.Resolution"/> used for certain field when
/// no <see cref="DateTools.Resolution"/> is defined for this field.
/// </summary>
public virtual DateTools.Resolution DateResolution => QueryConfigHandler.Get(ConfigurationKeys.DATE_RESOLUTION);
/// <summary>
/// Sets the <see cref="DateTools.Resolution"/> used for each field
/// </summary>
/// <param name="dateRes">a collection that maps a field to its <see cref="DateTools.Resolution"/></param>
[Obsolete("Use DateResolutionMap property instead.")]
public virtual void SetDateResolution(IDictionary<string, DateTools.Resolution?> dateRes)
{
DateResolutionMap = dateRes;
}
/// <summary>
/// Gets or Sets the field to <see cref="T:DateTools.Resolution?"/> map used to normalize each date field.
/// </summary>
public virtual IDictionary<string, DateTools.Resolution?> DateResolutionMap
{
get => QueryConfigHandler.Get(ConfigurationKeys.FIELD_DATE_RESOLUTION_MAP);
set => QueryConfigHandler.Set(ConfigurationKeys.FIELD_DATE_RESOLUTION_MAP, value);
}
}
}