Skip to content

Commit c80e3fb

Browse files
committed
When serializing a collection of queries/filters, do not serialize null or conditionless queries/filters
Fixes #1817 Fixes #1749
1 parent 0462791 commit c80e3fb

File tree

15 files changed

+347
-93
lines changed

15 files changed

+347
-93
lines changed

src/Benchmarking/project.lock.json

Lines changed: 36 additions & 36 deletions
Large diffs are not rendered by default.

src/Elasticsearch.Net/project.lock.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2025,7 +2025,7 @@
20252025
"System.Dynamic.Runtime/4.0.11-beta-23516": {
20262026
"type": "package",
20272027
"serviceable": true,
2028-
"sha512": "C2GXB20I5vMcO4wemZr5pEjwwEb6H6zVkwF12JSUhripKBIKgI0YKpfp9glyDSL903cYgIXAztMQDajwCR0PmA==",
2028+
"sha512": "ypkxS0e+yUw7F6JEwuB22u0qqruMeZFOmtcImh2efDHpTAuhF2FOqCDJ7f4qLf9yomVvB4kjkZ6xGunbIQryxQ==",
20292029
"files": [
20302030
"lib/DNXCore50/System.Dynamic.Runtime.dll",
20312031
"lib/MonoAndroid10/_._",

src/Nest/Aggregations/Bucket/Filter/FilterAggregation.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace Nest
77
[ContractJsonConverter(typeof(FilterAggregationJsonConverter))]
88
public interface IFilterAggregation : IBucketAggregation
99
{
10+
[JsonProperty("filter")]
1011
QueryContainer Filter { get; set; }
1112
}
1213

src/Nest/Aggregations/Bucket/Filter/FilterAggregationJsonConverter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s
1616
var f = value as IFilterAggregation;
1717
if (f == null || f.Filter == null)
1818
{
19-
writer.WriteNull();
19+
writer.WriteStartObject();
20+
writer.WriteEndObject();
2021
return;
2122
};
2223

src/Nest/Aggregations/Bucket/Filters/FiltersAggregation.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ public FiltersAggregationDescriptor<T> NamedFilters(Func<NamedFiltersContainerDe
8585
Assign(a => a.Filters = new Union<INamedFiltersContainer, List<QueryContainer>>(selector?.Invoke(new NamedFiltersContainerDescriptor<T>())?.Value));
8686

8787
public FiltersAggregationDescriptor<T> AnonymousFilters(params Func<QueryContainerDescriptor<T>, QueryContainer>[] selectors) =>
88-
Assign(a => a.Filters = selectors.Select(s=>s?.InvokeQuery(new QueryContainerDescriptor<T>())).ToListOrNullIfEmpty());
88+
Assign(a => a.Filters = selectors.Select(s=>s?.InvokeQuery(new QueryContainerDescriptor<T>())).ToList());
8989

9090
public FiltersAggregationDescriptor<T> AnonymousFilters(IEnumerable<Func<QueryContainerDescriptor<T>, QueryContainer>> selectors) =>
91-
Assign(a => a.Filters = selectors.Select(s=>s?.InvokeQuery(new QueryContainerDescriptor<T>())).ToListOrNullIfEmpty());
91+
Assign(a => a.Filters = selectors.Select(s=>s?.InvokeQuery(new QueryContainerDescriptor<T>())).ToList());
9292

9393
}
9494
}

src/Nest/CommonAbstractions/SerializationBehavior/ElasticContractResolver.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ protected override JsonContract CreateContract(Type objectType)
4040

4141
if (typeof(IDictionary).IsAssignableFrom(objectType) && !typeof(IIsADictionary).IsAssignableFrom(objectType))
4242
contract.Converter = new VerbatimDictionaryKeysJsonConverter();
43+
if (typeof (IEnumerable<QueryContainer>).IsAssignableFrom(objectType))
44+
contract.Converter = new QueryContainerCollectionJsonConverter();
4345
else if (objectType == typeof(ServerError))
4446
contract.Converter = new ServerErrorJsonConverter();
4547
else if (objectType == typeof(DateTime) ||
@@ -162,7 +164,11 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ
162164
Predicate<object> shouldSerialize = obj =>
163165
{
164166
var collection = property.ValueProvider.GetValue(obj) as ICollection;
165-
return collection == null || collection.Count != 0;
167+
if (collection == null)
168+
{
169+
return true;
170+
}
171+
return collection.Count != 0 && collection.Cast<object>().Any(item => item != null);
166172
};
167173
property.ShouldSerialize = property.ShouldSerialize == null ? shouldSerialize : (o => property.ShouldSerialize(o) && shouldSerialize(o));
168174
}

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
24
using Newtonsoft.Json;
35

46
namespace Nest
@@ -17,4 +19,34 @@ protected override void SerializeJson(JsonWriter writer, object value, IQueryCon
1719
base.SerializeJson(writer, value, castValue, serializer);
1820
}
1921
}
22+
23+
internal class QueryContainerCollectionJsonConverter : JsonConverter
24+
{
25+
public override bool CanWrite => true;
26+
public override bool CanRead => false;
27+
28+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
29+
{
30+
var collection = (IEnumerable<QueryContainer>) value;
31+
32+
if (collection == null)
33+
{
34+
writer.WriteNull();
35+
return;
36+
}
37+
38+
writer.WriteStartArray();
39+
foreach (var queryContainer in collection)
40+
if (queryContainer != null && (queryContainer.IsStrict || !queryContainer.IsConditionless))
41+
serializer.Serialize(writer, queryContainer);
42+
writer.WriteEndArray();
43+
}
44+
45+
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
46+
{
47+
throw new NotImplementedException();
48+
}
49+
50+
public override bool CanConvert(Type objectType) => true;
51+
}
2052
}

src/Nest/QueryDsl/Abstractions/Query/QueryBase.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ private static QueryBase Combine(QueryBase leftQuery, QueryBase rightQuery, Func
5252

5353
IQueryContainer container = combine(leftQuery, rightQuery);
5454
var query = container.Bool;
55-
return new BoolQuery()
55+
return new BoolQuery
5656
{
5757
Must = query.Must,
5858
MustNot = query.MustNot,
@@ -69,7 +69,8 @@ private static bool IfEitherIsEmptyReturnTheOtherOrEmpty(QueryBase leftQuery, Qu
6969
return any;
7070
}
7171

72-
public static implicit operator QueryContainer(QueryBase query) => query == null || query.Conditionless ? null : new QueryContainer(query);
72+
public static implicit operator QueryContainer(QueryBase query) =>
73+
query == null || query.Conditionless ? null : new QueryContainer(query);
7374

7475
internal abstract void WrapInContainer(IQueryContainer container);
7576
}

src/Nest/project.lock.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2037,7 +2037,7 @@
20372037
"runtime.any.System.Linq.Expressions/4.0.11-beta-23516": {
20382038
"type": "package",
20392039
"serviceable": true,
2040-
"sha512": "P5nzo1Ye0GxB4BYdWian6Y427eTrhn1JS3jLWZq5bMWVn8hS/OIfyylASN0A/qqeLn4rGA0fOzmJSYqFSKvxgQ==",
2040+
"sha512": "4sPxQCjllMJ1uZNlwz/EataPyHSH+AqSDlOIPPqcy/88R2B+abfhPPC78rd7gvHp8KmMX4qbJF6lcCeDIQpmVg==",
20412041
"files": [
20422042
"lib/DNXCore50/System.Linq.Expressions.dll",
20432043
"lib/MonoAndroid10/_._",
@@ -2345,7 +2345,7 @@
23452345
"System.Dynamic.Runtime/4.0.11-beta-23516": {
23462346
"type": "package",
23472347
"serviceable": true,
2348-
"sha512": "C2GXB20I5vMcO4wemZr5pEjwwEb6H6zVkwF12JSUhripKBIKgI0YKpfp9glyDSL903cYgIXAztMQDajwCR0PmA==",
2348+
"sha512": "ypkxS0e+yUw7F6JEwuB22u0qqruMeZFOmtcImh2efDHpTAuhF2FOqCDJ7f4qLf9yomVvB4kjkZ6xGunbIQryxQ==",
23492349
"files": [
23502350
"lib/DNXCore50/System.Dynamic.Runtime.dll",
23512351
"lib/MonoAndroid10/_._",
@@ -2599,7 +2599,7 @@
25992599
"System.Linq.Expressions/4.0.11-beta-23516": {
26002600
"type": "package",
26012601
"serviceable": true,
2602-
"sha512": "FtKytB13HabzrSvrAgBgOOnG2uxJO4s7zvP5Sk0NS3bwbJUyb5AP1p4897UWnLiB6C95jI4nIkZps51sa9In8g==",
2602+
"sha512": "YEl5oyF5fifLbHHP099cvb/6f2r2h1QVHzoaoINPHOZtpNec+RfqvzETXcYDIdHT7l+bBAYsBuVUkBgfQEoYfQ==",
26032603
"files": [
26042604
"lib/MonoAndroid10/_._",
26052605
"lib/MonoTouch10/_._",

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

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ namespace Tests.Aggregations.Bucket.Filter
1515
*
1616
* Be sure to read the elasticsearch documentation {ref}/search-aggregations-bucket-filter-aggregation.html[on this subject here]
1717
*/
18+
1819
public class FilterAggregationUsageTests : AggregationUsageTestBase
1920
{
20-
public FilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }
21+
public FilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage)
22+
{
23+
}
2124

2225
public static string FirstNameToFind = Project.Projects.First().LeadDeveloper.FirstName.ToLowerInvariant();
2326

@@ -30,13 +33,13 @@ public FilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : bas
3033
filter = new
3134
{
3235
term = new Dictionary<string, object>
33-
{
34-
{ "leadDeveloper.firstName", new { value = FirstNameToFind }}
35-
}
36+
{
37+
{"leadDeveloper.firstName", new {value = FirstNameToFind}}
38+
}
3639
},
3740
aggs = new
3841
{
39-
project_tags = new { terms = new { field = "curatedTags.name" } }
42+
project_tags = new {terms = new {field = "curatedTags.name"}}
4043
}
4144
}
4245
}
@@ -57,9 +60,9 @@ public FilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : bas
5760
{
5861
Aggregations = new FilterAggregation("bethels_projects")
5962
{
60-
Filter = new TermQuery { Field = Field<Project>(p => p.LeadDeveloper.FirstName), Value = FirstNameToFind },
63+
Filter = new TermQuery {Field = Field<Project>(p => p.LeadDeveloper.FirstName), Value = FirstNameToFind},
6164
Aggregations =
62-
new TermsAggregation("project_tags") { Field = Field<Project>(p => p.CuratedTags.First().Name) }
65+
new TermsAggregation("project_tags") {Field = Field<Project>(p => p.CuratedTags.First().Name)}
6366
}
6467
};
6568

@@ -79,4 +82,51 @@ protected override void ExpectResponse(ISearchResponse<Project> response)
7982
tags.Buckets.Should().NotBeEmpty();
8083
}
8184
}
85+
86+
public class EmptyFilterAggregationUsageTests : AggregationUsageTestBase
87+
{
88+
public EmptyFilterAggregationUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage)
89+
{
90+
}
91+
92+
protected override object ExpectJson => new
93+
{
94+
aggs = new
95+
{
96+
empty_filter = new
97+
{
98+
filter = new object()
99+
}
100+
}
101+
};
102+
103+
protected override Func<SearchDescriptor<Project>, ISearchRequest> Fluent => s => s
104+
.Aggregations(aggs => aggs
105+
.Filter("empty_filter", date => date
106+
.Filter(f => f
107+
.Bool(b => b
108+
.Filter(new QueryContainer[0])
109+
)
110+
)
111+
)
112+
);
113+
114+
protected override SearchRequest<Project> Initializer =>
115+
new SearchRequest<Project>
116+
{
117+
Aggregations = new FilterAggregation("empty_filter")
118+
{
119+
Filter = new BoolQuery
120+
{
121+
Filter = new List<QueryContainer>()
122+
}
123+
}
124+
};
125+
126+
protected override void ExpectResponse(ISearchResponse<Project> response)
127+
{
128+
response.IsValid.Should().BeTrue();
129+
response.Aggs.Filter("empty_filter").DocCount.Should().BeGreaterThan(0);
130+
}
131+
}
82132
}

0 commit comments

Comments
 (0)