Skip to content

Commit

Permalink
Long range query support (#3299)
Browse files Browse the repository at this point in the history
add LongRangeQuery to handle range queries on long numeric field types.
  • Loading branch information
annashlyak authored and russcam committed Sep 6, 2018
1 parent 1230f1c commit 69733ea
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ public QueryContainer Conditionless(Func<ConditionlessQueryDescriptor<T>, ICondi
public QueryContainer Range(Func<NumericRangeQueryDescriptor<T>, INumericRangeQuery> selector) =>
WrapInContainer(selector, (query, container) => container.Range = query);

public QueryContainer LongRange(Func<LongRangeQueryDescriptor<T>, ILongRangeQuery> selector) =>
WrapInContainer(selector, (query, container) => container.Range = query);

/// <summary>
/// Matches documents with fields that have terms within a certain date range.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions src/Nest/QueryDsl/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ public static QueryContainer QueryString(Func<QueryStringQueryDescriptor<T>, IQu
public static QueryContainer Range(Func<NumericRangeQueryDescriptor<T>, INumericRangeQuery> selector) =>
new QueryContainerDescriptor<T>().Range(selector);

public static QueryContainer LongRange(Func<LongRangeQueryDescriptor<T>, ILongRangeQuery> selector) =>
new QueryContainerDescriptor<T>().LongRange(selector);

public static QueryContainer Regexp(Func<RegexpQueryDescriptor<T>, IRegexpQuery> selector) =>
new QueryContainerDescriptor<T>().Regexp(selector);

Expand Down
67 changes: 67 additions & 0 deletions src/Nest/QueryDsl/TermLevel/Range/LongRangeQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Newtonsoft.Json;

namespace Nest
{
public interface ILongRangeQuery : IRangeQuery
{
[JsonProperty("gte")]
long? GreaterThanOrEqualTo { get; set; }

[JsonProperty("lte")]
long? LessThanOrEqualTo { get; set; }

[JsonProperty("gt")]
long? GreaterThan { get; set; }

[JsonProperty("lt")]
long? LessThan { get; set; }

[JsonProperty("relation")]
RangeRelation? Relation { get; set; }
}

public class LongRangeQuery : FieldNameQueryBase, ILongRangeQuery
{
protected override bool Conditionless => IsConditionless(this);
public long? GreaterThanOrEqualTo { get; set; }
public long? LessThanOrEqualTo { get; set; }
public long? GreaterThan { get; set; }
public long? LessThan { get; set; }

public RangeRelation? Relation { get; set; }

internal override void InternalWrapInContainer(IQueryContainer c) => c.Range = this;

internal static bool IsConditionless(ILongRangeQuery q)
{
return q.Field.IsConditionless()
|| (q.GreaterThanOrEqualTo == null
&& q.LessThanOrEqualTo == null
&& q.GreaterThan == null
&& q.LessThan == null);
}
}

[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class LongRangeQueryDescriptor<T>
: FieldNameQueryDescriptorBase<LongRangeQueryDescriptor<T>, ILongRangeQuery, T>
, ILongRangeQuery where T : class
{
protected override bool Conditionless => LongRangeQuery.IsConditionless(this);
long? ILongRangeQuery.GreaterThanOrEqualTo { get; set; }
long? ILongRangeQuery.LessThanOrEqualTo { get; set; }
long? ILongRangeQuery.GreaterThan { get; set; }
long? ILongRangeQuery.LessThan { get; set; }
RangeRelation? ILongRangeQuery.Relation { get; set; }

public LongRangeQueryDescriptor<T> Relation(RangeRelation? relation) => Assign(a => a.Relation = relation);

public LongRangeQueryDescriptor<T> GreaterThan(long? from) => Assign(a => a.GreaterThan = from);

public LongRangeQueryDescriptor<T> GreaterThanOrEquals(long? from) => Assign(a => a.GreaterThanOrEqualTo = from);

public LongRangeQueryDescriptor<T> LessThan(long? to) => Assign(a => a.LessThan = to);

public LongRangeQueryDescriptor<T> LessThanOrEquals(long? to) => Assign(a => a.LessThanOrEqualTo = to);
}
}
1 change: 1 addition & 0 deletions src/Nest/QueryDsl/TermLevel/Range/NumericRangeQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public class NumericRangeQueryDescriptor<T>
double? INumericRangeQuery.LessThanOrEqualTo { get; set; }
double? INumericRangeQuery.GreaterThan { get; set; }
double? INumericRangeQuery.LessThan { get; set; }

RangeRelation? INumericRangeQuery.Relation{ get; set; }

public NumericRangeQueryDescriptor<T> GreaterThan(double? from) => Assign(a => a.GreaterThan = from);
Expand Down
40 changes: 26 additions & 14 deletions src/Nest/QueryDsl/TermLevel/Range/RangeQueryJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
var jo = firstProp.Value.Value<JObject>();
if (jo == null) return null;


var isNumeric = !jo.Properties().Any(p=>p.Name == "format" || p.Name == "time_zone")
&& jo.Properties().Any(p=> _rangeKeys.Contains(p.Name) && (p.Value.Type == JTokenType.Integer || p.Value.Type == JTokenType.Float));


IRangeQuery fq;
if (isNumeric)
{
fq = FromJson.ReadAs<NumericRangeQuery>(jo.CreateReader(), serializer);
}
else
{
fq = FromJson.ReadAs<DateRangeQuery>(jo.CreateReader(), serializer);
}
var fq = GetRangeQuery(serializer, jo);

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

private static IRangeQuery GetRangeQuery(JsonSerializer serializer, JObject jo)
{
var isNumeric = false;
var isLong = false;

foreach (var property in jo.Properties())
{
if (property.Name == "format" || property.Name == "time_zone")
return FromJson.ReadAs<DateRangeQuery>(jo.CreateReader(), serializer);
if (_rangeKeys.Contains(property.Name))
{
if (property.Value.Type == JTokenType.Float)
isNumeric = true;
else if (property.Value.Type == JTokenType.Integer)
isLong = true;
}
}
if (isNumeric)
return FromJson.ReadAs<NumericRangeQuery>(jo.CreateReader(), serializer);
if (isLong)
return FromJson.ReadAs<LongRangeQuery>(jo.CreateReader(), serializer);

return FromJson.ReadAs<DateRangeQuery>(jo.CreateReader(), serializer);
}

private static TReturn GetPropValue<TReturn>(JObject jObject, string field)
{
JToken jToken = null;
Expand Down
4 changes: 3 additions & 1 deletion src/Nest/QueryDsl/Visitor/DslPrettyPrintVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ private void Write(string queryType, Field field = null)

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

public virtual void Visit(ITermRangeQuery query) => Write("term_range");
public virtual void Visit(ILongRangeQuery query) => Write("long_range");

public virtual void Visit(ITermRangeQuery query) => Write("term_range");

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

Expand Down
5 changes: 4 additions & 1 deletion src/Nest/QueryDsl/Visitor/QueryVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public interface IQueryVisitor
void Visit(IExistsQuery query);
void Visit(IDateRangeQuery query);
void Visit(INumericRangeQuery query);
void Visit(ILongRangeQuery query);
void Visit(ITermRangeQuery query);
void Visit(ISpanFirstQuery query);
void Visit(ISpanNearQuery query);
Expand Down Expand Up @@ -123,7 +124,9 @@ public virtual void Visit(IDateRangeQuery query) { }

public virtual void Visit(INumericRangeQuery query) { }

public virtual void Visit(ITermRangeQuery query) { }
public virtual void Visit(ILongRangeQuery query) { }

public virtual void Visit(ITermRangeQuery query) { }

public virtual void Visit(IFunctionScoreQuery query) { }

Expand Down
1 change: 1 addition & 0 deletions src/Nest/QueryDsl/Visitor/QueryWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public void Walk(IQueryContainer qd, IQueryVisitor visitor)
v.Visit(d);
VisitQuery(d as IDateRangeQuery, visitor, (vv, dd) => v.Visit(dd));
VisitQuery(d as INumericRangeQuery, visitor, (vv, dd) => v.Visit(dd));
VisitQuery(d as ILongRangeQuery, visitor, (vv, dd) => v.Visit(dd));
VisitQuery(d as ITermRangeQuery, visitor, (vv, dd) => v.Visit(dd));
});
VisitQuery(qd.GeoShape, visitor, (v, d) =>
Expand Down
65 changes: 65 additions & 0 deletions src/Tests/QueryDsl/TermLevel/Range/LongRangeQueryUsageTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Nest;
using Tests.Framework.Integration;
using Tests.Framework.ManagedElasticsearch.Clusters;
using Tests.Framework.MockData;

namespace Tests.QueryDsl.TermLevel.Range
{
public class LongRangeQueryUsageTests : QueryDslUsageTestsBase
{
public LongRangeQueryUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) {}

protected override object QueryJson => new
{
range = new
{
description = new
{
_name = "named_query",
boost = 1.1,
gt = 636634079999999999,
gte = 636634080000000000,
lt = 636634080000000000,
lte = 636634079999999999,
relation = "within"
}
}
};

protected override QueryContainer QueryInitializer => new LongRangeQuery
{
Name = "named_query",
Boost = 1.1,
Field = "description",
GreaterThan = 636634079999999999,
GreaterThanOrEqualTo = 636634080000000000,
LessThan = 636634080000000000,
LessThanOrEqualTo = 636634079999999999,
Relation = RangeRelation.Within
};

protected override QueryContainer QueryFluent(QueryContainerDescriptor<Project> q) => q
.LongRange(c => c
.Name("named_query")
.Boost(1.1)
.Field(p => p.Description)
.GreaterThan(636634079999999999)
.GreaterThanOrEquals(636634080000000000)
.LessThan(636634080000000000)
.LessThanOrEquals(636634079999999999)
.Relation(RangeRelation.Within)
);

protected override ConditionlessWhen ConditionlessWhen => new ConditionlessWhen<ILongRangeQuery>(q => q.Range as ILongRangeQuery)
{
q=> q.Field = null,
q=>
{
q.GreaterThan = null;
q.GreaterThanOrEqualTo = null;
q.LessThan = null;
q.LessThanOrEqualTo = null;
}
};
}
}

0 comments on commit 69733ea

Please sign in to comment.