Skip to content

Commit 69733ea

Browse files
annashlyakrusscam
authored andcommitted
Long range query support (#3299)
add LongRangeQuery to handle range queries on long numeric field types.
1 parent 1230f1c commit 69733ea

File tree

9 files changed

+173
-16
lines changed

9 files changed

+173
-16
lines changed

src/Nest/QueryDsl/Abstractions/Container/QueryContainerDescriptor.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ public QueryContainer Conditionless(Func<ConditionlessQueryDescriptor<T>, ICondi
138138
public QueryContainer Range(Func<NumericRangeQueryDescriptor<T>, INumericRangeQuery> selector) =>
139139
WrapInContainer(selector, (query, container) => container.Range = query);
140140

141+
public QueryContainer LongRange(Func<LongRangeQueryDescriptor<T>, ILongRangeQuery> selector) =>
142+
WrapInContainer(selector, (query, container) => container.Range = query);
143+
141144
/// <summary>
142145
/// Matches documents with fields that have terms within a certain date range.
143146
/// </summary>

src/Nest/QueryDsl/Query.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ public static QueryContainer QueryString(Func<QueryStringQueryDescriptor<T>, IQu
125125
public static QueryContainer Range(Func<NumericRangeQueryDescriptor<T>, INumericRangeQuery> selector) =>
126126
new QueryContainerDescriptor<T>().Range(selector);
127127

128+
public static QueryContainer LongRange(Func<LongRangeQueryDescriptor<T>, ILongRangeQuery> selector) =>
129+
new QueryContainerDescriptor<T>().LongRange(selector);
130+
128131
public static QueryContainer Regexp(Func<RegexpQueryDescriptor<T>, IRegexpQuery> selector) =>
129132
new QueryContainerDescriptor<T>().Regexp(selector);
130133

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using Newtonsoft.Json;
2+
3+
namespace Nest
4+
{
5+
public interface ILongRangeQuery : IRangeQuery
6+
{
7+
[JsonProperty("gte")]
8+
long? GreaterThanOrEqualTo { get; set; }
9+
10+
[JsonProperty("lte")]
11+
long? LessThanOrEqualTo { get; set; }
12+
13+
[JsonProperty("gt")]
14+
long? GreaterThan { get; set; }
15+
16+
[JsonProperty("lt")]
17+
long? LessThan { get; set; }
18+
19+
[JsonProperty("relation")]
20+
RangeRelation? Relation { get; set; }
21+
}
22+
23+
public class LongRangeQuery : FieldNameQueryBase, ILongRangeQuery
24+
{
25+
protected override bool Conditionless => IsConditionless(this);
26+
public long? GreaterThanOrEqualTo { get; set; }
27+
public long? LessThanOrEqualTo { get; set; }
28+
public long? GreaterThan { get; set; }
29+
public long? LessThan { get; set; }
30+
31+
public RangeRelation? Relation { get; set; }
32+
33+
internal override void InternalWrapInContainer(IQueryContainer c) => c.Range = this;
34+
35+
internal static bool IsConditionless(ILongRangeQuery q)
36+
{
37+
return q.Field.IsConditionless()
38+
|| (q.GreaterThanOrEqualTo == null
39+
&& q.LessThanOrEqualTo == null
40+
&& q.GreaterThan == null
41+
&& q.LessThan == null);
42+
}
43+
}
44+
45+
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
46+
public class LongRangeQueryDescriptor<T>
47+
: FieldNameQueryDescriptorBase<LongRangeQueryDescriptor<T>, ILongRangeQuery, T>
48+
, ILongRangeQuery where T : class
49+
{
50+
protected override bool Conditionless => LongRangeQuery.IsConditionless(this);
51+
long? ILongRangeQuery.GreaterThanOrEqualTo { get; set; }
52+
long? ILongRangeQuery.LessThanOrEqualTo { get; set; }
53+
long? ILongRangeQuery.GreaterThan { get; set; }
54+
long? ILongRangeQuery.LessThan { get; set; }
55+
RangeRelation? ILongRangeQuery.Relation { get; set; }
56+
57+
public LongRangeQueryDescriptor<T> Relation(RangeRelation? relation) => Assign(a => a.Relation = relation);
58+
59+
public LongRangeQueryDescriptor<T> GreaterThan(long? from) => Assign(a => a.GreaterThan = from);
60+
61+
public LongRangeQueryDescriptor<T> GreaterThanOrEquals(long? from) => Assign(a => a.GreaterThanOrEqualTo = from);
62+
63+
public LongRangeQueryDescriptor<T> LessThan(long? to) => Assign(a => a.LessThan = to);
64+
65+
public LongRangeQueryDescriptor<T> LessThanOrEquals(long? to) => Assign(a => a.LessThanOrEqualTo = to);
66+
}
67+
}

src/Nest/QueryDsl/TermLevel/Range/NumericRangeQuery.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public class NumericRangeQueryDescriptor<T>
5252
double? INumericRangeQuery.LessThanOrEqualTo { get; set; }
5353
double? INumericRangeQuery.GreaterThan { get; set; }
5454
double? INumericRangeQuery.LessThan { get; set; }
55+
5556
RangeRelation? INumericRangeQuery.Relation{ get; set; }
5657

5758
public NumericRangeQueryDescriptor<T> GreaterThan(double? from) => Assign(a => a.GreaterThan = from);

src/Nest/QueryDsl/TermLevel/Range/RangeQueryJsonConverter.cs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
2626
var jo = firstProp.Value.Value<JObject>();
2727
if (jo == null) return null;
2828

29-
30-
var isNumeric = !jo.Properties().Any(p=>p.Name == "format" || p.Name == "time_zone")
31-
&& jo.Properties().Any(p=> _rangeKeys.Contains(p.Name) && (p.Value.Type == JTokenType.Integer || p.Value.Type == JTokenType.Float));
32-
33-
34-
IRangeQuery fq;
35-
if (isNumeric)
36-
{
37-
fq = FromJson.ReadAs<NumericRangeQuery>(jo.CreateReader(), serializer);
38-
}
39-
else
40-
{
41-
fq = FromJson.ReadAs<DateRangeQuery>(jo.CreateReader(), serializer);
42-
}
29+
var fq = GetRangeQuery(serializer, jo);
4330

4431
fq.Name = GetPropValue<string>(jo, "_name");
4532
fq.Boost = GetPropValue<double?>(jo, "boost");
@@ -48,6 +35,31 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
4835
return fq;
4936
}
5037

38+
private static IRangeQuery GetRangeQuery(JsonSerializer serializer, JObject jo)
39+
{
40+
var isNumeric = false;
41+
var isLong = false;
42+
43+
foreach (var property in jo.Properties())
44+
{
45+
if (property.Name == "format" || property.Name == "time_zone")
46+
return FromJson.ReadAs<DateRangeQuery>(jo.CreateReader(), serializer);
47+
if (_rangeKeys.Contains(property.Name))
48+
{
49+
if (property.Value.Type == JTokenType.Float)
50+
isNumeric = true;
51+
else if (property.Value.Type == JTokenType.Integer)
52+
isLong = true;
53+
}
54+
}
55+
if (isNumeric)
56+
return FromJson.ReadAs<NumericRangeQuery>(jo.CreateReader(), serializer);
57+
if (isLong)
58+
return FromJson.ReadAs<LongRangeQuery>(jo.CreateReader(), serializer);
59+
60+
return FromJson.ReadAs<DateRangeQuery>(jo.CreateReader(), serializer);
61+
}
62+
5163
private static TReturn GetPropValue<TReturn>(JObject jObject, string field)
5264
{
5365
JToken jToken = null;

src/Nest/QueryDsl/Visitor/DslPrettyPrintVisitor.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@ private void Write(string queryType, Field field = null)
7979

8080
public virtual void Visit(INumericRangeQuery query) => Write("numeric_range");
8181

82-
public virtual void Visit(ITermRangeQuery query) => Write("term_range");
82+
public virtual void Visit(ILongRangeQuery query) => Write("long_range");
83+
84+
public virtual void Visit(ITermRangeQuery query) => Write("term_range");
8385

8486
public virtual void Visit(IFunctionScoreQuery query) => Write("function_core");
8587

src/Nest/QueryDsl/Visitor/QueryVisitor.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public interface IQueryVisitor
6161
void Visit(IExistsQuery query);
6262
void Visit(IDateRangeQuery query);
6363
void Visit(INumericRangeQuery query);
64+
void Visit(ILongRangeQuery query);
6465
void Visit(ITermRangeQuery query);
6566
void Visit(ISpanFirstQuery query);
6667
void Visit(ISpanNearQuery query);
@@ -123,7 +124,9 @@ public virtual void Visit(IDateRangeQuery query) { }
123124

124125
public virtual void Visit(INumericRangeQuery query) { }
125126

126-
public virtual void Visit(ITermRangeQuery query) { }
127+
public virtual void Visit(ILongRangeQuery query) { }
128+
129+
public virtual void Visit(ITermRangeQuery query) { }
127130

128131
public virtual void Visit(IFunctionScoreQuery query) { }
129132

src/Nest/QueryDsl/Visitor/QueryWalker.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public void Walk(IQueryContainer qd, IQueryVisitor visitor)
2626
v.Visit(d);
2727
VisitQuery(d as IDateRangeQuery, visitor, (vv, dd) => v.Visit(dd));
2828
VisitQuery(d as INumericRangeQuery, visitor, (vv, dd) => v.Visit(dd));
29+
VisitQuery(d as ILongRangeQuery, visitor, (vv, dd) => v.Visit(dd));
2930
VisitQuery(d as ITermRangeQuery, visitor, (vv, dd) => v.Visit(dd));
3031
});
3132
VisitQuery(qd.GeoShape, visitor, (v, d) =>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using Nest;
2+
using Tests.Framework.Integration;
3+
using Tests.Framework.ManagedElasticsearch.Clusters;
4+
using Tests.Framework.MockData;
5+
6+
namespace Tests.QueryDsl.TermLevel.Range
7+
{
8+
public class LongRangeQueryUsageTests : QueryDslUsageTestsBase
9+
{
10+
public LongRangeQueryUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) {}
11+
12+
protected override object QueryJson => new
13+
{
14+
range = new
15+
{
16+
description = new
17+
{
18+
_name = "named_query",
19+
boost = 1.1,
20+
gt = 636634079999999999,
21+
gte = 636634080000000000,
22+
lt = 636634080000000000,
23+
lte = 636634079999999999,
24+
relation = "within"
25+
}
26+
}
27+
};
28+
29+
protected override QueryContainer QueryInitializer => new LongRangeQuery
30+
{
31+
Name = "named_query",
32+
Boost = 1.1,
33+
Field = "description",
34+
GreaterThan = 636634079999999999,
35+
GreaterThanOrEqualTo = 636634080000000000,
36+
LessThan = 636634080000000000,
37+
LessThanOrEqualTo = 636634079999999999,
38+
Relation = RangeRelation.Within
39+
};
40+
41+
protected override QueryContainer QueryFluent(QueryContainerDescriptor<Project> q) => q
42+
.LongRange(c => c
43+
.Name("named_query")
44+
.Boost(1.1)
45+
.Field(p => p.Description)
46+
.GreaterThan(636634079999999999)
47+
.GreaterThanOrEquals(636634080000000000)
48+
.LessThan(636634080000000000)
49+
.LessThanOrEquals(636634079999999999)
50+
.Relation(RangeRelation.Within)
51+
);
52+
53+
protected override ConditionlessWhen ConditionlessWhen => new ConditionlessWhen<ILongRangeQuery>(q => q.Range as ILongRangeQuery)
54+
{
55+
q=> q.Field = null,
56+
q=>
57+
{
58+
q.GreaterThan = null;
59+
q.GreaterThanOrEqualTo = null;
60+
q.LessThan = null;
61+
q.LessThanOrEqualTo = null;
62+
}
63+
};
64+
}
65+
}

0 commit comments

Comments
 (0)