Skip to content

Commit e8d536a

Browse files
committed
Merge pull request #568 from elasticsearch/feature/suggest_endpoint
Implemented _suggest endpoint as .Suggest() fix #397 and fix #518
2 parents 1956d49 + 3c3eb6b commit e8d536a

11 files changed

+264
-8
lines changed

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

-1
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,6 @@ public SearchDescriptor<T> SuggestGlobalText(string globalSuggestText)
870870
return this;
871871
}
872872

873-
874873
/// <summary>
875874
/// The term suggester suggests terms based on edit distance. The provided suggest text is analyzed before terms are suggested.
876875
/// The suggested terms are provided per analyzed suggest text token. The term suggester doesn’t take the query into account that is part of request.

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

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Linq;
5+
using System.Text;
6+
using Elasticsearch.Net;
7+
using Newtonsoft.Json;
8+
using System.Linq.Expressions;
9+
using Nest.Resolvers;
10+
using Nest.Domain;
11+
12+
namespace Nest
13+
{
14+
[DescriptorFor("Suggest")]
15+
public partial class SuggestDescriptor<T> :
16+
IndicesOptionalPathDescriptor<SuggestDescriptor<T>, SuggestRequestParameters>
17+
, IPathInfo<SuggestRequestParameters>
18+
where T : class
19+
{
20+
internal IDictionary<string, object> _Suggest = new Dictionary<string, object>();
21+
22+
/// <summary>
23+
/// To avoid repetition of the suggest text, it is possible to define a global text.
24+
/// </summary>
25+
public SuggestDescriptor<T> GlobalText(string globalSuggestText)
26+
{
27+
this._Suggest.Add("text", globalSuggestText);
28+
return this;
29+
}
30+
31+
/// <summary>
32+
/// The term suggester suggests terms based on edit distance. The provided suggest text is analyzed before terms are suggested.
33+
/// The suggested terms are provided per analyzed suggest text token. The term suggester doesn’t take the query into account that is part of request.
34+
/// </summary>
35+
public SuggestDescriptor<T> Term(string name, Func<TermSuggestDescriptor<T>, TermSuggestDescriptor<T>> suggest)
36+
{
37+
name.ThrowIfNullOrEmpty("name");
38+
suggest.ThrowIfNull("suggest");
39+
var desc = new TermSuggestDescriptor<T>();
40+
var item = suggest(desc);
41+
var bucket = new SuggestDescriptorBucket<T> { _Text = item._Text, TermSuggest = item };
42+
this._Suggest.Add(name, bucket);
43+
return this;
44+
}
45+
46+
/// <summary>
47+
/// The phrase suggester adds additional logic on top of the term suggester to select entire corrected phrases
48+
/// instead of individual tokens weighted based on ngram-langugage models.
49+
/// </summary>
50+
public SuggestDescriptor<T> Phrase(string name, Func<PhraseSuggestDescriptor<T>, PhraseSuggestDescriptor<T>> suggest)
51+
{
52+
name.ThrowIfNullOrEmpty("name");
53+
suggest.ThrowIfNull("suggest");
54+
if (this._Suggest == null)
55+
this._Suggest = new Dictionary<string, object>();
56+
57+
var desc = new PhraseSuggestDescriptor<T>();
58+
var item = suggest(desc);
59+
var bucket = new SuggestDescriptorBucket<T> { _Text = item._Text, PhraseSuggest = item };
60+
this._Suggest.Add(name, bucket);
61+
return this;
62+
}
63+
64+
/// <summary>
65+
/// The completion suggester is a so-called prefix suggester.
66+
/// It does not do spell correction like the term or phrase suggesters but allows basic auto-complete functionality.
67+
/// </summary>
68+
public SuggestDescriptor<T> Completion(string name, Func<CompletionSuggestDescriptor<T>, CompletionSuggestDescriptor<T>> suggest)
69+
{
70+
name.ThrowIfNullOrEmpty("name");
71+
suggest.ThrowIfNull("suggest");
72+
if (this._Suggest == null)
73+
this._Suggest = new Dictionary<string, object>();
74+
75+
var desc = new CompletionSuggestDescriptor<T>();
76+
var item = suggest(desc);
77+
var bucket = new SuggestDescriptorBucket<T> { _Text = item._Text, CompletionSuggest = item };
78+
this._Suggest.Add(name, bucket);
79+
return this;
80+
}
81+
82+
ElasticsearchPathInfo<SuggestRequestParameters> IPathInfo<SuggestRequestParameters>.ToPathInfo(IConnectionSettingsValues settings)
83+
{
84+
var pathInfo = base.ToPathInfo<SuggestRequestParameters>(settings, this._QueryString);
85+
pathInfo.HttpMethod = PathInfoHttpMethod.POST;
86+
87+
return pathInfo;
88+
}
89+
}
90+
}

Diff for: src/Nest/DSL/_Descriptors.generated.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -4788,53 +4788,53 @@ public SnapshotRestoreDescriptor WaitForCompletion(bool wait_for_completion = tr
47884788
///http://www.elasticsearch.org/guide/en/elasticsearch/reference/1.x/search-search.html
47894789
///</pre>
47904790
///</summary>
4791-
public partial class SuggestDescriptor
4791+
public partial class SuggestDescriptor<T>
47924792
{
47934793
internal SuggestRequestParameters _QueryString = new SuggestRequestParameters();
47944794

47954795

47964796
///<summary>Whether specified concrete indices should be ignored when unavailable (missing or closed)</summary>
4797-
public SuggestDescriptor IgnoreUnavailable(bool ignore_unavailable = true)
4797+
public SuggestDescriptor<T> IgnoreUnavailable(bool ignore_unavailable = true)
47984798
{
47994799
this._QueryString.IgnoreUnavailable(ignore_unavailable);
48004800
return this;
48014801
}
48024802

48034803

48044804
///<summary>Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)</summary>
4805-
public SuggestDescriptor AllowNoIndices(bool allow_no_indices = true)
4805+
public SuggestDescriptor<T> AllowNoIndices(bool allow_no_indices = true)
48064806
{
48074807
this._QueryString.AllowNoIndices(allow_no_indices);
48084808
return this;
48094809
}
48104810

48114811

48124812
///<summary>Whether to expand wildcard expression to concrete indices that are open, closed or both.</summary>
4813-
public SuggestDescriptor ExpandWildcards(ExpandWildcardsOptions expand_wildcards)
4813+
public SuggestDescriptor<T> ExpandWildcards(ExpandWildcardsOptions expand_wildcards)
48144814
{
48154815
this._QueryString.ExpandWildcards(expand_wildcards);
48164816
return this;
48174817
}
48184818

48194819

48204820
///<summary>Specify the node or shard the operation should be performed on (default: random)</summary>
4821-
public SuggestDescriptor Preference(string preference)
4821+
public SuggestDescriptor<T> Preference(string preference)
48224822
{
48234823
this._QueryString.Preference(preference);
48244824
return this;
48254825
}
48264826

48274827

48284828
///<summary>Specific routing value</summary>
4829-
public SuggestDescriptor Routing(string routing)
4829+
public SuggestDescriptor<T> Routing(string routing)
48304830
{
48314831
this._QueryString.Routing(routing);
48324832
return this;
48334833
}
48344834

48354835

48364836
///<summary>The URL-encoded request definition (instead of using request body)</summary>
4837-
public SuggestDescriptor Source(string source)
4837+
public SuggestDescriptor<T> Source(string source)
48384838
{
48394839
this._QueryString.Source(source);
48404840
return this;

Diff for: src/Nest/Domain/Responses/SuggestResponse.cs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Newtonsoft.Json;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Nest
6+
{
7+
public interface ISuggestResponse : IResponse
8+
{
9+
ShardsMetaData Shards { get; }
10+
IDictionary<string, Suggest[]> Suggestions { get; set; }
11+
}
12+
13+
[JsonObject]
14+
public class SuggestResponse : BaseResponse, ISuggestResponse
15+
{
16+
public SuggestResponse()
17+
{
18+
this.IsValid = true;
19+
this.Suggestions = new Dictionary<string, Suggest[]>();
20+
}
21+
22+
public ShardsMetaData Shards { get; internal set; }
23+
24+
public IDictionary<string, Suggest[]> Suggestions { get; set;}
25+
}
26+
}

Diff for: src/Nest/ElasticClient-Suggest.cs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using Elasticsearch.Net;
6+
7+
namespace Nest
8+
{
9+
public partial class ElasticClient
10+
{
11+
/// <inheritdoc />
12+
public ISuggestResponse Suggest<T>(Func<SuggestDescriptor<T>, SuggestDescriptor<T>> selector)
13+
where T : class
14+
{
15+
return this.Dispatch<SuggestDescriptor<T>, SuggestRequestParameters, SuggestResponse>(
16+
selector,
17+
(p, d) => this.RawDispatch.SuggestDispatch<SuggestResponse>(p, d._Suggest)
18+
);
19+
}
20+
21+
/// <inheritdoc />
22+
public Task<ISuggestResponse> SuggestAsync<T>(Func<SuggestDescriptor<T>, SuggestDescriptor<T>> selector)
23+
where T : class
24+
{
25+
return this.DispatchAsync<SuggestDescriptor<T>, SuggestRequestParameters, SuggestResponse, ISuggestResponse>(
26+
selector,
27+
(p, d) => this.RawDispatch.SuggestDispatchAsync<SuggestResponse>(p, d._Suggest)
28+
);
29+
}
30+
}
31+
}

Diff for: src/Nest/IElasticClient.cs

+18
Original file line numberDiff line numberDiff line change
@@ -990,5 +990,23 @@ Task<IBulkResponse> IndexManyAsync<T>(IEnumerable<T> objects, string index = nul
990990
/// </summary>
991991
/// <param name="selector">An optional descriptor that further describes the status operation, i.e limiting it to certain indices</param>
992992
Task<IStatusResponse> StatusAsync(Func<IndicesStatusDescriptor, IndicesStatusDescriptor> selector = null);
993+
994+
/// <summary>
995+
/// The suggest feature suggests similar looking terms based on a provided text by using a suggester.
996+
/// <para> </para>http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-status.html
997+
/// </summary>
998+
/// <typeparam name="T">The type used to strongly type parts of the suggest operation</typeparam>
999+
/// <param name="selector">The suggesters to use this operation (can be multiple)</param>
1000+
ISuggestResponse Suggest<T>(Func<SuggestDescriptor<T>, SuggestDescriptor<T>> selector)
1001+
where T : class;
1002+
1003+
/// <summary>
1004+
/// The suggest feature suggests similar looking terms based on a provided text by using a suggester.
1005+
/// <para> </para>http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-status.html
1006+
/// </summary>
1007+
/// <typeparam name="T">The type used to strongly type parts of the suggest operation</typeparam>
1008+
/// <param name="selector">The suggesters to use this operation (can be multiple)</param>
1009+
Task<ISuggestResponse> SuggestAsync<T>(Func<SuggestDescriptor<T>, SuggestDescriptor<T>> selector)
1010+
where T : class;
9931011
}
9941012
}

Diff for: src/Nest/Nest.csproj

+4
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@
138138
<Compile Include="Domain\Bulk\BulkUpdateBody.cs" />
139139
<Compile Include="Domain\Alias\IndexAliases.cs" />
140140
<Compile Include="Domain\Responses\IShardsOperationResponse.cs" />
141+
<Compile Include="Domain\Responses\SuggestResponse.cs" />
141142
<Compile Include="DSL\Aggregations\AggregationDescriptor.cs" />
142143
<Compile Include="DSL\Aggregations\BucketAggregationBaseDescriptor.cs" />
143144
<Compile Include="DSL\Aggregations\DateHistogramAggregationDescriptor.cs" />
@@ -164,6 +165,7 @@
164165
<Compile Include="DSL\Aggregations\SignificantTermsAggregationDescriptor.cs" />
165166
<Compile Include="DSL\Aggregations\TermsAggregationDescriptor.cs" />
166167
<Compile Include="DSL\Aggregations\ValueCountAggregationDescriptor.cs" />
168+
<Compile Include="DSL\SuggestDescriptor.cs" />
167169
<Compile Include="DSL\CloseIndexDescriptor.cs" />
168170
<Compile Include="DSL\ClusterStateDescriptor.cs" />
169171
<Compile Include="DSL\ClearCacheDescriptor.cs" />
@@ -220,6 +222,7 @@
220222
<Compile Include="DSL\ScrollDescriptor.cs" />
221223
<Compile Include="DSL\UpdateSettingsDescriptor.cs" />
222224
<Compile Include="DSL\Paths\IndexOptionalPathDescriptor.cs" />
225+
<Compile Include="ElasticClient-Suggest.cs" />
223226
<Compile Include="ElasticClient-RootNodeInfo.cs" />
224227
<Compile Include="ElasticClient-UpdateSettings.cs" />
225228
<Compile Include="DSL\ISimpleGetDescriptor.cs" />
@@ -668,6 +671,7 @@
668671
<Compile Include="Resolvers\Converters\BulkOperationResponseItemConverter.cs" />
669672
<Compile Include="Resolvers\Converters\AggregationConverter.cs" />
670673
<Compile Include="Resolvers\Converters\IndexSettingsResponseConverter.cs" />
674+
<Compile Include="Resolvers\Converters\SuggestResponseConverter.cs" />
671675
<Compile Include="Resolvers\Converters\PropertyPathMarkerConverter.cs" />
672676
<Compile Include="Resolvers\Converters\DynamicMappingOptionConverter.cs" />
673677
<Compile Include="Resolvers\Converters\DictionaryKeysAreNotPropertyNamesJsonConverter.cs" />
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Elasticsearch.Net;
5+
using Elasticsearch.Net.Connection;
6+
using Nest.Domain;
7+
using Newtonsoft.Json;
8+
using Newtonsoft.Json.Linq;
9+
using System.Reflection;
10+
11+
namespace Nest.Resolvers.Converters
12+
{
13+
14+
public class SuggestResponseConverter : JsonConverter
15+
{
16+
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
17+
{
18+
throw new NotSupportedException();
19+
}
20+
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
21+
{
22+
var response = new SuggestResponse();
23+
var jsonObject = JObject.Load(reader);
24+
foreach (var prop in jsonObject.Properties())
25+
{
26+
if (prop.Name == "_shards")
27+
response.Shards = prop.Value.ToObject<ShardsMetaData>();
28+
else
29+
response.Suggestions.Add(prop.Name, prop.Value.ToObject<Suggest[]>());
30+
}
31+
32+
return response;
33+
}
34+
35+
public override bool CanConvert(Type objectType)
36+
{
37+
return true;
38+
}
39+
}
40+
}

Diff for: src/Nest/Resolvers/ElasticContractResolver.cs

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ protected override JsonContract CreateContract(Type objectType)
7272
if (objectType == typeof(PropertyPathMarker))
7373
contract.Converter = new PropertyPathMarkerConverter(this.ConnectionSettings);
7474

75+
if (objectType == typeof(SuggestResponse))
76+
contract.Converter = new SuggestResponseConverter();
77+
7578
if (objectType == typeof(MultiSearchResponse))
7679
contract.Converter = new MultiSearchConverter();
7780

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System.Linq;
2+
using FluentAssertions;
3+
using NUnit.Framework;
4+
using Nest.Tests.MockData.Domain;
5+
using Nest.Resolvers;
6+
using Elasticsearch.Net;
7+
8+
namespace Nest.Tests.Integration.Core.Suggest
9+
{
10+
[TestFixture]
11+
public class SuggestTests : IntegrationTests
12+
{
13+
[Test]
14+
public void TestSuggest()
15+
{
16+
var country = this._client.Search<ElasticsearchProject>(s => s.Size(1)).Documents.First().Country;
17+
var wrongCountry = country + "x";
18+
19+
var suggestResults = ((ElasticClient)_client).Suggest<ElasticsearchProject>(s => s
20+
.Term("mySuggest", m => m
21+
.SuggestMode(SuggestMode.Always)
22+
.Text(wrongCountry)
23+
.Size(1)
24+
.OnField("country")
25+
)
26+
);
27+
28+
suggestResults.IsValid.Should().BeTrue();
29+
30+
suggestResults.Shards.Should().NotBeNull();
31+
suggestResults.Suggestions.Should().NotBeNull().And.HaveCount(1);
32+
var suggestions = suggestResults.Suggestions["mySuggest"];
33+
suggestions.Should().NotBeNull().And.NotBeEmpty();
34+
35+
var suggestion = suggestions.First();
36+
suggestion.Text.Should().Be(wrongCountry);
37+
var option = suggestion.Options.First();
38+
option.Text.Should().Be(country);
39+
40+
}
41+
42+
43+
}
44+
}

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

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
<Compile Include="Connection\Thrift\ThiftBugReportTests.cs" />
100100
<Compile Include="Core\Bulk\BulkTests.cs" />
101101
<Compile Include="Core\Bulk\BulkUpdateTests.cs" />
102+
<Compile Include="Core\Suggest\SuggestTests.cs" />
102103
<Compile Include="Core\MultiSearch\MultiSearchTests.cs" />
103104
<Compile Include="DebugTests\MemoryUsageTests.cs" />
104105
<Compile Include="ElasticsearchConfiguration.cs" />

0 commit comments

Comments
 (0)