Skip to content

Commit 53ee2a5

Browse files
authored
Support wildcard field in WildcardQuery (#6038) (#6043)
* Support use of `wildcard` field in WildcardQuery (cherry picked from commit a59e92d)
1 parent 4073b6b commit 53ee2a5

File tree

3 files changed

+112
-6
lines changed

3 files changed

+112
-6
lines changed

src/Nest/QueryDsl/TermLevel/Wildcard/WildcardQuery.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ public interface IWildcardQuery : ITermQuery
1515
{
1616
[DataMember(Name = "rewrite")]
1717
MultiTermQueryRewrite Rewrite { get; set; }
18+
19+
[DataMember(Name = "wildcard")]
20+
string Wildcard { get; set; }
1821
}
1922

20-
public class WildcardQuery<T, TValue> : WildcardQuery
21-
where T : class
23+
public class WildcardQuery<T, TValue> : WildcardQuery where T : class
2224
{
2325
public WildcardQuery(Expression<Func<T, TValue>> field) => Field = field;
2426
}
@@ -28,18 +30,29 @@ public class WildcardQuery : FieldNameQueryBase, IWildcardQuery
2830
public MultiTermQueryRewrite Rewrite { get; set; }
2931
public object Value { get; set; }
3032
public bool? CaseInsensitive { get; set; }
31-
protected override bool Conditionless => TermQuery.IsConditionless(this);
33+
public string Wildcard { get; set; }
34+
35+
protected override bool Conditionless => IsConditionless(this);
3236

3337
internal override void InternalWrapInContainer(IQueryContainer c) => c.Wildcard = this;
38+
39+
// Wildcard queries must include the `field` and either a `value` OR a `wildcard` to match
40+
internal static bool IsConditionless(IWildcardQuery q) => (q.Value == null && q.Wildcard == null)
41+
|| ((q.Value?.ToString().IsNullOrEmpty() ?? true) && (q.Wildcard?.ToString().IsNullOrEmpty() ?? true))
42+
|| q.Field.IsConditionless();
3443
}
3544

3645
public class WildcardQueryDescriptor<T>
37-
: TermQueryDescriptorBase<WildcardQueryDescriptor<T>, IWildcardQuery, T>,
38-
IWildcardQuery
39-
where T : class
46+
: TermQueryDescriptorBase<WildcardQueryDescriptor<T>, IWildcardQuery, T>, IWildcardQuery
47+
where T : class
4048
{
4149
MultiTermQueryRewrite IWildcardQuery.Rewrite { get; set; }
50+
string IWildcardQuery.Wildcard { get; set; }
51+
52+
protected override bool Conditionless => WildcardQuery.IsConditionless(this);
4253

4354
public WildcardQueryDescriptor<T> Rewrite(MultiTermQueryRewrite rewrite) => Assign(rewrite, (a, v) => a.Rewrite = v);
55+
56+
public WildcardQueryDescriptor<T> Wildcard(string value) => Assign(value, (a, v) => a.Wildcard = v);
4457
}
4558
}

tests/Tests/QueryDsl/TermLevel/Wildcard/WildcardQueryUsageTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,48 @@ protected override QueryContainer QueryFluent(QueryContainerDescriptor<Project>
5252
.Rewrite(MultiTermQueryRewrite.TopTermsBoost(10))
5353
);
5454
}
55+
56+
public class WildcardQueryUsingWildcardFieldUsageTests : QueryDslUsageTestsBase
57+
{
58+
public WildcardQueryUsingWildcardFieldUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
59+
60+
protected override ConditionlessWhen ConditionlessWhen => new ConditionlessWhen<IWildcardQuery>(a => a.Wildcard)
61+
{
62+
q => q.Field = null,
63+
q => { q.Value = null; q.Wildcard = null; },
64+
q => { q.Value = string.Empty; q.Wildcard = string.Empty; }
65+
};
66+
67+
protected override QueryContainer QueryInitializer => new WildcardQuery
68+
{
69+
Name = "named_query",
70+
Boost = 1.1,
71+
Field = "description",
72+
Wildcard = "p*oj",
73+
Rewrite = MultiTermQueryRewrite.TopTermsBoost(10)
74+
};
75+
76+
protected override object QueryJson => new
77+
{
78+
wildcard = new
79+
{
80+
description = new
81+
{
82+
_name = "named_query",
83+
boost = 1.1,
84+
rewrite = "top_terms_boost_10",
85+
wildcard = "p*oj"
86+
}
87+
}
88+
};
89+
90+
protected override QueryContainer QueryFluent(QueryContainerDescriptor<Project> q) => q
91+
.Wildcard(c => c
92+
.Name("named_query")
93+
.Boost(1.1)
94+
.Field(p => p.Description)
95+
.Wildcard("p*oj")
96+
.Rewrite(MultiTermQueryRewrite.TopTermsBoost(10))
97+
);
98+
}
5599
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Text;
9+
using Elastic.Elasticsearch.Xunit.XunitPlumbing;
10+
using Elasticsearch.Net;
11+
using FluentAssertions;
12+
using Nest;
13+
14+
namespace Tests.QueryDsl.TermLevel.Wildcard
15+
{
16+
public class WildcardSerialisationTests
17+
{
18+
[U]
19+
public void DeserialisesAndSerialises()
20+
{
21+
// This test validates that a response from SQL translate can be used in the seubsequent query
22+
// The WildcardQueryBuilder prefers the `wildcard` field over the `value` field.
23+
24+
var translateResponse = @"{""size"":1000,""query"":{""bool"":{""must"":[{""wildcard"":{""customershortnm.keyword"":{""wildcard"":""*B*"",""boost"":1}}}],""adjust_pure_negative"":true,""boost"":1}}}";
25+
26+
var pool = new SingleNodeConnectionPool(new Uri($"http://localhost:9200"));
27+
var settings = new ConnectionSettings(pool, new InMemoryConnection(Encoding.UTF8.GetBytes(translateResponse)));
28+
var client = new ElasticClient(settings);
29+
30+
var response = client.Sql.Translate();
31+
32+
IQueryContainer queryContainer = response.Result.Query;
33+
34+
queryContainer.Bool.Should().NotBeNull();
35+
var clauses = queryContainer.Bool.Must.ToList();
36+
queryContainer = clauses.Single();
37+
queryContainer.Wildcard.Wildcard.Should().Be("*B*");
38+
39+
var stream = new MemoryStream();
40+
client.ConnectionSettings.RequestResponseSerializer.Serialize(response.Result, stream);
41+
stream.Position = 0;
42+
var reader = new StreamReader(stream);
43+
var json = reader.ReadToEnd();
44+
45+
// note: adjust_pure_negative is not recommended
46+
json.Should().Be(@"{""query"":{""bool"":{""must"":[{""wildcard"":{""customershortnm.keyword"":{""wildcard"":""*B*"",""boost"":1.0}}}],""boost"":1.0}},""size"":1000}");
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)