Skip to content

Commit 0e9cf8f

Browse files
committed
Fix #3327 Add span_gap query for use with span_near query (#3361)
(cherry picked from commit d215520)
1 parent 9946165 commit 0e9cf8f

File tree

5 files changed

+129
-33
lines changed

5 files changed

+129
-33
lines changed

src/Nest/CommonAbstractions/SerializationBehavior/GenericJsonConverters/ReserializeJsonConverter.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
2626

2727
protected TReadAs ReadAs(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
2828
{
29-
3029
return this.Reader.ReadJson(reader, objectType, existingValue, serializer) as TReadAs;
3130
}
3231

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System;
2+
using System.Linq.Expressions;
3+
using Newtonsoft.Json;
4+
5+
namespace Nest
6+
{
7+
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
8+
[JsonConverter(typeof(SpanGapQueryJsonConverter))]
9+
public interface ISpanGapQuery : ISpanSubQuery
10+
{
11+
Field Field { get; set; }
12+
int? Width { get; set; }
13+
}
14+
15+
public class SpanGapQuery : QueryBase, ISpanGapQuery
16+
{
17+
protected override bool Conditionless => SpanGapQuery.IsConditionless(this);
18+
19+
internal static bool IsConditionless(ISpanGapQuery q) => q?.Width == null || q.Field.IsConditionless();
20+
21+
public Field Field { get; set; }
22+
public int? Width { get; set; }
23+
24+
internal override void InternalWrapInContainer(IQueryContainer c) =>
25+
throw new Exception("span_gap may only appear as a span near clause");
26+
27+
}
28+
29+
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
30+
public class SpanGapQueryDescriptor<T> : QueryDescriptorBase<SpanGapQueryDescriptor<T>, ISpanGapQuery>, ISpanGapQuery
31+
where T : class
32+
{
33+
protected override bool Conditionless => SpanGapQuery.IsConditionless(this);
34+
35+
Field ISpanGapQuery.Field { get; set; }
36+
37+
int? ISpanGapQuery.Width { get; set; }
38+
39+
public SpanGapQueryDescriptor<T> Field(Field field) => Assign(a => a.Field = field);
40+
41+
public SpanGapQueryDescriptor<T> Field(Expression<Func<T, object>> objectPath) => Assign(a => a.Field = objectPath);
42+
43+
public SpanGapQueryDescriptor<T> Width(int? width) => Assign(a => a.Width = width);
44+
}
45+
46+
internal class SpanGapQueryJsonConverter : JsonConverter
47+
{
48+
public override bool CanRead => true;
49+
public override bool CanWrite => true;
50+
public override bool CanConvert(Type objectType) => typeof(ISpanGapQuery).IsAssignableFrom(objectType);
51+
52+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
53+
{
54+
var gapQuery = value as ISpanGapQuery;
55+
if (value == null || SpanGapQuery.IsConditionless(gapQuery))
56+
{
57+
writer.WriteNull();
58+
return;
59+
}
60+
var settings = serializer.GetConnectionSettings();
61+
var fieldName = settings.Inferrer.Field(gapQuery.Field);
62+
writer.WriteStartObject();
63+
writer.WritePropertyName(fieldName);
64+
writer.WriteValue(gapQuery.Width);
65+
writer.WriteEndObject();
66+
}
67+
68+
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
69+
{
70+
if (reader.TokenType != JsonToken.StartObject) return null;
71+
reader.Read();
72+
var field = (Field)reader.Value.ToString(); // field
73+
var width = reader.ReadAsInt32();
74+
reader.Read();
75+
return new SpanGapQuery {Field = field, Width = width};
76+
}
77+
78+
}
79+
}

src/Nest/QueryDsl/Span/SpanQuery.cs

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,20 @@ public interface ISpanQuery : IQuery
1717
[JsonProperty("span_near")]
1818
ISpanNearQuery SpanNear { get; set; }
1919

20+
[JsonProperty("span_gap")]
21+
ISpanGapQuery SpanGap { get; set; }
22+
2023
[JsonProperty("span_or")]
2124
ISpanOrQuery SpanOr { get; set; }
2225

2326
[JsonProperty("span_not")]
2427
ISpanNotQuery SpanNot { get; set; }
25-
26-
[JsonProperty("span_containing")]
27-
ISpanContainingQuery SpanContaining { get; set; }
28-
29-
[JsonProperty("span_within")]
30-
ISpanWithinQuery SpanWithin { get; set; }
28+
29+
[JsonProperty("span_containing")]
30+
ISpanContainingQuery SpanContaining { get; set; }
31+
32+
[JsonProperty("span_within")]
33+
ISpanWithinQuery SpanWithin { get; set; }
3134

3235
[JsonProperty("span_multi")]
3336
ISpanMultiTermQuery SpanMultiTerm { get; set; }
@@ -50,14 +53,15 @@ public class SpanQuery : ISpanQuery
5053
public ISpanTermQuery SpanTerm { get; set; }
5154
public ISpanFirstQuery SpanFirst { get; set; }
5255
public ISpanNearQuery SpanNear { get; set; }
56+
public ISpanGapQuery SpanGap { get; set; }
5357
public ISpanOrQuery SpanOr { get; set; }
5458
public ISpanNotQuery SpanNot { get; set; }
55-
public ISpanMultiTermQuery SpanMultiTerm { get; set; }
56-
public ISpanContainingQuery SpanContaining{ get; set; }
57-
public ISpanWithinQuery SpanWithin { get; set; }
58-
public ISpanFieldMaskingQuery SpanFieldMasking { get; set; }
59-
60-
public void Accept(IQueryVisitor visitor) => new QueryWalker().Walk(this, visitor);
59+
public ISpanMultiTermQuery SpanMultiTerm { get; set; }
60+
public ISpanContainingQuery SpanContaining{ get; set; }
61+
public ISpanWithinQuery SpanWithin { get; set; }
62+
public ISpanFieldMaskingQuery SpanFieldMasking { get; set; }
63+
64+
public void Accept(IQueryVisitor visitor) => new QueryWalker().Walk(this, visitor);
6165

6266
internal static bool IsConditionless(ISpanQuery q) => new[]
6367
{
@@ -67,6 +71,7 @@ internal static bool IsConditionless(ISpanQuery q) => new[]
6771
q.SpanOr ,
6872
q.SpanNot,
6973
q.SpanMultiTerm,
74+
q.SpanGap,
7075
q.SpanFieldMasking
7176
}.All(sq => sq == null || sq.Conditionless);
7277
}
@@ -78,10 +83,11 @@ public class SpanQueryDescriptor<T> : QueryDescriptorBase<SpanQueryDescriptor<T>
7883
ISpanTermQuery ISpanQuery.SpanTerm { get; set; }
7984
ISpanFirstQuery ISpanQuery.SpanFirst { get; set; }
8085
ISpanNearQuery ISpanQuery.SpanNear { get; set; }
86+
ISpanGapQuery ISpanQuery.SpanGap { get; set; }
8187
ISpanOrQuery ISpanQuery.SpanOr { get; set; }
8288
ISpanNotQuery ISpanQuery.SpanNot { get; set; }
83-
ISpanMultiTermQuery ISpanQuery.SpanMultiTerm { get; set; }
84-
ISpanContainingQuery ISpanQuery.SpanContaining{ get; set; }
89+
ISpanMultiTermQuery ISpanQuery.SpanMultiTerm { get; set; }
90+
ISpanContainingQuery ISpanQuery.SpanContaining{ get; set; }
8591
ISpanWithinQuery ISpanQuery.SpanWithin { get; set; }
8692
ISpanFieldMaskingQuery ISpanQuery.SpanFieldMasking { get; set; }
8793

@@ -94,6 +100,9 @@ public SpanQueryDescriptor<T> SpanFirst(Func<SpanFirstQueryDescriptor<T>, ISpanF
94100
public SpanQueryDescriptor<T> SpanNear(Func<SpanNearQueryDescriptor<T>, ISpanNearQuery> selector) =>
95101
Assign(a => a.SpanNear = selector?.Invoke(new SpanNearQueryDescriptor<T>()));
96102

103+
public SpanQueryDescriptor<T> SpanGap(Func<SpanGapQueryDescriptor<T>, ISpanGapQuery> selector) =>
104+
Assign(a => a.SpanGap = selector?.Invoke(new SpanGapQueryDescriptor<T>()));
105+
97106
public SpanQueryDescriptor<T> SpanOr(Func<SpanOrQueryDescriptor<T>, ISpanOrQuery> selector) =>
98107
Assign(a => a.SpanOr = selector?.Invoke(new SpanOrQueryDescriptor<T>()));
99108

@@ -102,17 +111,17 @@ public SpanQueryDescriptor<T> SpanNot(Func<SpanNotQueryDescriptor<T>, ISpanNotQu
102111

103112
public SpanQueryDescriptor<T> SpanMultiTerm(Func<SpanMultiTermQueryDescriptor<T>, ISpanMultiTermQuery> selector) =>
104113
Assign(a => a.SpanMultiTerm = selector?.Invoke(new SpanMultiTermQueryDescriptor<T>()));
105-
106-
public SpanQueryDescriptor<T> SpanContaining(Func<SpanContainingQueryDescriptor<T>, ISpanContainingQuery> selector) =>
107-
Assign(a => a.SpanContaining = selector?.Invoke(new SpanContainingQueryDescriptor<T>()));
108-
109-
public SpanQueryDescriptor<T> SpanWithin(Func<SpanWithinQueryDescriptor<T>, ISpanWithinQuery> selector) =>
110-
Assign(a => a.SpanWithin = selector?.Invoke(new SpanWithinQueryDescriptor<T>()));
111-
112-
public SpanQueryDescriptor<T> SpanFieldMasking(Func<SpanFieldMaskingQueryDescriptor<T>, ISpanFieldMaskingQuery> selector) =>
113-
Assign(a => a.SpanFieldMasking = selector?.Invoke(new SpanFieldMaskingQueryDescriptor<T>()));
114-
115-
void ISpanQuery.Accept(IQueryVisitor visitor) => new QueryWalker().Walk(this, visitor);
114+
115+
public SpanQueryDescriptor<T> SpanContaining(Func<SpanContainingQueryDescriptor<T>, ISpanContainingQuery> selector) =>
116+
Assign(a => a.SpanContaining = selector?.Invoke(new SpanContainingQueryDescriptor<T>()));
117+
118+
public SpanQueryDescriptor<T> SpanWithin(Func<SpanWithinQueryDescriptor<T>, ISpanWithinQuery> selector) =>
119+
Assign(a => a.SpanWithin = selector?.Invoke(new SpanWithinQueryDescriptor<T>()));
120+
121+
public SpanQueryDescriptor<T> SpanFieldMasking(Func<SpanFieldMaskingQueryDescriptor<T>, ISpanFieldMaskingQuery> selector) =>
122+
Assign(a => a.SpanFieldMasking = selector?.Invoke(new SpanFieldMaskingQueryDescriptor<T>()));
123+
124+
void ISpanQuery.Accept(IQueryVisitor visitor) => new QueryWalker().Walk(this, visitor);
116125

117126
}
118127
}

src/Tests/Tests/CodeStandards/Queries.doc.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,13 @@ [U] public void FluentDescriptorExposesAll()
5151

5252
[U] public void VisitorVisitsAll()
5353
{
54-
var skipQueryImplementations = new[] { typeof(IFieldNameQuery), typeof(IFuzzyQuery<,>), typeof(IConditionlessQuery) };
54+
var skipQueryImplementations = new[]
55+
{
56+
typeof(IFieldNameQuery),
57+
typeof(IFuzzyQuery<,>),
58+
typeof(IConditionlessQuery),
59+
typeof(ISpanGapQuery)
60+
};
5561
var queries = typeof(IQuery).Assembly().ExportedTypes
5662
.Where(t => t.IsInterface() && typeof(IQuery).IsAssignableFrom(t))
5763
.Where(t => !skipQueryImplementations.Contains(t))

src/Tests/Tests/QueryDsl/Span/Near/SpanNearQueryUsageTests.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ public SpanNearUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usag
1818
{
1919
span_near = new
2020
{
21-
clauses = new[] {
21+
clauses = new object[] {
2222
new { span_term = new { field = new { value = "value1" } } },
2323
new { span_term = new { field = new { value = "value2" } } },
24-
new { span_term = new { field = new { value = "value3" } } }
24+
new { span_term = new { field = new { value = "value3" } } },
25+
new { span_gap = new { field = 2 } }
2526
},
2627
slop = 12,
2728
in_order = false,
@@ -39,7 +40,8 @@ public SpanNearUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usag
3940
{
4041
new SpanQuery { SpanTerm = new SpanTermQuery { Field = "field", Value = "value1" } },
4142
new SpanQuery { SpanTerm = new SpanTermQuery { Field = "field", Value = "value2" } },
42-
new SpanQuery { SpanTerm = new SpanTermQuery { Field = "field", Value = "value3" } }
43+
new SpanQuery { SpanTerm = new SpanTermQuery { Field = "field", Value = "value3" } },
44+
new SpanQuery { SpanGap = new SpanGapQuery { Field = "field", Width = 2 } }
4345
},
4446
Slop = 12,
4547
InOrder = false,
@@ -50,9 +52,10 @@ protected override QueryContainer QueryFluent(QueryContainerDescriptor<Project>
5052
.Name("named_query")
5153
.Boost(1.1)
5254
.Clauses(
53-
c=>c.SpanTerm(st=>st.Field("field").Value("value1")),
54-
c=>c.SpanTerm(st=>st.Field("field").Value("value2")),
55-
c=>c.SpanTerm(st=>st.Field("field").Value("value3"))
55+
c => c.SpanTerm(st => st.Field("field").Value("value1")),
56+
c => c.SpanTerm(st => st.Field("field").Value("value2")),
57+
c => c.SpanTerm(st => st.Field("field").Value("value3")),
58+
c => c.SpanGap(st => st.Field("field").Width(2))
5659
)
5760
.Slop(12)
5861
.InOrder(false)

0 commit comments

Comments
 (0)