Skip to content

Commit

Permalink
Add support for fuzzy intervals in interval query
Browse files Browse the repository at this point in the history
Relates: #4341

This commit adds support for fuzzy intervals source in
Intervals query
  • Loading branch information
russcam committed Feb 10, 2020
1 parent 6b3f816 commit 69a7a0e
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 1 deletion.
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)
)
);
}
}

0 comments on commit 69a7a0e

Please sign in to comment.