Skip to content

Commit dc5dcb2

Browse files
committed
added match query support (with new cutoff_frequency and missing rewrite property) fix #238, fix #232, fix #202
1 parent 6e0bc8b commit dc5dcb2

9 files changed

+332
-0
lines changed

Diff for: src/Nest.Tests.Unit/Nest.Tests.Unit.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@
159159
<Compile Include="Search\Query\ConditionLessStrict\ConditionLessStrictTests.cs" />
160160
<Compile Include="Search\Query\ConditionLessStrict\SemiStrictBoolTests.cs" />
161161
<Compile Include="Search\Query\QueriesInQueries\QueriesInQueriesTests.cs" />
162+
<Compile Include="Search\Query\Singles\MatchQueryJson.cs" />
162163
<Compile Include="Search\Query\UpdateQuery\UpdateQueriesTests.cs" />
163164
<Compile Include="Search\Query\Singles\BoolQueryJson.cs" />
164165
<Compile Include="Search\Query\Singles\BoostingQueryJson.cs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using NUnit.Framework;
2+
using Nest.Tests.MockData.Domain;
3+
4+
namespace Nest.Tests.Unit.Search.Query.Singles
5+
{
6+
[TestFixture]
7+
public class MatchQueryJson
8+
{
9+
[Test]
10+
public void MatchQuery()
11+
{
12+
var s = new SearchDescriptor<ElasticSearchProject>()
13+
.From(0)
14+
.Size(10)
15+
.Query(q=>q
16+
.Match(t=>t
17+
.OnField(f=>f.Name)
18+
.QueryString("this is a test")
19+
)
20+
);
21+
22+
var json = TestElasticClient.Serialize(s);
23+
var expected = @"{ from: 0, size: 10,
24+
query : {
25+
match: {
26+
name : {
27+
query : ""this is a test""
28+
}
29+
}
30+
}
31+
}";
32+
Assert.True(json.JsonEquals(expected), json);
33+
}
34+
[Test]
35+
public void MatchQuerySomeOptions()
36+
{
37+
var s = new SearchDescriptor<ElasticSearchProject>()
38+
.From(0)
39+
.Size(10)
40+
.Query(q => q
41+
.Match(t => t
42+
.OnField(f => f.Name)
43+
.QueryString("this is a test")
44+
.Fuzziness(1.0)
45+
.Analyzer("my_analyzer")
46+
.CutoffFrequency(0.3)
47+
.Rewrite(RewriteMultiTerm.constant_score_filter)
48+
.PrefixLength(2)
49+
)
50+
);
51+
52+
var json = TestElasticClient.Serialize(s);
53+
var expected = @"{ from: 0, size: 10,
54+
query : {
55+
match: {
56+
name : {
57+
query : ""this is a test"",
58+
analyzer : ""my_analyzer"",
59+
rewrite: ""constant_score_filter"",
60+
fuzziness: 1.0,
61+
cutoff_frequency: 0.3,
62+
prefix_length: 2
63+
}
64+
}
65+
}
66+
}";
67+
Assert.True(json.JsonEquals(expected), json);
68+
}
69+
}
70+
}

Diff for: src/Nest/DSL/IQueryDescriptor.cs

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ interface IQueryDescriptor<T>
4242
BaseQuery Text(Action<TextQueryDescriptor<T>> selector);
4343
BaseQuery TextPhrase(Action<TextPhraseQueryDescriptor<T>> selector);
4444
BaseQuery TextPhrasePrefix(Action<TextPhrasePrefixQueryDescriptor<T>> selector);
45+
BaseQuery Match(Action<MatchQueryDescriptor<T>> selector);
46+
BaseQuery MatchPhrase(Action<MatchPhraseQueryDescriptor<T>> selector);
47+
BaseQuery MatchPhrasePrefix(Action<MatchPhrasePrefixQueryDescriptor<T>> selector);
4548
BaseQuery TopChildren<K>(Action<TopChildrenQueryDescriptor<K>> selector) where K : class;
4649
BaseQuery Wildcard(System.Linq.Expressions.Expression<Func<T, object>> fieldDescriptor, string value, double? Boost = null, RewriteMultiTerm? Rewrite = null);
4750
BaseQuery Wildcard(string field, string value, double? Boost = null, RewriteMultiTerm? Rewrite = null);

Diff for: src/Nest/DSL/Query.cs

+15
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,21 @@ public static BaseQuery TextPhrasePrefix(Action<TextPhrasePrefixQueryDescriptor<
202202
return new QueryDescriptor<T>().TextPhrasePrefix(selector);
203203
}
204204

205+
public static BaseQuery Match(Action<MatchQueryDescriptor<T>> selector)
206+
{
207+
return new QueryDescriptor<T>().Match(selector);
208+
}
209+
210+
public static BaseQuery MatchPhrase(Action<MatchPhraseQueryDescriptor<T>> selector)
211+
{
212+
return new QueryDescriptor<T>().MatchPhrase(selector);
213+
}
214+
215+
public static BaseQuery MatchPhrasePrefix(Action<MatchPhrasePrefixQueryDescriptor<T>> selector)
216+
{
217+
return new QueryDescriptor<T>().MatchPhrasePrefix(selector);
218+
}
219+
205220
public static BaseQuery TopChildren<K>(Action<TopChildrenQueryDescriptor<K>> selector) where K : class
206221
{
207222
return new QueryDescriptor<T>().TopChildren<K>(selector);
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using Newtonsoft.Json;
6+
using Newtonsoft.Json.Converters;
7+
using System.Linq.Expressions;
8+
9+
namespace Nest
10+
{
11+
/// <summary>
12+
/// A Query that matches documents containing a particular sequence of terms.
13+
/// It allows for prefix matches on the last term in the text.
14+
/// </summary>
15+
/// <typeparam name="T">Type of document</typeparam>
16+
public class MatchPhrasePrefixQueryDescriptor<T> : MatchQueryDescriptor<T> where T : class
17+
{
18+
[JsonProperty(PropertyName = "type")]
19+
internal override string _Type { get { return "phrase_prefix"; } }
20+
}
21+
}

Diff for: src/Nest/DSL/Query/MatchPhraseQueryDescriptor.cs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using Newtonsoft.Json;
6+
using Newtonsoft.Json.Converters;
7+
using System.Linq.Expressions;
8+
9+
namespace Nest
10+
{
11+
/// <summary>
12+
/// A Query that matches documents containing a particular sequence of terms. A PhraseQuery is built by QueryParser for input like "new york".
13+
/// </summary>
14+
/// <typeparam name="T">Type of document</typeparam>
15+
public class MatchPhraseQueryDescriptor<T> : MatchQueryDescriptor<T> where T : class
16+
{
17+
[JsonProperty(PropertyName = "type")]
18+
internal override string _Type { get { return "phrase"; } }
19+
}
20+
}

Diff for: src/Nest/DSL/Query/MatchQueryDescriptor.cs

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using Newtonsoft.Json;
6+
using Newtonsoft.Json.Converters;
7+
using System.Linq.Expressions;
8+
using Nest.Resolvers;
9+
10+
namespace Nest
11+
{
12+
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
13+
public class MatchQueryDescriptor<T> : IQuery where T : class
14+
{
15+
[JsonProperty(PropertyName = "type")]
16+
internal virtual string _Type { get { return null; } }
17+
18+
[JsonProperty(PropertyName = "query")]
19+
internal string _Query { get; set; }
20+
21+
[JsonProperty(PropertyName = "analyzer")]
22+
internal string _Analyzer { get; set; }
23+
24+
[JsonProperty(PropertyName = "rewrite")]
25+
[JsonConverter(typeof(StringEnumConverter))]
26+
internal RewriteMultiTerm? _Rewrite { get; set; }
27+
28+
[JsonProperty(PropertyName = "fuzziness")]
29+
internal double? _Fuzziness { get; set; }
30+
31+
[JsonProperty(PropertyName = "cutoff_frequency")]
32+
internal double? _CutoffFrequency { get; set; }
33+
34+
[JsonProperty(PropertyName = "prefix_length")]
35+
internal int? _PrefixLength { get; set; }
36+
37+
[JsonProperty(PropertyName = "max_expansions")]
38+
internal int? _MaxExpansions { get; set; }
39+
40+
[JsonProperty(PropertyName = "slop")]
41+
internal int? _Slop { get; set; }
42+
43+
[JsonProperty(PropertyName = "boost")]
44+
internal double? _Boost { get; set; }
45+
46+
[JsonProperty(PropertyName = "operator")]
47+
[JsonConverter(typeof(StringEnumConverter))]
48+
internal Operator? _Operator { get; set; }
49+
50+
internal bool IsConditionless
51+
{
52+
get
53+
{
54+
return this._Field.IsNullOrEmpty() || this._Query.IsNullOrEmpty();
55+
}
56+
}
57+
58+
59+
internal string _Field { get; set; }
60+
public MatchQueryDescriptor<T> OnField(string field)
61+
{
62+
this._Field = field;
63+
return this;
64+
}
65+
public MatchQueryDescriptor<T> OnField(Expression<Func<T, object>> objectPath)
66+
{
67+
var fieldName = new PropertyNameResolver().Resolve(objectPath);
68+
return this.OnField(fieldName);
69+
}
70+
71+
public MatchQueryDescriptor<T> QueryString(string queryString)
72+
{
73+
this._Query = queryString;
74+
return this;
75+
}
76+
public MatchQueryDescriptor<T> Analyzer(string analyzer)
77+
{
78+
analyzer.ThrowIfNullOrEmpty("analyzer");
79+
this._Analyzer = analyzer;
80+
return this;
81+
}
82+
public MatchQueryDescriptor<T> Fuzziness(double fuzziness)
83+
{
84+
fuzziness.ThrowIfNull("fuzziness");
85+
this._Fuzziness = fuzziness;
86+
return this;
87+
}
88+
public MatchQueryDescriptor<T> CutoffFrequency(double cutoffFrequency)
89+
{
90+
cutoffFrequency.ThrowIfNull("cutoffFrequency");
91+
this._CutoffFrequency = cutoffFrequency;
92+
return this;
93+
}
94+
95+
public MatchQueryDescriptor<T> Rewrite(RewriteMultiTerm rewrite)
96+
{
97+
rewrite.ThrowIfNull("rewrite");
98+
this._Rewrite = rewrite;
99+
return this;
100+
}
101+
102+
public MatchQueryDescriptor<T> Boost(double boost)
103+
{
104+
boost.ThrowIfNull("boost");
105+
this._Boost = boost;
106+
return this;
107+
}
108+
public MatchQueryDescriptor<T> PrefixLength(int prefixLength)
109+
{
110+
prefixLength.ThrowIfNull("prefixLength");
111+
this._PrefixLength = prefixLength;
112+
return this;
113+
}
114+
public MatchQueryDescriptor<T> MaxExpansions(int maxExpansions)
115+
{
116+
maxExpansions.ThrowIfNull("maxExpansions");
117+
this._MaxExpansions = maxExpansions;
118+
return this;
119+
}
120+
public MatchQueryDescriptor<T> Slop(int slop)
121+
{
122+
slop.ThrowIfNull("slop");
123+
this._Slop = slop;
124+
return this;
125+
}
126+
public MatchQueryDescriptor<T> Operator(Operator op)
127+
{
128+
this._Operator = op;
129+
return this;
130+
}
131+
}
132+
}

Diff for: src/Nest/DSL/QueryDescriptor.cs

+67
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public QueryDescriptor()
4949
internal FilteredQueryDescriptor<T> FilteredQueryDescriptor { get; set; }
5050
[JsonProperty(PropertyName = "text")]
5151
internal IDictionary<string, object> TextQueryDescriptor { get; set; }
52+
[JsonProperty(PropertyName = "match")]
53+
internal IDictionary<string, object> MatchQueryDescriptor { get; set; }
5254
[JsonProperty(PropertyName = "fuzzy")]
5355
internal IDictionary<string, object> FuzzyQueryDescriptor { get; set; }
5456
[JsonProperty(PropertyName = "terms")]
@@ -87,6 +89,7 @@ public QueryDescriptor()
8789

8890
public QueryDescriptor<T> Strict(bool strict = true)
8991
{
92+
//TODO: is this clone really neccessary ?
9093
var q = this.Clone();
9194
q._Strict = strict;
9295
return q;
@@ -122,6 +125,7 @@ internal QueryDescriptor<T> Clone()
122125
DismaxQueryDescriptor = DismaxQueryDescriptor,
123126
FilteredQueryDescriptor = FilteredQueryDescriptor,
124127
TextQueryDescriptor = TextQueryDescriptor,
128+
MatchQueryDescriptor = MatchQueryDescriptor,
125129
FuzzyQueryDescriptor = FuzzyQueryDescriptor,
126130
TermsQueryDescriptor = TermsQueryDescriptor,
127131
QueryStringDescriptor = QueryStringDescriptor,
@@ -328,6 +332,69 @@ public BaseQuery TextPhrasePrefix(Action<TextPhrasePrefixQueryDescriptor<T>> sel
328332
return new QueryDescriptor<T> { TextQueryDescriptor = this.TextQueryDescriptor };
329333
}
330334

335+
/// <summary>
336+
/// The default text query is of type boolean. It means that the text provided is analyzed and the analysis
337+
/// process constructs a boolean query from the provided text.
338+
/// </summary>
339+
public BaseQuery Match(Action<MatchQueryDescriptor<T>> selector)
340+
{
341+
var query = new MatchQueryDescriptor<T>();
342+
selector(query);
343+
if (string.IsNullOrWhiteSpace(query._Field))
344+
throw new DslException("Field name not set for text query");
345+
346+
if (query.IsConditionless)
347+
return CreateConditionlessQueryDescriptor(query);
348+
349+
this.MatchQueryDescriptor = new Dictionary<string, object>()
350+
{
351+
{ query._Field, query}
352+
};
353+
return new QueryDescriptor<T> { MatchQueryDescriptor = this.MatchQueryDescriptor };
354+
}
355+
356+
/// <summary>
357+
/// The text_phrase query analyzes the text and creates a phrase query out of the analyzed text.
358+
/// </summary>
359+
public BaseQuery MatchPhrase(Action<MatchPhraseQueryDescriptor<T>> selector)
360+
{
361+
var query = new MatchPhraseQueryDescriptor<T>();
362+
selector(query);
363+
if (string.IsNullOrWhiteSpace(query._Field))
364+
throw new DslException("Field name not set for text_phrase query");
365+
366+
if (query.IsConditionless)
367+
return CreateConditionlessQueryDescriptor(query);
368+
369+
this.MatchQueryDescriptor = new Dictionary<string, object>()
370+
{
371+
{ query._Field, query}
372+
};
373+
return new QueryDescriptor<T> { MatchQueryDescriptor = this.MatchQueryDescriptor };
374+
}
375+
376+
/// <summary>
377+
/// The text_phrase_prefix is the same as text_phrase, expect it allows for prefix matches on the last term
378+
/// in the text
379+
/// </summary>
380+
public BaseQuery MatchPhrasePrefix(Action<MatchPhrasePrefixQueryDescriptor<T>> selector)
381+
{
382+
var query = new MatchPhrasePrefixQueryDescriptor<T>();
383+
selector(query);
384+
385+
if (string.IsNullOrWhiteSpace(query._Field))
386+
throw new DslException("Field name not set for text_phrase query");
387+
388+
if (query.IsConditionless)
389+
return CreateConditionlessQueryDescriptor(query);
390+
391+
this.MatchQueryDescriptor = new Dictionary<string, object>()
392+
{
393+
{ query._Field, query}
394+
};
395+
return new QueryDescriptor<T> { MatchQueryDescriptor = this.MatchQueryDescriptor };
396+
}
397+
331398
/// <summary>
332399
/// Nested query allows to query nested objects / docs (see nested mapping). The query is executed against the
333400
/// nested objects / docs as if they were indexed as separate docs (they are, internally) and resulting in the

Diff for: src/Nest/Nest.csproj

+3
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@
7777
<Compile Include="DSL\CreateWarmerDescriptor.cs" />
7878
<Compile Include="DSL\PutWarmerDescriptor.cs" />
7979
<Compile Include="DSL\GetWarmerDescriptor.cs" />
80+
<Compile Include="DSL\Query\MatchPhrasePrefixQueryDescriptor.cs" />
81+
<Compile Include="DSL\Query\MatchPhraseQueryDescriptor.cs" />
82+
<Compile Include="DSL\Query\MatchQueryDescriptor.cs" />
8083
<Compile Include="DSL\Query\CustomFiltersScoreDescriptor.cs" />
8184
<Compile Include="DSL\Query\FilterScoreDescriptor.cs" />
8285
<Compile Include="ElasticClient-Warmers.cs" />

0 commit comments

Comments
 (0)