diff --git a/src/Nest/CommonAbstractions/SerializationBehavior/ElasticContractResolver.cs b/src/Nest/CommonAbstractions/SerializationBehavior/ElasticContractResolver.cs index 01f0c7540e5..82c394cf64a 100644 --- a/src/Nest/CommonAbstractions/SerializationBehavior/ElasticContractResolver.cs +++ b/src/Nest/CommonAbstractions/SerializationBehavior/ElasticContractResolver.cs @@ -44,7 +44,7 @@ protected override JsonContract CreateContract(Type objectType) contract.Converter = new QueryContainerCollectionJsonConverter(); else if (objectType == typeof(ServerError)) contract.Converter = new ServerErrorJsonConverter(); - else if (objectType == typeof(DateTime) || + else if (objectType == typeof(DateTime) || objectType == typeof(DateTime?) || objectType == typeof(DateTimeOffset) || objectType == typeof(DateTimeOffset?)) diff --git a/src/Nest/Indices/Monitoring/IndicesShardStores/ShardStoreJsonConverter.cs b/src/Nest/Indices/Monitoring/IndicesShardStores/ShardStoreJsonConverter.cs index 6309547406d..035bc3f7c59 100644 --- a/src/Nest/Indices/Monitoring/IndicesShardStores/ShardStoreJsonConverter.cs +++ b/src/Nest/Indices/Monitoring/IndicesShardStores/ShardStoreJsonConverter.cs @@ -15,7 +15,7 @@ internal class ShardStoreJsonConverter : JsonConverter public override bool CanRead => true; public override bool CanWrite => false; - public override bool CanConvert(Type objectType) => objectType == typeof(IDictionary); + public override bool CanConvert(Type objectType) => true; public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { diff --git a/src/Nest/Nest.csproj b/src/Nest/Nest.csproj index cdcbf409780..c7ba0b5f176 100644 --- a/src/Nest/Nest.csproj +++ b/src/Nest/Nest.csproj @@ -1012,6 +1012,7 @@ + diff --git a/src/Nest/QueryDsl/Compound/FunctionScore/Functions/ScoreFunctionJsonConverter.cs b/src/Nest/QueryDsl/Compound/FunctionScore/Functions/ScoreFunctionJsonConverter.cs index 2b91ff7371b..4b1c3cdd972 100644 --- a/src/Nest/QueryDsl/Compound/FunctionScore/Functions/ScoreFunctionJsonConverter.cs +++ b/src/Nest/QueryDsl/Compound/FunctionScore/Functions/ScoreFunctionJsonConverter.cs @@ -49,11 +49,12 @@ private bool WriteScriptScore(JsonWriter writer, IScriptScoreFunction value, Jso { if (value == null) return false; writer.WritePropertyName("script_score"); - writer.WriteStartObject(); - { - writer.WriteProperty(serializer, "script", value.Script); - } - writer.WriteEndObject(); + serializer.Serialize(writer, value.Script); + //writer.WriteStartObject(); + //{ + // writer.WriteProperty(serializer, "script", value.Script); + //} + //writer.WriteEndObject(); return true; } @@ -235,4 +236,4 @@ private IDecayFunction ReadDecayFunction(string type, JObject o, JsonSerializer } } -} \ No newline at end of file +} diff --git a/src/Nest/QueryDsl/Compound/FunctionScore/Functions/ScriptScore/ScriptScoreFunction.cs b/src/Nest/QueryDsl/Compound/FunctionScore/Functions/ScriptScore/ScriptScoreFunction.cs index bf31ec4b295..9610a400b5a 100644 --- a/src/Nest/QueryDsl/Compound/FunctionScore/Functions/ScriptScore/ScriptScoreFunction.cs +++ b/src/Nest/QueryDsl/Compound/FunctionScore/Functions/ScriptScore/ScriptScoreFunction.cs @@ -24,4 +24,4 @@ public class ScriptScoreFunctionDescriptor : public ScriptScoreFunctionDescriptor Script(Func, IScriptQuery> selector) => Assign(a => a.Script = selector?.Invoke(new ScriptQueryDescriptor())); } -} \ No newline at end of file +} diff --git a/src/Nest/QueryDsl/Specialized/Script/ScriptQuery.cs b/src/Nest/QueryDsl/Specialized/Script/ScriptQuery.cs index 0e689951e0d..1acd63e7ba1 100644 --- a/src/Nest/QueryDsl/Specialized/Script/ScriptQuery.cs +++ b/src/Nest/QueryDsl/Specialized/Script/ScriptQuery.cs @@ -5,7 +5,7 @@ namespace Nest { - [JsonConverter(typeof(ReadAsTypeJsonConverter))] + [JsonConverter(typeof(ScriptQueryConverter))] [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public interface IScriptQuery : IQuery { @@ -36,11 +36,12 @@ public class ScriptQuery : QueryBase, IScriptQuery public string Lang { get; set; } internal override void WrapInContainer(IQueryContainer c) => c.Script = this; - internal static bool IsConditionless(IScriptQuery q) => + internal static bool IsConditionless(IScriptQuery q) => q.Inline.IsNullOrEmpty() && q.Id == null && q.File.IsNullOrEmpty(); + } - public class ScriptQueryDescriptor + public class ScriptQueryDescriptor : QueryDescriptorBase, IScriptQuery> , IScriptQuery where T : class { diff --git a/src/Nest/QueryDsl/Specialized/Script/ScriptQueryConverter.cs b/src/Nest/QueryDsl/Specialized/Script/ScriptQueryConverter.cs new file mode 100644 index 00000000000..cc2cd2319fe --- /dev/null +++ b/src/Nest/QueryDsl/Specialized/Script/ScriptQueryConverter.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Nest +{ + internal class ScriptQueryConverter : JsonConverter + { + private readonly VerbatimDictionaryKeysJsonConverter _dictionaryConverter = + new VerbatimDictionaryKeysJsonConverter(); + + private readonly PropertyJsonConverter _elasticTypeConverter = new PropertyJsonConverter(); + + public override bool CanRead => true; + public override bool CanWrite => true; + public override bool CanConvert(Type objectType) => true; + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var v = value as IScriptQuery; + if (v == null) return; + + writer.WriteStartObject(); + if (!v.Name.IsNullOrEmpty()) writer.WriteProperty(serializer, "_name", v.Name); + if (v.Boost != null) writer.WriteProperty(serializer, "boost", v.Boost); + writer.WritePropertyName("script"); + writer.WriteStartObject(); + { + if (v.Id != null) writer.WriteProperty(serializer, "id", v.Id); + if (v.File != null) writer.WriteProperty(serializer, "file", v.File); + if (v.Inline != null) writer.WriteProperty(serializer, "inline", v.Inline); + if (v.Lang != null) writer.WriteProperty(serializer, "lang", v.Lang); + if (v.Params != null) writer.WriteProperty(serializer, "params", v.Params); + } + writer.WriteEndObject(); + writer.WriteEndObject(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var r = new ScriptQuery(); + JObject o = JObject.Load(reader); + var properties = o.Properties().ToListOrNullIfEmpty(); + var scriptProperty = properties.FirstOrDefault(p => p.Name == "script"); + if (scriptProperty != null) + properties.AddRange(scriptProperty.Value.Value().Properties()); + + foreach (var p in properties) + { + switch (p.Name) + { + case "_name": + r.Name = p.Value.Value(); + break; + case "boost": + r.Boost = p.Value.Value(); + break; + case "id": + r.Id = p.Value.Value(); + break; + case "file": + r.File = p.Value.Value(); + break; + case "inline": + r.Inline = p.Value.Value(); + break; + case "lang": + r.Lang = p.Value.Value(); + break; + case "params": + r.Params = p.Value.ToObject>(); + break; + } + } + return r; + } + } + + + + internal class SimpleScriptQueryConverter : JsonConverter + { + private readonly VerbatimDictionaryKeysJsonConverter _dictionaryConverter = + new VerbatimDictionaryKeysJsonConverter(); + + private readonly PropertyJsonConverter _elasticTypeConverter = new PropertyJsonConverter(); + + public override bool CanRead => true; + public override bool CanWrite => true; + public override bool CanConvert(Type objectType) => true; + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var v = value as IScriptQuery; + if (v == null) return; + + writer.WriteStartObject(); + if (!v.Name.IsNullOrEmpty()) writer.WriteProperty(serializer, "_name", v.Name); + if (v.Boost != null) writer.WriteProperty(serializer, "boost", v.Boost); + //writer.WritePropertyName("script"); + writer.WriteStartObject(); + { + if (v.Id != null) writer.WriteProperty(serializer, "id", v.Id); + if (v.File != null) writer.WriteProperty(serializer, "file", v.File); + if (v.Inline != null) writer.WriteProperty(serializer, "inline", v.Inline); + if (v.Lang != null) writer.WriteProperty(serializer, "lang", v.Lang); + if (v.Params != null) writer.WriteProperty(serializer, "params", v.Params); + } + //writer.WriteEndObject(); + writer.WriteEndObject(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var r = new ScriptQuery(); + JObject o = JObject.Load(reader); + var properties = o.Properties().ToListOrNullIfEmpty(); + //var scriptProperty = properties.First(p=>p.Name == "script"); + //properties.AddRange(scriptProperty.Value.Value().Properties()); + + foreach (var p in properties) + { + switch (p.Name) + { + case "_name": + r.Name = p.Value.Value(); + break; + case "boost": + r.Boost = p.Value.Value(); + break; + case "id": + r.Id = p.Value.Value(); + break; + case "file": + r.File = p.Value.Value(); + break; + case "inline": + r.Inline = p.Value.Value(); + break; + case "lang": + r.Lang = p.Value.Value(); + break; + case "params": + r.Params = p.Value.ToObject>(); + break; + } + } + return r; + } + } +} diff --git a/src/Tests/Aggregations/Bucket/Filter/FilterAggregationUsageTests.cs b/src/Tests/Aggregations/Bucket/Filter/FilterAggregationUsageTests.cs index aacfdae10d7..bd491dff198 100644 --- a/src/Tests/Aggregations/Bucket/Filter/FilterAggregationUsageTests.cs +++ b/src/Tests/Aggregations/Bucket/Filter/FilterAggregationUsageTests.cs @@ -10,7 +10,7 @@ namespace Tests.Aggregations.Bucket.Filter { /** - * Defines a single bucket of all the documents in the current document set context that match a specified filter. + * Defines a single bucket of all the documents in the current document set context that match a specified filter. * Often this will be used to narrow down the current aggregation context to a specific set of documents. * * Be sure to read the elasticsearch documentation {ref}/search-aggregations-bucket-filter-aggregation.html[on this subject here] @@ -71,7 +71,7 @@ protected override void ExpectResponse(ISearchResponse response) response.IsValid.Should().BeTrue(); /** - * Using the `.Agg` aggregation helper we can fetch our aggregation results easily + * Using the `.Agg` aggregation helper we can fetch our aggregation results easily * in the correct type. [Be sure to read more about `.Agg` vs `.Aggregations` on the response here]() */ var filterAgg = response.Aggs.Filter("bethels_projects"); @@ -85,18 +85,13 @@ protected override void ExpectResponse(ISearchResponse response) public class EmptyFilterAggregationUsageTests : AggregationUsageTestBase { - public EmptyFilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) - { - } + public EmptyFilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { } protected override object ExpectJson => new { aggs = new { - empty_filter = new - { - filter = new {} - } + empty_filter = new { filter = new {} } } }; @@ -129,4 +124,57 @@ protected override void ExpectResponse(ISearchResponse response) response.Aggs.Filter("empty_filter").DocCount.Should().BeGreaterThan(0); } } + + //reproduce of https://github.com/elastic/elasticsearch-net/issues/1931 + public class InlineScriptFilterAggregationUsageTests : AggregationUsageTestBase + { + private string _ctxNumberofcommits = "_source.numberOfCommits > 0"; + private string _aggName = "script_filter"; + + public InlineScriptFilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { } + + protected override object ExpectJson => new + { + aggs = new { + script_filter = new { + filter = new { + script = new { + script = new { + inline = _ctxNumberofcommits + } + } + } + } + } + }; + + protected override Func, ISearchRequest> Fluent => s => s + .Aggregations(aggs => aggs + .Filter(_aggName, date => date + .Filter(f => f + .Script(b => b + .Inline(_ctxNumberofcommits) + ) + ) + ) + ); + + protected override SearchRequest Initializer => + new SearchRequest + { + Aggregations = new FilterAggregation(_aggName) + { + Filter = new ScriptQuery + { + Inline = _ctxNumberofcommits + } + } + }; + + protected override void ExpectResponse(ISearchResponse response) + { + response.IsValid.Should().BeTrue(); + response.Aggs.Filter(_aggName).DocCount.Should().BeGreaterThan(0); + } + } } diff --git a/src/Tests/QueryDsl/Compound/FunctionScore/FunctionScoreQueryUsageTests.cs b/src/Tests/QueryDsl/Compound/FunctionScore/FunctionScoreQueryUsageTests.cs index ef71c4e6846..863f647b4ea 100644 --- a/src/Tests/QueryDsl/Compound/FunctionScore/FunctionScoreQueryUsageTests.cs +++ b/src/Tests/QueryDsl/Compound/FunctionScore/FunctionScoreQueryUsageTests.cs @@ -94,7 +94,7 @@ public FunctionScoreQueryUsageTests(ReadOnlyCluster i, EndpointUsage usage) : ba new ExponentialDecayFunction { Origin = 1.0, Decay = 0.5, Field = Field(p=>p.NumberOfCommits), Scale = 0.1, Weight = 2.1 }, new GaussDateDecayFunction { Origin = DateMath.Now, Field = Field(p=>p.LastActivity), Decay = 0.5, Scale = TimeSpan.FromDays(1) }, new LinearGeoDecayFunction { Origin = new GeoLocation(70, -70), Field = Field(p=>p.Location), Scale = Distance.Miles(1), MultiValueMode = MultiValueMode.Average }, - new FieldValueFactorFunction + new FieldValueFactorFunction { Field = "x", Factor = 1.1, Missing = 0.1, Modifier = FieldValueFactorModifier.Ln }, diff --git a/src/Tests/QueryDsl/Specialized/Script/ScriptQueryUsageTests.cs b/src/Tests/QueryDsl/Specialized/Script/ScriptQueryUsageTests.cs index 7c07d261865..af3d9440721 100644 --- a/src/Tests/QueryDsl/Specialized/Script/ScriptQueryUsageTests.cs +++ b/src/Tests/QueryDsl/Specialized/Script/ScriptQueryUsageTests.cs @@ -17,15 +17,12 @@ public ScriptUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { _name = "named_query", boost = 1.1, - inline = "doc['num1'].value > param1", - @params = new + script = new { - param1 = 1 + inline = "doc['num1'].value > param1", + @params = new { param1 = 1 } } } - - - }; protected override QueryContainer QueryInitializer => new ScriptQuery @@ -61,4 +58,4 @@ protected override QueryContainer QueryFluent(QueryContainerDescriptor } }; } -} \ No newline at end of file +}