Skip to content

Commit 04fbfeb

Browse files
committed
Merge pull request #1944 from elastic/fix/script-query-serialization
fix #1931 script query was not serializing properly
2 parents 99c537b + 7231bef commit 04fbfeb

File tree

10 files changed

+230
-29
lines changed

10 files changed

+230
-29
lines changed

src/Nest/CommonAbstractions/SerializationBehavior/ElasticContractResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ protected override JsonContract CreateContract(Type objectType)
4444
contract.Converter = new QueryContainerCollectionJsonConverter();
4545
else if (objectType == typeof(ServerError))
4646
contract.Converter = new ServerErrorJsonConverter();
47-
else if (objectType == typeof(DateTime) ||
47+
else if (objectType == typeof(DateTime) ||
4848
objectType == typeof(DateTime?) ||
4949
objectType == typeof(DateTimeOffset) ||
5050
objectType == typeof(DateTimeOffset?))

src/Nest/Indices/Monitoring/IndicesShardStores/ShardStoreJsonConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal class ShardStoreJsonConverter : JsonConverter
1515

1616
public override bool CanRead => true;
1717
public override bool CanWrite => false;
18-
public override bool CanConvert(Type objectType) => objectType == typeof(IDictionary<string, IFieldMapping>);
18+
public override bool CanConvert(Type objectType) => true;
1919

2020
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
2121
{

src/Nest/Nest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,7 @@
10121012
<Compile Include="QueryDsl\Specialized\MoreLikeThis\Like\LikeDocument.cs" />
10131013
<Compile Include="QueryDsl\Specialized\MoreLikeThis\MoreLikeThisQuery.cs" />
10141014
<Compile Include="QueryDsl\Specialized\Script\ScriptQuery.cs" />
1015+
<Compile Include="QueryDsl\Specialized\Script\ScriptQueryConverter.cs" />
10151016
<Compile Include="QueryDsl\Specialized\Template\TemplateQuery.cs" />
10161017
<Compile Include="QueryDsl\TermLevel\Exists\ExistsQuery.cs" />
10171018
<Compile Include="QueryDsl\TermLevel\Fuzzy\FuzzyQueries.cs" />

src/Nest/QueryDsl/Compound/FunctionScore/Functions/ScoreFunctionJsonConverter.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@ private bool WriteScriptScore(JsonWriter writer, IScriptScoreFunction value, Jso
4949
{
5050
if (value == null) return false;
5151
writer.WritePropertyName("script_score");
52-
writer.WriteStartObject();
53-
{
54-
writer.WriteProperty(serializer, "script", value.Script);
55-
}
56-
writer.WriteEndObject();
52+
serializer.Serialize(writer, value.Script);
53+
//writer.WriteStartObject();
54+
//{
55+
// writer.WriteProperty(serializer, "script", value.Script);
56+
//}
57+
//writer.WriteEndObject();
5758
return true;
5859
}
5960

@@ -235,4 +236,4 @@ private IDecayFunction ReadDecayFunction(string type, JObject o, JsonSerializer
235236
}
236237

237238
}
238-
}
239+
}

src/Nest/QueryDsl/Compound/FunctionScore/Functions/ScriptScore/ScriptScoreFunction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ public class ScriptScoreFunctionDescriptor<T> :
2424
public ScriptScoreFunctionDescriptor<T> Script(Func<ScriptQueryDescriptor<T>, IScriptQuery> selector) =>
2525
Assign(a => a.Script = selector?.Invoke(new ScriptQueryDescriptor<T>()));
2626
}
27-
}
27+
}

src/Nest/QueryDsl/Specialized/Script/ScriptQuery.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace Nest
66
{
77

8-
[JsonConverter(typeof(ReadAsTypeJsonConverter<ScriptQuery>))]
8+
[JsonConverter(typeof(ScriptQueryConverter))]
99
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
1010
public interface IScriptQuery : IQuery
1111
{
@@ -36,11 +36,12 @@ public class ScriptQuery : QueryBase, IScriptQuery
3636
public string Lang { get; set; }
3737

3838
internal override void WrapInContainer(IQueryContainer c) => c.Script = this;
39-
internal static bool IsConditionless(IScriptQuery q) =>
39+
internal static bool IsConditionless(IScriptQuery q) =>
4040
q.Inline.IsNullOrEmpty() && q.Id == null && q.File.IsNullOrEmpty();
41+
4142
}
4243

43-
public class ScriptQueryDescriptor<T>
44+
public class ScriptQueryDescriptor<T>
4445
: QueryDescriptorBase<ScriptQueryDescriptor<T>, IScriptQuery>
4546
, IScriptQuery where T : class
4647
{
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Newtonsoft.Json;
5+
using Newtonsoft.Json.Linq;
6+
7+
namespace Nest
8+
{
9+
internal class ScriptQueryConverter : JsonConverter
10+
{
11+
private readonly VerbatimDictionaryKeysJsonConverter _dictionaryConverter =
12+
new VerbatimDictionaryKeysJsonConverter();
13+
14+
private readonly PropertyJsonConverter _elasticTypeConverter = new PropertyJsonConverter();
15+
16+
public override bool CanRead => true;
17+
public override bool CanWrite => true;
18+
public override bool CanConvert(Type objectType) => true;
19+
20+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
21+
{
22+
var v = value as IScriptQuery;
23+
if (v == null) return;
24+
25+
writer.WriteStartObject();
26+
if (!v.Name.IsNullOrEmpty()) writer.WriteProperty(serializer, "_name", v.Name);
27+
if (v.Boost != null) writer.WriteProperty(serializer, "boost", v.Boost);
28+
writer.WritePropertyName("script");
29+
writer.WriteStartObject();
30+
{
31+
if (v.Id != null) writer.WriteProperty(serializer, "id", v.Id);
32+
if (v.File != null) writer.WriteProperty(serializer, "file", v.File);
33+
if (v.Inline != null) writer.WriteProperty(serializer, "inline", v.Inline);
34+
if (v.Lang != null) writer.WriteProperty(serializer, "lang", v.Lang);
35+
if (v.Params != null) writer.WriteProperty(serializer, "params", v.Params);
36+
}
37+
writer.WriteEndObject();
38+
writer.WriteEndObject();
39+
}
40+
41+
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
42+
{
43+
var r = new ScriptQuery();
44+
JObject o = JObject.Load(reader);
45+
var properties = o.Properties().ToListOrNullIfEmpty();
46+
var scriptProperty = properties.FirstOrDefault(p => p.Name == "script");
47+
if (scriptProperty != null)
48+
properties.AddRange(scriptProperty.Value.Value<JObject>().Properties());
49+
50+
foreach (var p in properties)
51+
{
52+
switch (p.Name)
53+
{
54+
case "_name":
55+
r.Name = p.Value.Value<string>();
56+
break;
57+
case "boost":
58+
r.Boost = p.Value.Value<double>();
59+
break;
60+
case "id":
61+
r.Id = p.Value.Value<string>();
62+
break;
63+
case "file":
64+
r.File = p.Value.Value<string>();
65+
break;
66+
case "inline":
67+
r.Inline = p.Value.Value<string>();
68+
break;
69+
case "lang":
70+
r.Lang = p.Value.Value<string>();
71+
break;
72+
case "params":
73+
r.Params = p.Value.ToObject<Dictionary<string, object>>();
74+
break;
75+
}
76+
}
77+
return r;
78+
}
79+
}
80+
81+
82+
83+
internal class SimpleScriptQueryConverter : JsonConverter
84+
{
85+
private readonly VerbatimDictionaryKeysJsonConverter _dictionaryConverter =
86+
new VerbatimDictionaryKeysJsonConverter();
87+
88+
private readonly PropertyJsonConverter _elasticTypeConverter = new PropertyJsonConverter();
89+
90+
public override bool CanRead => true;
91+
public override bool CanWrite => true;
92+
public override bool CanConvert(Type objectType) => true;
93+
94+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
95+
{
96+
var v = value as IScriptQuery;
97+
if (v == null) return;
98+
99+
writer.WriteStartObject();
100+
if (!v.Name.IsNullOrEmpty()) writer.WriteProperty(serializer, "_name", v.Name);
101+
if (v.Boost != null) writer.WriteProperty(serializer, "boost", v.Boost);
102+
//writer.WritePropertyName("script");
103+
writer.WriteStartObject();
104+
{
105+
if (v.Id != null) writer.WriteProperty(serializer, "id", v.Id);
106+
if (v.File != null) writer.WriteProperty(serializer, "file", v.File);
107+
if (v.Inline != null) writer.WriteProperty(serializer, "inline", v.Inline);
108+
if (v.Lang != null) writer.WriteProperty(serializer, "lang", v.Lang);
109+
if (v.Params != null) writer.WriteProperty(serializer, "params", v.Params);
110+
}
111+
//writer.WriteEndObject();
112+
writer.WriteEndObject();
113+
}
114+
115+
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
116+
{
117+
var r = new ScriptQuery();
118+
JObject o = JObject.Load(reader);
119+
var properties = o.Properties().ToListOrNullIfEmpty();
120+
//var scriptProperty = properties.First(p=>p.Name == "script");
121+
//properties.AddRange(scriptProperty.Value.Value<JObject>().Properties());
122+
123+
foreach (var p in properties)
124+
{
125+
switch (p.Name)
126+
{
127+
case "_name":
128+
r.Name = p.Value.Value<string>();
129+
break;
130+
case "boost":
131+
r.Boost = p.Value.Value<double>();
132+
break;
133+
case "id":
134+
r.Id = p.Value.Value<string>();
135+
break;
136+
case "file":
137+
r.File = p.Value.Value<string>();
138+
break;
139+
case "inline":
140+
r.Inline = p.Value.Value<string>();
141+
break;
142+
case "lang":
143+
r.Lang = p.Value.Value<string>();
144+
break;
145+
case "params":
146+
r.Params = p.Value.ToObject<Dictionary<string, object>>();
147+
break;
148+
}
149+
}
150+
return r;
151+
}
152+
}
153+
}

src/Tests/Aggregations/Bucket/Filter/FilterAggregationUsageTests.cs

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace Tests.Aggregations.Bucket.Filter
1111
{
1212
/**
13-
* Defines a single bucket of all the documents in the current document set context that match a specified filter.
13+
* Defines a single bucket of all the documents in the current document set context that match a specified filter.
1414
* Often this will be used to narrow down the current aggregation context to a specific set of documents.
1515
*
1616
* 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<Project> response)
7171
response.IsValid.Should().BeTrue();
7272

7373
/**
74-
* Using the `.Agg` aggregation helper we can fetch our aggregation results easily
74+
* Using the `.Agg` aggregation helper we can fetch our aggregation results easily
7575
* in the correct type. [Be sure to read more about `.Agg` vs `.Aggregations` on the response here]()
7676
*/
7777
var filterAgg = response.Aggs.Filter("bethels_projects");
@@ -85,18 +85,13 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
8585

8686
public class EmptyFilterAggregationUsageTests : AggregationUsageTestBase
8787
{
88-
public EmptyFilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage)
89-
{
90-
}
88+
public EmptyFilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }
9189

9290
protected override object ExpectJson => new
9391
{
9492
aggs = new
9593
{
96-
empty_filter = new
97-
{
98-
filter = new {}
99-
}
94+
empty_filter = new { filter = new {} }
10095
}
10196
};
10297

@@ -129,4 +124,57 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
129124
response.Aggs.Filter("empty_filter").DocCount.Should().BeGreaterThan(0);
130125
}
131126
}
127+
128+
//reproduce of https://github.com/elastic/elasticsearch-net/issues/1931
129+
public class InlineScriptFilterAggregationUsageTests : AggregationUsageTestBase
130+
{
131+
private string _ctxNumberofcommits = "_source.numberOfCommits > 0";
132+
private string _aggName = "script_filter";
133+
134+
public InlineScriptFilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }
135+
136+
protected override object ExpectJson => new
137+
{
138+
aggs = new {
139+
script_filter = new {
140+
filter = new {
141+
script = new {
142+
script = new {
143+
inline = _ctxNumberofcommits
144+
}
145+
}
146+
}
147+
}
148+
}
149+
};
150+
151+
protected override Func<SearchDescriptor<Project>, ISearchRequest> Fluent => s => s
152+
.Aggregations(aggs => aggs
153+
.Filter(_aggName, date => date
154+
.Filter(f => f
155+
.Script(b => b
156+
.Inline(_ctxNumberofcommits)
157+
)
158+
)
159+
)
160+
);
161+
162+
protected override SearchRequest<Project> Initializer =>
163+
new SearchRequest<Project>
164+
{
165+
Aggregations = new FilterAggregation(_aggName)
166+
{
167+
Filter = new ScriptQuery
168+
{
169+
Inline = _ctxNumberofcommits
170+
}
171+
}
172+
};
173+
174+
protected override void ExpectResponse(ISearchResponse<Project> response)
175+
{
176+
response.IsValid.Should().BeTrue();
177+
response.Aggs.Filter(_aggName).DocCount.Should().BeGreaterThan(0);
178+
}
179+
}
132180
}

src/Tests/QueryDsl/Compound/FunctionScore/FunctionScoreQueryUsageTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public FunctionScoreQueryUsageTests(ReadOnlyCluster i, EndpointUsage usage) : ba
9494
new ExponentialDecayFunction { Origin = 1.0, Decay = 0.5, Field = Field<Project>(p=>p.NumberOfCommits), Scale = 0.1, Weight = 2.1 },
9595
new GaussDateDecayFunction { Origin = DateMath.Now, Field = Field<Project>(p=>p.LastActivity), Decay = 0.5, Scale = TimeSpan.FromDays(1) },
9696
new LinearGeoDecayFunction { Origin = new GeoLocation(70, -70), Field = Field<Project>(p=>p.Location), Scale = Distance.Miles(1), MultiValueMode = MultiValueMode.Average },
97-
new FieldValueFactorFunction
97+
new FieldValueFactorFunction
9898
{
9999
Field = "x", Factor = 1.1, Missing = 0.1, Modifier = FieldValueFactorModifier.Ln
100100
},

src/Tests/QueryDsl/Specialized/Script/ScriptQueryUsageTests.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,12 @@ public ScriptUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage)
1717
{
1818
_name = "named_query",
1919
boost = 1.1,
20-
inline = "doc['num1'].value > param1",
21-
@params = new
20+
script = new
2221
{
23-
param1 = 1
22+
inline = "doc['num1'].value > param1",
23+
@params = new { param1 = 1 }
2424
}
2525
}
26-
27-
28-
2926
};
3027

3128
protected override QueryContainer QueryInitializer => new ScriptQuery
@@ -61,4 +58,4 @@ protected override QueryContainer QueryFluent(QueryContainerDescriptor<Project>
6158
}
6259
};
6360
}
64-
}
61+
}

0 commit comments

Comments
 (0)