diff --git a/src/Nest/CommonAbstractions/SerializationBehavior/GenericJsonConverters/ReserializeJsonConverter.cs b/src/Nest/CommonAbstractions/SerializationBehavior/GenericJsonConverters/ReserializeJsonConverter.cs index 499e7c9001d..12f0233a1f6 100644 --- a/src/Nest/CommonAbstractions/SerializationBehavior/GenericJsonConverters/ReserializeJsonConverter.cs +++ b/src/Nest/CommonAbstractions/SerializationBehavior/GenericJsonConverters/ReserializeJsonConverter.cs @@ -26,7 +26,6 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist protected TReadAs ReadAs(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - return this.Reader.ReadJson(reader, objectType, existingValue, serializer) as TReadAs; } diff --git a/src/Nest/QueryDsl/Span/Gap/SpanGapQuery.cs b/src/Nest/QueryDsl/Span/Gap/SpanGapQuery.cs new file mode 100644 index 00000000000..4d07033a059 --- /dev/null +++ b/src/Nest/QueryDsl/Span/Gap/SpanGapQuery.cs @@ -0,0 +1,79 @@ +using System; +using System.Linq.Expressions; +using Newtonsoft.Json; + +namespace Nest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [JsonConverter(typeof(SpanGapQueryJsonConverter))] + public interface ISpanGapQuery : ISpanSubQuery + { + Field Field { get; set; } + int? Width { get; set; } + } + + public class SpanGapQuery : QueryBase, ISpanGapQuery + { + protected override bool Conditionless => SpanGapQuery.IsConditionless(this); + + internal static bool IsConditionless(ISpanGapQuery q) => q?.Width == null || q.Field.IsConditionless(); + + public Field Field { get; set; } + public int? Width { get; set; } + + internal override void InternalWrapInContainer(IQueryContainer c) => + throw new Exception("span_gap may only appear as a span near clause"); + + } + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class SpanGapQueryDescriptor : QueryDescriptorBase, ISpanGapQuery>, ISpanGapQuery + where T : class + { + protected override bool Conditionless => SpanGapQuery.IsConditionless(this); + + Field ISpanGapQuery.Field { get; set; } + + int? ISpanGapQuery.Width { get; set; } + + public SpanGapQueryDescriptor Field(Field field) => Assign(a => a.Field = field); + + public SpanGapQueryDescriptor Field(Expression> objectPath) => Assign(a => a.Field = objectPath); + + public SpanGapQueryDescriptor Width(int? width) => Assign(a => a.Width = width); + } + + internal class SpanGapQueryJsonConverter : JsonConverter + { + public override bool CanRead => true; + public override bool CanWrite => true; + public override bool CanConvert(Type objectType) => typeof(ISpanGapQuery).IsAssignableFrom(objectType); + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var gapQuery = value as ISpanGapQuery; + if (value == null || SpanGapQuery.IsConditionless(gapQuery)) + { + writer.WriteNull(); + return; + } + var settings = serializer.GetConnectionSettings(); + var fieldName = settings.Inferrer.Field(gapQuery.Field); + writer.WriteStartObject(); + writer.WritePropertyName(fieldName); + writer.WriteValue(gapQuery.Width); + writer.WriteEndObject(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.StartObject) return null; + reader.Read(); + var field = (Field)reader.Value.ToString(); // field + var width = reader.ReadAsInt32(); + reader.Read(); + return new SpanGapQuery {Field = field, Width = width}; + } + + } +} diff --git a/src/Nest/QueryDsl/Span/SpanQuery.cs b/src/Nest/QueryDsl/Span/SpanQuery.cs index d690bd61f8a..c57eb276889 100644 --- a/src/Nest/QueryDsl/Span/SpanQuery.cs +++ b/src/Nest/QueryDsl/Span/SpanQuery.cs @@ -17,17 +17,20 @@ public interface ISpanQuery : IQuery [JsonProperty("span_near")] ISpanNearQuery SpanNear { get; set; } + [JsonProperty("span_gap")] + ISpanGapQuery SpanGap { get; set; } + [JsonProperty("span_or")] ISpanOrQuery SpanOr { get; set; } [JsonProperty("span_not")] ISpanNotQuery SpanNot { get; set; } - - [JsonProperty("span_containing")] - ISpanContainingQuery SpanContaining { get; set; } - - [JsonProperty("span_within")] - ISpanWithinQuery SpanWithin { get; set; } + + [JsonProperty("span_containing")] + ISpanContainingQuery SpanContaining { get; set; } + + [JsonProperty("span_within")] + ISpanWithinQuery SpanWithin { get; set; } [JsonProperty("span_multi")] ISpanMultiTermQuery SpanMultiTerm { get; set; } @@ -50,14 +53,15 @@ public class SpanQuery : ISpanQuery public ISpanTermQuery SpanTerm { get; set; } public ISpanFirstQuery SpanFirst { get; set; } public ISpanNearQuery SpanNear { get; set; } + public ISpanGapQuery SpanGap { get; set; } public ISpanOrQuery SpanOr { get; set; } public ISpanNotQuery SpanNot { get; set; } - public ISpanMultiTermQuery SpanMultiTerm { get; set; } - public ISpanContainingQuery SpanContaining{ get; set; } - public ISpanWithinQuery SpanWithin { get; set; } - public ISpanFieldMaskingQuery SpanFieldMasking { get; set; } - - public void Accept(IQueryVisitor visitor) => new QueryWalker().Walk(this, visitor); + public ISpanMultiTermQuery SpanMultiTerm { get; set; } + public ISpanContainingQuery SpanContaining{ get; set; } + public ISpanWithinQuery SpanWithin { get; set; } + public ISpanFieldMaskingQuery SpanFieldMasking { get; set; } + + public void Accept(IQueryVisitor visitor) => new QueryWalker().Walk(this, visitor); internal static bool IsConditionless(ISpanQuery q) => new[] { @@ -67,6 +71,7 @@ internal static bool IsConditionless(ISpanQuery q) => new[] q.SpanOr , q.SpanNot, q.SpanMultiTerm, + q.SpanGap, q.SpanFieldMasking }.All(sq => sq == null || sq.Conditionless); } @@ -78,10 +83,11 @@ public class SpanQueryDescriptor : QueryDescriptorBase ISpanTermQuery ISpanQuery.SpanTerm { get; set; } ISpanFirstQuery ISpanQuery.SpanFirst { get; set; } ISpanNearQuery ISpanQuery.SpanNear { get; set; } + ISpanGapQuery ISpanQuery.SpanGap { get; set; } ISpanOrQuery ISpanQuery.SpanOr { get; set; } ISpanNotQuery ISpanQuery.SpanNot { get; set; } - ISpanMultiTermQuery ISpanQuery.SpanMultiTerm { get; set; } - ISpanContainingQuery ISpanQuery.SpanContaining{ get; set; } + ISpanMultiTermQuery ISpanQuery.SpanMultiTerm { get; set; } + ISpanContainingQuery ISpanQuery.SpanContaining{ get; set; } ISpanWithinQuery ISpanQuery.SpanWithin { get; set; } ISpanFieldMaskingQuery ISpanQuery.SpanFieldMasking { get; set; } @@ -94,6 +100,9 @@ public SpanQueryDescriptor SpanFirst(Func, ISpanF public SpanQueryDescriptor SpanNear(Func, ISpanNearQuery> selector) => Assign(a => a.SpanNear = selector?.Invoke(new SpanNearQueryDescriptor())); + public SpanQueryDescriptor SpanGap(Func, ISpanGapQuery> selector) => + Assign(a => a.SpanGap = selector?.Invoke(new SpanGapQueryDescriptor())); + public SpanQueryDescriptor SpanOr(Func, ISpanOrQuery> selector) => Assign(a => a.SpanOr = selector?.Invoke(new SpanOrQueryDescriptor())); @@ -102,17 +111,17 @@ public SpanQueryDescriptor SpanNot(Func, ISpanNotQu public SpanQueryDescriptor SpanMultiTerm(Func, ISpanMultiTermQuery> selector) => Assign(a => a.SpanMultiTerm = selector?.Invoke(new SpanMultiTermQueryDescriptor())); - - public SpanQueryDescriptor SpanContaining(Func, ISpanContainingQuery> selector) => - Assign(a => a.SpanContaining = selector?.Invoke(new SpanContainingQueryDescriptor())); - - public SpanQueryDescriptor SpanWithin(Func, ISpanWithinQuery> selector) => - Assign(a => a.SpanWithin = selector?.Invoke(new SpanWithinQueryDescriptor())); - - public SpanQueryDescriptor SpanFieldMasking(Func, ISpanFieldMaskingQuery> selector) => - Assign(a => a.SpanFieldMasking = selector?.Invoke(new SpanFieldMaskingQueryDescriptor())); - - void ISpanQuery.Accept(IQueryVisitor visitor) => new QueryWalker().Walk(this, visitor); + + public SpanQueryDescriptor SpanContaining(Func, ISpanContainingQuery> selector) => + Assign(a => a.SpanContaining = selector?.Invoke(new SpanContainingQueryDescriptor())); + + public SpanQueryDescriptor SpanWithin(Func, ISpanWithinQuery> selector) => + Assign(a => a.SpanWithin = selector?.Invoke(new SpanWithinQueryDescriptor())); + + public SpanQueryDescriptor SpanFieldMasking(Func, ISpanFieldMaskingQuery> selector) => + Assign(a => a.SpanFieldMasking = selector?.Invoke(new SpanFieldMaskingQueryDescriptor())); + + void ISpanQuery.Accept(IQueryVisitor visitor) => new QueryWalker().Walk(this, visitor); } } diff --git a/src/Tests/Tests.Configuration/tests.default.yaml b/src/Tests/Tests.Configuration/tests.default.yaml index 246e803ded7..550f93f5743 100644 --- a/src/Tests/Tests.Configuration/tests.default.yaml +++ b/src/Tests/Tests.Configuration/tests.default.yaml @@ -5,7 +5,7 @@ # tracked by git). # mode either u (unit test), i (integration test) or m (mixed mode) -mode: i +mode: u # the elasticsearch version that should be started # Can be a snapshot version of sonatype or "latest" to get the latest snapshot of sonatype elasticsearch_version: 6.3.0 diff --git a/src/Tests/Tests/CodeStandards/Queries.doc.cs b/src/Tests/Tests/CodeStandards/Queries.doc.cs index 5d9fa31efbd..251da0a718f 100644 --- a/src/Tests/Tests/CodeStandards/Queries.doc.cs +++ b/src/Tests/Tests/CodeStandards/Queries.doc.cs @@ -51,7 +51,13 @@ [U] public void FluentDescriptorExposesAll() [U] public void VisitorVisitsAll() { - var skipQueryImplementations = new[] { typeof(IFieldNameQuery), typeof(IFuzzyQuery<,>), typeof(IConditionlessQuery) }; + var skipQueryImplementations = new[] + { + typeof(IFieldNameQuery), + typeof(IFuzzyQuery<,>), + typeof(IConditionlessQuery), + typeof(ISpanGapQuery) + }; var queries = typeof(IQuery).Assembly().ExportedTypes .Where(t => t.IsInterface() && typeof(IQuery).IsAssignableFrom(t)) .Where(t => !skipQueryImplementations.Contains(t)) diff --git a/src/Tests/Tests/QueryDsl/Span/Near/SpanNearQueryUsageTests.cs b/src/Tests/Tests/QueryDsl/Span/Near/SpanNearQueryUsageTests.cs index 7f849a146b3..73e9a32ccac 100644 --- a/src/Tests/Tests/QueryDsl/Span/Near/SpanNearQueryUsageTests.cs +++ b/src/Tests/Tests/QueryDsl/Span/Near/SpanNearQueryUsageTests.cs @@ -18,10 +18,11 @@ public SpanNearUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usag { span_near = new { - clauses = new[] { + clauses = new object[] { new { span_term = new { field = new { value = "value1" } } }, new { span_term = new { field = new { value = "value2" } } }, - new { span_term = new { field = new { value = "value3" } } } + new { span_term = new { field = new { value = "value3" } } }, + new { span_gap = new { field = 2 } } }, slop = 12, in_order = false, @@ -39,7 +40,8 @@ public SpanNearUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usag { new SpanQuery { SpanTerm = new SpanTermQuery { Field = "field", Value = "value1" } }, new SpanQuery { SpanTerm = new SpanTermQuery { Field = "field", Value = "value2" } }, - new SpanQuery { SpanTerm = new SpanTermQuery { Field = "field", Value = "value3" } } + new SpanQuery { SpanTerm = new SpanTermQuery { Field = "field", Value = "value3" } }, + new SpanQuery { SpanGap = new SpanGapQuery { Field = "field", Width = 2 } } }, Slop = 12, InOrder = false, @@ -50,9 +52,10 @@ protected override QueryContainer QueryFluent(QueryContainerDescriptor .Name("named_query") .Boost(1.1) .Clauses( - c=>c.SpanTerm(st=>st.Field("field").Value("value1")), - c=>c.SpanTerm(st=>st.Field("field").Value("value2")), - c=>c.SpanTerm(st=>st.Field("field").Value("value3")) + c => c.SpanTerm(st => st.Field("field").Value("value1")), + c => c.SpanTerm(st => st.Field("field").Value("value2")), + c => c.SpanTerm(st => st.Field("field").Value("value3")), + c => c.SpanGap(st => st.Field("field").Width(2)) ) .Slop(12) .InOrder(false)