/* SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. * * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. 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 System; using System.Runtime.Serialization; using OpenSearch.Net; using OpenSearch.Net.Utf8Json; namespace OpenSearch.Client { /// /// Multi term query rewrite method /// [StringEnum] public enum RewriteMultiTerm { /// /// A rewrite method that performs like constant_score_boolean when there are few matching terms and otherwise /// visits all matching terms in sequence and marks documents for that term. Matching documents are assigned a /// constant score equal to the query’s boost. /// [EnumMember(Value = "constant_score")] ConstantScore, /// /// A rewrite method that first translates each term into a should clause in a boolean query, and keeps the scores /// as computed by the query. Note that typically such scores are meaningless to the user, and require non-trivial /// CPU to compute. This rewrite method will hit too many /// clauses failure if it exceeds the boolean query limit (defaults to 1024). /// [EnumMember(Value = "scoring_boolean")] ScoringBoolean, /// /// Similar to scoring_boolean except scores are not computed. Instead, each matching document receives a constant /// score equal to the query’s boost. This rewrite method will hit too many clauses failure if it exceeds the /// boolean query limit (defaults to 1024). /// [EnumMember(Value = "constant_score_boolean")] ConstantScoreBoolean, /// /// A rewrite method that first translates each term into should clause in boolean query, and keeps the scores /// as computed by the query. This rewrite method only uses the top scoring terms so it will not overflow boolean /// max clause count. The N controls the size of the top scoring terms to use. /// [EnumMember(Value = "top_terms_N")] TopTermsN, /// /// A rewrite method that first translates each term into should clause in boolean query, but the scores are only /// computed as the boost. This rewrite method only uses the top scoring terms so it will not overflow the boolean /// max clause count. The N controls the size of the top scoring terms to use. /// [EnumMember(Value = "top_terms_boost_N")] TopTermsBoostN, /// /// A rewrite method that first translates each term into should clause in boolean query, but all term queries compute /// scores as if they had the same frequency. In practice the frequency which is used is the maximum frequency of all /// matching terms. This rewrite method only uses the top scoring terms so it will not overflow boolean max clause count. /// The N controls the size of the top scoring terms to use. /// [EnumMember(Value = "top_terms_blended_freqs_N")] TopTermsBlendedFreqsN } /// /// Controls how a multi term query such as a wildcard or prefix query, is rewritten. /// [JsonFormatter(typeof(MultiTermQueryRewriteFormatter))] public class MultiTermQueryRewrite : IEquatable { private static readonly char[] DigitCharacters = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; private readonly string _value; internal MultiTermQueryRewrite(RewriteMultiTerm rewrite, int? size = null) { switch (rewrite) { case RewriteMultiTerm.ConstantScore: case RewriteMultiTerm.ScoringBoolean: case RewriteMultiTerm.ConstantScoreBoolean: _value = rewrite.ToEnumValue(); break; case RewriteMultiTerm.TopTermsN: case RewriteMultiTerm.TopTermsBoostN: case RewriteMultiTerm.TopTermsBlendedFreqsN: if (size == null) throw new ArgumentException($"{nameof(size)} must be specified with {nameof(RewriteMultiTerm)}.{rewrite}"); var rewriteType = rewrite.ToEnumValue(); rewriteType = rewriteType.Substring(0, rewriteType.Length - 1); _value = rewriteType + size; break; default: throw new ArgumentOutOfRangeException(nameof(rewrite)); } Rewrite = rewrite; Size = size; } /// /// A rewrite method that performs like constant_score_boolean when there are few matching terms and otherwise /// visits all matching terms in sequence and marks documents for that term. Matching documents are assigned a /// constant score equal to the query’s boost. /// public static MultiTermQueryRewrite ConstantScore { get; } = new MultiTermQueryRewrite(RewriteMultiTerm.ConstantScore); /// /// Similar to scoring_boolean except scores are not computed. Instead, each matching document receives a constant /// score equal to the query’s boost. This rewrite method will hit too many clauses failure if it exceeds the /// boolean query limit (defaults to 1024). /// public static MultiTermQueryRewrite ConstantScoreBoolean { get; } = new MultiTermQueryRewrite(RewriteMultiTerm.ConstantScoreBoolean); /// /// The type of multi term rewrite to perform /// public RewriteMultiTerm Rewrite { get; } /// /// A rewrite method that first translates each term into a should clause in a boolean query, and keeps the scores /// as computed by the query. Note that typically such scores are meaningless to the user, and require non-trivial /// CPU to compute. This rewrite method will hit too many /// clauses failure if it exceeds the boolean query limit (defaults to 1024). /// public static MultiTermQueryRewrite ScoringBoolean { get; } = new MultiTermQueryRewrite(RewriteMultiTerm.ScoringBoolean); /// /// The size of the top scoring terms to use /// public int? Size { get; } public bool Equals(MultiTermQueryRewrite other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Rewrite == other.Rewrite && Size == other.Size; } /// /// A rewrite method that first translates each term into should clause in boolean query, and keeps the scores /// as computed by the query. This rewrite method only uses the top scoring terms so it will not overflow boolean /// max clause count. /// /// controls the size of the top scoring terms to use. /// public static MultiTermQueryRewrite TopTerms(int size) => new MultiTermQueryRewrite(RewriteMultiTerm.TopTermsN, size); /// /// A rewrite method that first translates each term into should clause in boolean query, but the scores are only /// computed as the boost. This rewrite method only uses the top scoring terms so it will not overflow the boolean /// max clause count. /// /// controls the size of the top scoring terms to use. /// public static MultiTermQueryRewrite TopTermsBoost(int size) => new MultiTermQueryRewrite(RewriteMultiTerm.TopTermsBoostN, size); /// /// A rewrite method that first translates each term into should clause in boolean query, but all term queries compute /// scores as if they had the same frequency. In practice the frequency which is used is the maximum frequency of all /// matching terms. This rewrite method only uses the top scoring terms so it will not overflow boolean max clause count. /// /// controls the size of the top scoring terms to use. /// public static MultiTermQueryRewrite TopTermsBlendedFreqs(int size) => new MultiTermQueryRewrite(RewriteMultiTerm.TopTermsBlendedFreqsN, size); internal static MultiTermQueryRewrite Create(string value) { if (value == null) throw new ArgumentNullException(nameof(value)); var rewriteType = value; var size = 0; var firstDigitIndex = value.IndexOfAny(DigitCharacters); if (firstDigitIndex > -1) { rewriteType = $"{value.Substring(0, firstDigitIndex)}N"; size = int.Parse(value.Substring(firstDigitIndex)); } var rewriteMultiTerm = rewriteType.ToEnum(); if (rewriteMultiTerm == null) throw new InvalidOperationException($"Unsupported {nameof(RewriteMultiTerm)} value: '{rewriteType}'"); switch (rewriteMultiTerm) { case RewriteMultiTerm.ConstantScore: return ConstantScore; case RewriteMultiTerm.ScoringBoolean: return ScoringBoolean; case RewriteMultiTerm.ConstantScoreBoolean: return ConstantScoreBoolean; case RewriteMultiTerm.TopTermsN: return TopTerms(size); case RewriteMultiTerm.TopTermsBoostN: return TopTermsBoost(size); case RewriteMultiTerm.TopTermsBlendedFreqsN: return TopTermsBlendedFreqs(size); default: throw new InvalidOperationException($"Unsupported {nameof(RewriteMultiTerm)} value: '{rewriteMultiTerm}'"); } } public override string ToString() => _value; public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; var value = obj as string; if (value != null) return string.Equals(value, _value); return obj.GetType() == GetType() && Equals((MultiTermQueryRewrite)obj); } public override int GetHashCode() { unchecked { return ((int)Rewrite * 397) ^ Size.GetHashCode(); } } public static bool operator ==(MultiTermQueryRewrite left, MultiTermQueryRewrite right) => Equals(left, right); public static bool operator !=(MultiTermQueryRewrite left, MultiTermQueryRewrite right) => !Equals(left, right); } }