Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions src/Nest/QueryDsl/FullText/Intervals/IntervalsFuzzy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
using System;
using System.Linq.Expressions;
using System.Runtime.Serialization;

namespace Nest
{
/// <summary>
/// The fuzzy rule matches terms that are similar to the provided term, within an edit distance defined by Fuzziness.
/// If the fuzzy expansion matches more than 128 terms, Elasticsearch returns an error.
/// <para />
/// Available in Elasticsearch 7.6.0+
/// </summary>
[ReadAs(typeof(IntervalsFuzzy))]
public interface IIntervalsFuzzy : IIntervalsNoFilter
{
/// <summary>
/// Analyzer used to normalize the term. Defaults to the top-level field's analyzer.
/// </summary>
[DataMember(Name = "analyzer")]
string Analyzer { get; set; }

/// <summary>
/// Number of beginning characters left unchanged when creating expansions. Defaults to <c>0</c>.
/// </summary>
[DataMember(Name = "prefix_length")]
int? PrefixLength { get; set; }

/// <summary>
/// Indicates whether edits include transpositions of two adjacent characters (ab → ba). Defaults to <c>true</c>.
/// </summary>
[DataMember(Name = "transpositions")]
bool? Transpositions { get; set; }

/// <summary>
/// Maximum edit distance allowed for matching. See Fuzziness for valid values and more information.
/// Defaults to <see cref="Nest.Fuzziness.Auto"/>.
/// </summary>
[DataMember(Name = "fuzziness")]
Fuzziness Fuzziness { get; set; }

/// <summary>
/// The term to match.
/// </summary>
[DataMember(Name = "term")]
string Term { get; set; }

/// <summary>
/// If specified, then match intervals from this field rather than the top-level field.
/// The term is normalized using the search analyzer from this field,
/// unless analyzer is specified separately.
/// </summary>
[DataMember(Name = "use_field")]
Field UseField { get; set; }
}

/// <inheritdoc cref="IIntervalsFuzzy" />
public class IntervalsFuzzy : IntervalsNoFilterBase, IIntervalsFuzzy
{
internal override void WrapInContainer(IIntervalsContainer container) => container.Fuzzy = this;

/// <inheritdoc />
public string Analyzer { get; set; }
/// <inheritdoc />
public int? PrefixLength { get; set; }
/// <inheritdoc />
public bool? Transpositions { get; set; }
/// <inheritdoc />
public Fuzziness Fuzziness { get; set; }
/// <inheritdoc />
public string Term { get; set; }
/// <inheritdoc />
public Field UseField { get; set; }
}

/// <inheritdoc cref="IIntervalsFuzzy" />
public class IntervalsFuzzyDescriptor : DescriptorBase<IntervalsFuzzyDescriptor, IIntervalsFuzzy>, IIntervalsFuzzy
{
string IIntervalsFuzzy.Analyzer { get; set; }
int? IIntervalsFuzzy.PrefixLength { get; set; }
bool? IIntervalsFuzzy.Transpositions { get; set; }
Fuzziness IIntervalsFuzzy.Fuzziness { get; set; }
string IIntervalsFuzzy.Term { get; set; }
Field IIntervalsFuzzy.UseField { get; set; }

/// <inheritdoc cref="IIntervalsFuzzy.Analyzer" />
public IntervalsFuzzyDescriptor Analyzer(string analyzer) => Assign(analyzer, (a, v) => a.Analyzer = v);

/// <inheritdoc cref="IIntervalsFuzzy.PrefixLength" />
public IntervalsFuzzyDescriptor PrefixLength(int? prefixLength) => Assign(prefixLength, (a, v) => a.PrefixLength = v);

/// <inheritdoc cref="IIntervalsFuzzy.Transpositions" />
public IntervalsFuzzyDescriptor Transpositions(bool? transpositions = true) => Assign(transpositions, (a, v) => a.Transpositions = v);

/// <inheritdoc cref="IIntervalsFuzzy.Fuzziness" />
public IntervalsFuzzyDescriptor Fuzziness(Fuzziness fuzziness) => Assign(fuzziness, (a, v) => a.Fuzziness = v);

/// <inheritdoc cref="IIntervalsFuzzy.Term" />
public IntervalsFuzzyDescriptor Term(string term) => Assign(term, (a, v) => a.Term = v);


/// <inheritdoc cref="IIntervalsFuzzy.UseField" />
public IntervalsFuzzyDescriptor UseField<T>(Expression<Func<T, object>> objectPath) => Assign(objectPath, (a, v) => a.UseField = v);

/// <inheritdoc cref="IIntervalsFuzzy.UseField" />
public IntervalsFuzzyDescriptor UseField(Field useField) => Assign(useField, (a, v) => a.UseField = v);
}
}
23 changes: 22 additions & 1 deletion src/Nest/QueryDsl/FullText/Intervals/IntervalsQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class IntervalsQuery : FieldNameQueryBase, IIntervalsQuery
public IIntervalsAnyOf AnyOf { get; set; }
/// <inheritdoc cref="IIntervalsMatch"/>
public IIntervalsMatch Match { get; set; }
/// <inheritdoc cref="IIntervalsFuzzy"/>
public IIntervalsFuzzy Fuzzy { get; set; }

/// <inheritdoc cref="IIntervalsPrefix"/>
public IIntervalsPrefix Prefix { get; set; }
Expand All @@ -35,7 +37,8 @@ public class IntervalsQuery : FieldNameQueryBase, IIntervalsQuery
protected override bool Conditionless => IsConditionless(this);

internal static bool IsConditionless(IIntervalsQuery q) =>
q.Field.IsConditionless() || q.Match == null && q.AllOf == null && q.AnyOf == null && q.Prefix == null && q.Wildcard == null;
q.Field.IsConditionless() || q.Match == null && q.AllOf == null && q.AnyOf == null && q.Prefix == null && q.Wildcard == null
&& q.Fuzzy == null;

internal override void InternalWrapInContainer(IQueryContainer container) => container.Intervals = this;
}
Expand All @@ -50,10 +53,15 @@ public class IntervalsQueryDescriptor<T>

IIntervalsAllOf IIntervalsContainer.AllOf { get; set; }
IIntervalsAnyOf IIntervalsContainer.AnyOf { get; set; }
IIntervalsFuzzy IIntervalsContainer.Fuzzy { get; set; }
IIntervalsMatch IIntervalsContainer.Match { get; set; }
IIntervalsPrefix IIntervalsContainer.Prefix { get; set; }
IIntervalsWildcard IIntervalsContainer.Wildcard { get; set; }

/// <inheritdoc cref="IntervalsQuery.Fuzzy" />
public IntervalsQueryDescriptor<T> Fuzzy(Func<IntervalsFuzzyDescriptor, IIntervalsFuzzy> selector) =>
Assign(selector, (a, v) => a.Fuzzy = v?.Invoke(new IntervalsFuzzyDescriptor()));

/// <inheritdoc cref="IntervalsQuery.Match" />
public IntervalsQueryDescriptor<T> Match(Func<IntervalsMatchDescriptor, IIntervalsMatch> selector) =>
Assign(selector, (a, v) => a.Match = v?.Invoke(new IntervalsMatchDescriptor()));
Expand Down Expand Up @@ -88,6 +96,10 @@ public interface IIntervalsContainer
[DataMember(Name = "any_of")]
IIntervalsAnyOf AnyOf { get; set; }

/// <inheritdoc cref="IIntervalsFuzzy" />
[DataMember(Name = "fuzzy")]
IIntervalsFuzzy Fuzzy { get; set; }

/// <inheritdoc cref="IIntervalsMatch" />
[DataMember(Name = "match")]
IIntervalsMatch Match { get; set; }
Expand Down Expand Up @@ -120,6 +132,7 @@ public IntervalsContainer(IntervalsNoFilterBase intervals)

IIntervalsAllOf IIntervalsContainer.AllOf { get; set; }
IIntervalsAnyOf IIntervalsContainer.AnyOf { get; set; }
IIntervalsFuzzy IIntervalsContainer.Fuzzy { get; set; }
IIntervalsMatch IIntervalsContainer.Match { get; set; }
IIntervalsPrefix IIntervalsContainer.Prefix { get; set; }
IIntervalsWildcard IIntervalsContainer.Wildcard { get; set; }
Expand All @@ -141,6 +154,10 @@ public class IntervalsDescriptor : IntervalsContainer
private IntervalsDescriptor Assign<TValue>(TValue value, Action<IIntervalsContainer, TValue> assigner) =>
Fluent.Assign(this, value, assigner);

/// <inheritdoc cref="IntervalsFuzzyDescriptor" />
public IntervalsDescriptor Fuzzy(Func<IntervalsFuzzyDescriptor, IIntervalsFuzzy> selector) =>
Assign(selector, (a, v) => a.Fuzzy = v?.Invoke(new IntervalsFuzzyDescriptor()));

/// <inheritdoc cref="IntervalsMatchDescriptor" />
public IntervalsDescriptor Match(Func<IntervalsMatchDescriptor, IIntervalsMatch> selector) =>
Assign(selector, (a, v) => a.Match = v?.Invoke(new IntervalsMatchDescriptor()));
Expand Down Expand Up @@ -222,6 +239,10 @@ public class IntervalsListDescriptor : DescriptorPromiseBase<IntervalsListDescri
{
public IntervalsListDescriptor() : base(new List<IntervalsContainer>()) { }

/// <inheritdoc cref="IIntervalsFuzzy" />
public IntervalsListDescriptor Fuzzy(Func<IntervalsFuzzyDescriptor, IIntervalsFuzzy> selector) =>
Assign(selector, (a, v) => a.AddIfNotNull(new IntervalsDescriptor().Fuzzy(v)));

/// <inheritdoc cref="IIntervalsMatch" />
public IntervalsListDescriptor Match(Func<IntervalsMatchDescriptor, IIntervalsMatch> selector) =>
Assign(selector, (a, v) => a.AddIfNotNull(new IntervalsDescriptor().Match(v)));
Expand Down
58 changes: 58 additions & 0 deletions tests/Tests/QueryDsl/FullText/Intervals/IntervalsUsageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,62 @@ protected override QueryContainer QueryFluent(QueryContainerDescriptor<Project>
)
);
}

/**[float]
* === Fuzzy rules
*
* Fuzzy rules can be used to match terms that are similar to the provided term, within an edit distance defined by Fuzziness.
* If the fuzzy expansion matches more than 128 terms, Elasticsearch returns an error.
*
* NOTE: Only available in Elasticsearch 7.6.0+
*/
[SkipVersion("<7.6.0", "fuzzy rules introduced in 7.6.0")]
public class IntervalsFuzzyUsageTests : QueryDslUsageTestsBase
{
public IntervalsFuzzyUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }

private static readonly string IntervalsPrefix = Project.First.Description.Split(' ')[0];

private static readonly string IntervalsFuzzy = IntervalsPrefix.Substring(0, IntervalsPrefix.Length) + "z";

protected override QueryContainer QueryInitializer => new IntervalsQuery
{
Field = Field<Project>(p => p.Description),
Name = "named_query",
Boost = 1.1,
Fuzzy = new IntervalsFuzzy
{
Term = IntervalsFuzzy,
Fuzziness = Fuzziness.Auto
}
};

protected override object QueryJson => new
{
intervals = new
{
description = new
{
_name = "named_query",
boost = 1.1,
fuzzy = new
{
term = IntervalsFuzzy,
fuzziness = "AUTO"
}
}
}
};

protected override QueryContainer QueryFluent(QueryContainerDescriptor<Project> q) => q
.Intervals(c => c
.Field(p => p.Description)
.Name("named_query")
.Boost(1.1)
.Fuzzy(m => m
.Term(IntervalsFuzzy)
.Fuzziness(Fuzziness.Auto)
)
);
}
}