Skip to content

Commit bcc2eb2

Browse files
committed
Merge pull request #1377 from elastic/feature/mlt-updates
Feature/mlt updates
2 parents e08e6d5 + ef0a8bb commit bcc2eb2

File tree

7 files changed

+190
-15
lines changed

7 files changed

+190
-15
lines changed

src/Nest/DSL/MultiGet/IMultiGetOperation.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,20 @@ public interface IMultiGetOperation
2424

2525
[JsonProperty(PropertyName = "_source")]
2626
ISourceFilter Source { get; set; }
27-
27+
28+
// Only used for the MLT query for specifying an artificial document.
29+
// TODO: For 2.0, we should consider decoupling IMultiGetOperation from
30+
// MoreLikeThisQuery and have a dedicatd MoreLikeThisDocument object.
31+
[JsonProperty(PropertyName = "doc")]
32+
object Document { get; set; }
33+
34+
// Only used for the MLT query for providing a different analyzer per
35+
// artificial document field.
36+
// TODO: For 2.0, we should consider decoupling IMultiGetOperation from
37+
// MoreLikeThisQuery and have a dedicatd MoreLikeThisDocument object.
38+
[JsonProperty(PropertyName = "per_field_analyzer")]
39+
IDictionary<PropertyPathMarker, string> PerFieldAnalyzer { get; set; }
40+
2841
Type ClrType { get; }
2942
}
3043
}

src/Nest/DSL/MultiGet/MultiGetOperationDescriptor.cs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public MultiGetOperation(string id)
1515
this.Index = typeof(T);
1616
this.Type = typeof(T);
1717
}
18+
1819
public MultiGetOperation(long id) : this(id.ToString(CultureInfo.InvariantCulture)) {}
1920

2021
Type IMultiGetOperation.ClrType { get { return typeof(T); } }
@@ -30,6 +31,10 @@ public MultiGetOperation(long id) : this(id.ToString(CultureInfo.InvariantCultur
3031
public ISourceFilter Source { get; set; }
3132

3233
public string Routing { get; set; }
34+
35+
public object Document { get; set; }
36+
37+
public IDictionary<PropertyPathMarker, string> PerFieldAnalyzer { get; set; }
3338
}
3439

3540
public class MultiGetOperationDescriptor<T> : IMultiGetOperation
@@ -43,6 +48,8 @@ public class MultiGetOperationDescriptor<T> : IMultiGetOperation
4348
string IMultiGetOperation.Routing { get; set; }
4449
ISourceFilter IMultiGetOperation.Source { get; set; }
4550
IList<PropertyPathMarker> IMultiGetOperation.Fields { get; set; }
51+
object IMultiGetOperation.Document { get; set; }
52+
IDictionary<PropertyPathMarker, string> IMultiGetOperation.PerFieldAnalyzer { get; set; }
4653
Type IMultiGetOperation.ClrType { get { return typeof(T); } }
4754

4855
public MultiGetOperationDescriptor()
@@ -59,7 +66,8 @@ public MultiGetOperationDescriptor()
5966
/// </pre>
6067
/// </summary>
6168
/// <param name="initializeEmpty"></param>
62-
public MultiGetOperationDescriptor(bool allowExplicitIndex) : this()
69+
public MultiGetOperationDescriptor(bool allowExplicitIndex)
70+
: this()
6371
{
6472
if (allowExplicitIndex) return;
6573
Self.Index = null;
@@ -85,7 +93,7 @@ public MultiGetOperationDescriptor<T> Type(string type)
8593
return this;
8694
}
8795

88-
96+
8997
/// <summary>
9098
/// Manually set the type of which a typename will be inferred
9199
/// </summary>
@@ -140,7 +148,7 @@ public MultiGetOperationDescriptor<T> Routing(string routing)
140148
/// </summary>
141149
public MultiGetOperationDescriptor<T> Fields(params Expression<Func<T, object>>[] expressions)
142150
{
143-
((IMultiGetOperation) this).Fields = expressions.Select(e => (PropertyPathMarker) e).ToList();
151+
Self.Fields = expressions.Select(e => (PropertyPathMarker)e).ToList();
144152
return this;
145153
}
146154

@@ -150,7 +158,38 @@ public MultiGetOperationDescriptor<T> Fields(params Expression<Func<T, object>>[
150158
/// </summary>
151159
public MultiGetOperationDescriptor<T> Fields(params string[] fields)
152160
{
153-
((IMultiGetOperation) this).Fields = fields.Select(f => (PropertyPathMarker) f).ToList();
161+
Self.Fields = fields.Select(f => (PropertyPathMarker)f).ToList();
162+
return this;
163+
}
164+
165+
// Only used for the MLT query for specifying an artificial document.
166+
// TODO: For 2.0, we should consider decoupling IMultiGetOperation from
167+
// MoreLikeThisQuery and have a dedicatd MoreLikeThisDocument object.
168+
public MultiGetOperationDescriptor<T> Document(T document)
169+
{
170+
Self.Document = document;
171+
return this;
172+
}
173+
174+
// Only used for the MLT query for providing a different analyzer per
175+
// artificial document field.
176+
// TODO: For 2.0, we should consider decoupling IMultiGetOperation from
177+
// MoreLikeThisQuery and have a dedicatd MoreLikeThisDocument object.
178+
public MultiGetOperationDescriptor<T> PerFieldAnalyzer(Func<FluentDictionary<Expression<Func<T, object>>, string>, FluentDictionary<Expression<Func<T, object>>, string>> analyzerSelector)
179+
{
180+
var d = new FluentDictionary<Expression<Func<T, object>>, string>();
181+
analyzerSelector(d);
182+
Self.PerFieldAnalyzer = d.ToDictionary(x => PropertyPathMarker.Create(x.Key), x => x.Value);
183+
return this;
184+
}
185+
186+
// Only used for the MLT query for providing a different analyzer per
187+
// artificial document field.
188+
// TODO: For 2.0, we should consider decoupling IMultiGetOperation from
189+
// MoreLikeThisQuery and have a dedicatd MoreLikeThisDocument object.
190+
public MultiGetOperationDescriptor<T> PerFieldAnalyzer(Func<FluentDictionary<PropertyPathMarker, string>, FluentDictionary<PropertyPathMarker, string>> analyzerSelector)
191+
{
192+
Self.PerFieldAnalyzer = analyzerSelector(new FluentDictionary<PropertyPathMarker, string>());
154193
return this;
155194
}
156195
}

src/Nest/DSL/Query/MoreLikeThisQueryDescriptor.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public interface IMoreLikeThisQuery : IQuery
2121
[JsonProperty(PropertyName = "percent_terms_to_match")]
2222
double? TermMatchPercentage { get; set; }
2323

24+
[JsonProperty(PropertyName = "minimum_should_match")]
25+
string MinimumShouldMatch { get; set; }
26+
2427
[JsonProperty(PropertyName = "stop_words")]
2528
IEnumerable<string> StopWords { get; set; }
2629

@@ -84,6 +87,7 @@ protected override void WrapInContainer(IQueryContainer container)
8487
public IEnumerable<PropertyPathMarker> Fields { get; set; }
8588
public string LikeText { get; set; }
8689
public double? TermMatchPercentage { get; set; }
90+
public string MinimumShouldMatch { get; set; }
8791
public IEnumerable<string> StopWords { get; set; }
8892
public int? MinTermFrequency { get; set; }
8993
public int? MaxQueryTerms { get; set; }
@@ -108,7 +112,9 @@ public class MoreLikeThisQueryDescriptor<T> : IMoreLikeThisQuery where T : class
108112
string IMoreLikeThisQuery.LikeText { get; set; }
109113

110114
double? IMoreLikeThisQuery.TermMatchPercentage { get; set; }
111-
115+
116+
string IMoreLikeThisQuery.MinimumShouldMatch { get; set; }
117+
112118
IEnumerable<string> IMoreLikeThisQuery.StopWords { get; set; }
113119

114120
int? IMoreLikeThisQuery.MinTermFrequency { get; set; }
@@ -212,6 +218,19 @@ public MoreLikeThisQueryDescriptor<T> TermMatchPercentage(double termMatchPercen
212218
this.Self.TermMatchPercentage = termMatchPercentage;
213219
return this;
214220
}
221+
222+
public MoreLikeThisQueryDescriptor<T> MinimumShouldMatch(string minMatch)
223+
{
224+
this.Self.MinimumShouldMatch = minMatch;
225+
return this;
226+
}
227+
228+
public MoreLikeThisQueryDescriptor<T> MinimumShouldMatch(int minMatch)
229+
{
230+
this.Self.MinimumShouldMatch = minMatch.ToString();
231+
return this;
232+
}
233+
215234
public MoreLikeThisQueryDescriptor<T> Boost(double boost)
216235
{
217236
this.Self.Boost = boost;

src/Nest/DSL/Query/SubDescriptors/MoreLikeThisQueryDocumentsDescriptor.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,27 @@ public MoreLikeThisQueryDocumentsDescriptor<T> Get<TDocument>(string id, Func<Mu
5757
return this;
5858

5959
}
60+
61+
public MoreLikeThisQueryDocumentsDescriptor<T> Get<TDocument>(Func<MultiGetOperationDescriptor<TDocument>, MultiGetOperationDescriptor<TDocument>> getSelector)
62+
where TDocument : class
63+
{
64+
getSelector = getSelector ?? (s => s);
65+
var descriptor = getSelector(new MultiGetOperationDescriptor<TDocument>(_allowExplicitIndex));
66+
this.GetOperations.Add(descriptor);
67+
return this;
68+
}
69+
70+
public MoreLikeThisQueryDocumentsDescriptor<T> Document<TDocument>(TDocument document, string index = null, string type = null)
71+
where TDocument : class
72+
{
73+
var descriptor = new MultiGetOperationDescriptor<TDocument>(_allowExplicitIndex)
74+
.Document(document);
75+
if (!string.IsNullOrEmpty(index))
76+
descriptor.Index(index);
77+
if (!string.IsNullOrEmpty(type))
78+
descriptor.Type(type);
79+
this.GetOperations.Add(descriptor);
80+
return this;
81+
}
6082
}
6183
}

src/Nest/DSL/TermVectorDescriptor.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using Elasticsearch.Net;
55
using Newtonsoft.Json;
6+
using System.Linq.Expressions;
67

78
namespace Nest
89
{
@@ -13,6 +14,9 @@ public interface ITermvectorRequest : IDocumentOptionalPath<TermvectorRequestPar
1314
/// </summary>
1415
[JsonProperty("doc")]
1516
object Document { get; set; }
17+
18+
[JsonProperty("per_field_analyzer")]
19+
IDictionary<PropertyPathMarker, string> PerFieldAnalyzer { get; set; }
1620
}
1721

1822
public interface ITermvectorRequest<T> : ITermvectorRequest where T : class { }
@@ -35,13 +39,17 @@ protected override void UpdatePathInfo(IConnectionSettingsValues settings, Elast
3539
}
3640

3741
public object Document { get; set; }
42+
43+
public IDictionary<PropertyPathMarker, string> PerFieldAnalyzer { get; set; }
3844
}
3945

4046
public partial class TermvectorRequest<T> : DocumentOptionalPathBase<TermvectorRequestParameters, T>, ITermvectorRequest<T>
4147
where T : class
4248
{
4349
object ITermvectorRequest.Document { get; set; }
4450

51+
IDictionary<PropertyPathMarker, string> ITermvectorRequest.PerFieldAnalyzer { get; set; }
52+
4553
public TermvectorRequest(string id) : base(id) { }
4654

4755
public TermvectorRequest(long id) : base(id) { }
@@ -58,14 +66,30 @@ public partial class TermvectorDescriptor<T> : DocumentOptionalPathDescriptor<Te
5866
, ITermvectorRequest
5967
where T : class
6068
{
69+
private ITermvectorRequest Self { get { return this; } }
70+
6171
object ITermvectorRequest.Document { get; set; }
6272

73+
IDictionary<PropertyPathMarker, string> ITermvectorRequest.PerFieldAnalyzer { get; set; }
74+
6375
public TermvectorDescriptor<T> Document<TDocument>(TDocument document) where TDocument : class
6476
{
65-
((ITermvectorRequest) this).Document = document;
77+
Self.Document = document;
78+
return this;
79+
}
80+
public TermvectorDescriptor<T> PerFieldAnalyzer(Func<FluentDictionary<Expression<Func<T, object>>, string>, FluentDictionary<Expression<Func<T, object>>, string>> analyzerSelector)
81+
{
82+
var d = new FluentDictionary<Expression<Func<T, object>>, string>();
83+
analyzerSelector(d);
84+
Self.PerFieldAnalyzer = d.ToDictionary(x => PropertyPathMarker.Create(x.Key), x => x.Value);
6685
return this;
6786
}
6887

88+
public TermvectorDescriptor<T> PerFieldAnalyzer(Func<FluentDictionary<PropertyPathMarker, string>, FluentDictionary<PropertyPathMarker, string>> analyzerSelector)
89+
{
90+
Self.PerFieldAnalyzer = analyzerSelector(new FluentDictionary<PropertyPathMarker, string>());
91+
return this;
92+
}
6993
protected override void UpdatePathInfo(IConnectionSettingsValues settings, ElasticsearchPathInfo<TermvectorRequestParameters> pathInfo)
7094
{
7195
TermvectorPathInfo.Update(settings, pathInfo, this);

src/Tests/Nest.Tests.Integration/Core/TermVectors/TermVectorTests.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,23 @@ public void TermVectorDocument()
3333
AssertContentsVectors(result);
3434
}
3535

36+
[Test]
37+
[SkipVersion("0 - 1.4.9", "Per field analyzers added in ES 1.5")]
38+
public void TermVectorDocumentWithPerFieldAnalyzers()
39+
{
40+
var document = NestTestData.Data.FirstOrDefault(d => d.Id == 1);
41+
var result = Client.TermVector<ElasticsearchProject>(s => s
42+
.Document(document)
43+
.Fields(ep => ep.Content)
44+
.PerFieldAnalyzer(pfa => pfa
45+
.Add(p => p.Name, "keyword")
46+
.Add(p => p.Country, "simple")
47+
)
48+
);
49+
50+
AssertContentsVectors(result);
51+
}
52+
3653
private static void AssertContentsVectors(ITermVectorResponse result)
3754
{
3855
result.IsValid.Should().BeTrue();

src/Tests/Nest.Tests.Unit/Search/Query/Singles/MoreLikeThisQueryJson.cs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ namespace Nest.Tests.Unit.Search.Query.Singles
66
[TestFixture]
77
public class MoreLikeThisQueryJson
88
{
9+
public class MoreLikeThisTestDoc
10+
{
11+
public string Name { get; set; }
12+
public string Text { get; set; }
13+
}
14+
915
[Test]
1016
public void TestMoreLikeThisQuery()
1117
{
@@ -28,7 +34,7 @@ public void TestMoreLikeThisQuery()
2834
}}}";
2935
Assert.True(json.JsonEquals(expected), json);
3036
}
31-
37+
3238
[Test]
3339
public void MoreLikeThisWithIds()
3440
{
@@ -43,15 +49,23 @@ public void MoreLikeThisWithIds()
4349
}";
4450
Assert.True(json.JsonEquals(expected), json);
4551
}
46-
52+
4753
[Test]
4854
public void MoreLikeThisWithDocuments()
4955
{
5056
var s = new MoreLikeThisQueryDescriptor<ElasticsearchProject>()
5157
.OnFields(p => p.Name)
52-
.Documents(d=>d
53-
.Get(1, g=>g.Fields(p=>p.Product.Name).Routing("routing_value"))
54-
.Get<Person>("some-string-id", g=>g.Routing("routing_value").Type("people").Index("different_index"))
58+
.Documents(d => d
59+
.Get(1, g => g.Fields(p => p.Product.Name).Routing("routing_value"))
60+
.Get<Person>("some-string-id", g => g.Routing("routing_value").Type("people").Index("different_index"))
61+
.Get<MoreLikeThisTestDoc>(g => g
62+
.Document(new MoreLikeThisTestDoc { Name = "elasticsearch", Text = "foo" })
63+
.PerFieldAnalyzer(pfa => pfa
64+
.Add(p => p.Name, "keyword")
65+
)
66+
)
67+
.Document<MoreLikeThisTestDoc>(new MoreLikeThisTestDoc { Name = "nest" })
68+
.Document<MoreLikeThisTestDoc>(new MoreLikeThisTestDoc { Name = "foo" }, "myindex", "mytype")
5569
);
5670
var json = TestElasticClient.Serialize(s);
5771

@@ -72,6 +86,31 @@ public void MoreLikeThisWithDocuments()
7286
_type: ""people"",
7387
_id: ""some-string-id"",
7488
_routing: ""routing_value""
89+
},
90+
{
91+
_index: ""nest_test_data"",
92+
_type: ""morelikethistestdoc"",
93+
doc: {
94+
name: ""elasticsearch"",
95+
text: ""foo""
96+
},
97+
per_field_analyzer: {
98+
name: ""keyword""
99+
}
100+
},
101+
{
102+
_index: ""nest_test_data"",
103+
_type: ""morelikethistestdoc"",
104+
doc: {
105+
name: ""nest""
106+
}
107+
},
108+
{
109+
_index: ""myindex"",
110+
_type: ""mytype"",
111+
doc: {
112+
name: ""foo""
113+
}
75114
}]
76115
}";
77116
Assert.True(json.JsonEquals(expected), json);
@@ -82,9 +121,9 @@ public void MoreLikeThisWithDocumentsExplicit()
82121
{
83122
var s = new MoreLikeThisQueryDescriptor<ElasticsearchProject>()
84123
.OnFields(p => p.Name)
85-
.DocumentsExplicit(d=>d
86-
.Get(1, g=>g.Fields(p=>p.Product.Name).Routing("routing_value"))
87-
.Get<Person>("some-string-id", g=>g.Routing("routing_value").Type("people").Index("different_index"))
124+
.DocumentsExplicit(d => d
125+
.Get(1, g => g.Fields(p => p.Product.Name).Routing("routing_value"))
126+
.Get<Person>("some-string-id", g => g.Routing("routing_value").Type("people").Index("different_index"))
88127
);
89128
var json = TestElasticClient.Serialize(s);
90129

@@ -130,6 +169,7 @@ public void TestMoreLikeThisAllQuery()
130169
.StopWords(new[] { "thou", "shall" })
131170
.BoostTerms(1.4)
132171
.TermMatchPercentage(12)
172+
.MinimumShouldMatch("30%")
133173
.Boost(1.1)
134174
.Analyzer("my_analyzer")
135175
)
@@ -140,6 +180,7 @@ public void TestMoreLikeThisAllQuery()
140180
fields : [""name"" ],
141181
like_text : ""elasticsearcc"",
142182
percent_terms_to_match: 12.0,
183+
minimum_should_match: ""30%"",
143184
stop_words: [
144185
""thou"",
145186
""shall""

0 commit comments

Comments
 (0)