Skip to content

Commit 111956e

Browse files
committed
Move HighlightQuery to HighlightField
More complete integration test for highlighting Closes #1789
1 parent 9248dff commit 111956e

File tree

5 files changed

+203
-38
lines changed

5 files changed

+203
-38
lines changed

Diff for: src/Nest/Search/Search/Highlighting/Highlight.cs

-8
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ public interface IHighlight
4545

4646
[JsonProperty("boundary_chars")]
4747
string BoundaryChars { get; set; }
48-
49-
[JsonProperty("highlight_query")]
50-
QueryContainer HighlightQuery { get; set; }
5148
}
5249

5350
public class Highlight : IHighlight
@@ -64,7 +61,6 @@ public class Highlight : IHighlight
6461
public Dictionary<Field, IHighlightField> Fields { get; set; }
6562
public bool? RequireFieldMatch { get; set; }
6663
public string BoundaryChars { get; set; }
67-
public QueryContainer HighlightQuery { get; set; }
6864
}
6965

7066
public class HighlightDescriptor<T> : DescriptorBase<HighlightDescriptor<T> ,IHighlight>, IHighlight
@@ -83,7 +79,6 @@ public class HighlightDescriptor<T> : DescriptorBase<HighlightDescriptor<T> ,IHi
8379
Dictionary<Field, IHighlightField> IHighlight.Fields { get; set; }
8480
bool? IHighlight.RequireFieldMatch { get; set; }
8581
string IHighlight.BoundaryChars { get; set; }
86-
QueryContainer IHighlight.HighlightQuery { get; set; }
8782

8883
public HighlightDescriptor<T> Fields(params Func<HighlightFieldDescriptor<T>, IHighlightField>[] fieldHighlighters) =>
8984
Assign(a => a.Fields = fieldHighlighters?
@@ -120,8 +115,5 @@ public HighlightDescriptor<T> Fields(params Func<HighlightFieldDescriptor<T>, IH
120115
public HighlightDescriptor<T> BoundaryCharacters(string boundaryCharacters) => Assign(a => a.BoundaryChars = boundaryCharacters);
121116

122117
public HighlightDescriptor<T> BoundaryMaxSize(int boundaryMaxSize) => Assign(a => a.BoundaryMaxSize = boundaryMaxSize);
123-
124-
public HighlightDescriptor<T> HighlightQuery(Func<QueryContainerDescriptor<T>, QueryContainer> querySelector) =>
125-
Assign(a => a.HighlightQuery = querySelector?.InvokeQuery(new QueryContainerDescriptor<T>()));
126118
}
127119
}

Diff for: src/Nest/Search/Search/Highlighting/HighlightField.cs

+9
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ public interface IHighlightField
5555

5656
[JsonProperty("matched_fields")]
5757
Fields MatchedFields { get; set; }
58+
59+
[JsonProperty("highlight_query")]
60+
QueryContainer HighlightQuery { get; set; }
5861
}
5962

6063
public class HighlightField : IHighlightField
@@ -75,6 +78,7 @@ public class HighlightField : IHighlightField
7578
public HighlighterType? Type { get; set; }
7679
public bool? ForceSource { get; set; }
7780
public Fields MatchedFields { get; set; }
81+
public QueryContainer HighlightQuery { get; set; }
7882
}
7983

8084
public class HighlightFieldDescriptor<T> : DescriptorBase<HighlightFieldDescriptor<T>,IHighlightField>, IHighlightField
@@ -112,6 +116,8 @@ public class HighlightFieldDescriptor<T> : DescriptorBase<HighlightFieldDescript
112116

113117
Fields IHighlightField.MatchedFields { get; set; }
114118

119+
QueryContainer IHighlightField.HighlightQuery { get; set; }
120+
115121
public HighlightFieldDescriptor<T> Field(Field field) => Assign(a => a.Field = field);
116122
public HighlightFieldDescriptor<T> Field(Expression<Func<T, object>> objectPath) => Assign(a => a.Field = objectPath);
117123

@@ -151,5 +157,8 @@ public class HighlightFieldDescriptor<T> : DescriptorBase<HighlightFieldDescript
151157

152158
public HighlightFieldDescriptor<T> MatchedFields(Func<FieldsDescriptor<T>, IPromise<Fields>> fields) =>
153159
Assign(a => a.MatchedFields = fields?.Invoke(new FieldsDescriptor<T>())?.Value);
160+
161+
public HighlightFieldDescriptor<T> HighlightQuery(Func<QueryContainerDescriptor<T>, QueryContainer> querySelector) =>
162+
Assign(a => a.HighlightQuery = querySelector?.InvokeQuery(new QueryContainerDescriptor<T>()));
154163
}
155164
}

Diff for: src/Nest/Search/Search/Highlighting/HighlighterType.cs

+21
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,34 @@
44

55
namespace Nest
66
{
7+
/// <summary>
8+
/// Type of highlighter
9+
/// </summary>
710
[JsonConverter(typeof(StringEnumConverter))]
811
public enum HighlighterType
912
{
13+
/// <summary>
14+
/// Plain Highlighter.
15+
/// The default choice of highlighter is of type plain and uses the Lucene highlighter.
16+
/// It tries hard to reflect the query matching logic in terms of understanding word
17+
/// importance and any word positioning criteria in phrase queries.
18+
/// </summary>
1019
[EnumMember(Value = "plain")]
1120
Plain,
21+
22+
/// <summary>
23+
/// Postings Highlighter.
24+
/// If index_options is set to offsets in the mapping the postings highlighter
25+
/// will be used instead of the plain highlighter
26+
/// </summary>
1227
[EnumMember(Value = "postings")]
1328
Postings,
29+
30+
/// <summary>
31+
/// Fast Vector Highlighter.
32+
/// If term_vector information is provided by setting term_vector to with_positions_offsets
33+
/// in the mapping then the fast vector highlighter will be used instead of the plain highlighter
34+
/// </summary>
1435
[EnumMember(Value = "fvh")]
1536
Fvh
1637
}

Diff for: src/Tests/Framework/Integration/Bootstrappers/Seeder.cs

+10-2
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,21 @@ private void CreateProjectIndex()
132132
public static TypeMappingDescriptor<Project> MapProject(TypeMappingDescriptor<Project> m) => m
133133
.Properties(props => props
134134
.String(s => s
135-
.Name(p => p.Name).NotAnalyzed()
135+
.Name(p => p.Name)
136+
.NotAnalyzed()
136137
.Fields(fs => fs
138+
.String(ss => ss.Name("standard").Analyzer("standard"))
137139
.Completion(cm => cm.Name("suggest"))
138140
)
139141
)
140142
.Date(d => d.Name(p => p.StartedOn))
141-
.String(d => d.Name(p => p.State).NotAnalyzed())
143+
.String(d => d
144+
.Name(p => p.State)
145+
.NotAnalyzed()
146+
.Fields(fs => fs
147+
.String(st => st.Name("offsets").IndexOptions(IndexOptions.Offsets))
148+
)
149+
)
142150
.Nested<Tag>(mo => mo
143151
.Name(p => p.Tags)
144152
.Properties(TagProperties)

Diff for: src/Tests/Search/Request/HighlightingUsageTests.cs

+163-28
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
4+
using Bogus;
35
using Nest;
46
using Tests.Framework.Integration;
57
using Tests.Framework.MockData;
8+
using FluentAssertions;
9+
using Newtonsoft.Json.Linq;
10+
using Xunit;
611

712
namespace Tests.Search.Request
813
{
@@ -12,77 +17,207 @@ public HighlightingUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : ba
1217

1318
protected override object ExpectJson => new
1419
{
20+
query = new
21+
{
22+
match = new JObject
23+
{
24+
{ "name.standard", new JObject
25+
{
26+
{ "query", "Upton Sons Shield Rice Rowe Roberts" }
27+
}
28+
}
29+
}
30+
},
1531
highlight = new
1632
{
1733
pre_tags = new[] { "<tag1>" },
1834
post_tags = new[] { "</tag1>" },
19-
fields = new
35+
fields = new JObject
2036
{
21-
name = new
22-
{
23-
type = "plain",
24-
force_source = true,
25-
fragment_size = 150,
26-
number_of_fragments = 3,
27-
no_match_size = 150
37+
{ "name.standard", new JObject
38+
{
39+
{ "type", "plain" },
40+
{ "force_source", true},
41+
{ "fragment_size", 150 },
42+
{ "number_of_fragments", 3},
43+
{ "no_match_size", 150 }
44+
}
45+
},
46+
{ "leadDeveloper.firstName", new JObject
47+
{
48+
{ "type", "fvh" },
49+
{ "pre_tags", new JArray { "<name>" } },
50+
{ "post_tags", new JArray { "</name>" } },
51+
{ "highlight_query", new JObject
52+
{
53+
{ "match", new JObject
54+
{
55+
{ "leadDeveloper.firstName", new JObject
56+
{
57+
{ "query", "Kurt Edgardo Naomi Dariana Justice Felton" }
58+
}
59+
}
60+
}
61+
}
62+
}
63+
}
64+
}
2865
},
29-
leadDeveloper = new { type = "fvh" },
30-
tags = new { type = "postings" }
66+
{ "state.offsets", new JObject
67+
{
68+
{ "type", "postings" },
69+
{ "pre_tags", new JArray { "<state>" } },
70+
{ "post_tags", new JArray { "</state>" } },
71+
{ "highlight_query", new JObject
72+
{
73+
{ "terms", new JObject
74+
{
75+
{ "state.offsets", new JArray { "stable" , "bellyup" } }
76+
}
77+
}
78+
}
79+
}
80+
}
81+
}
3182
}
3283
}
3384
};
3485

3586
protected override Func<SearchDescriptor<Project>, ISearchRequest> Fluent => s => s
87+
.Query(q => q
88+
.Match(m => m
89+
.Field(f => f.Name.Suffix("standard"))
90+
.Query("Upton Sons Shield Rice Rowe Roberts")
91+
)
92+
)
3693
.Highlight(h => h
3794
.PreTags("<tag1>")
3895
.PostTags("</tag1>")
3996
.Fields(
4097
fs => fs
41-
.Field(p => p.Name)
98+
.Field(p => p.Name.Suffix("standard"))
4299
.Type(HighlighterType.Plain)
43100
.ForceSource()
44101
.FragmentSize(150)
45102
.NumberOfFragments(3)
46103
.NoMatchSize(150),
47104
fs => fs
48-
.Field(p => p.LeadDeveloper)
49-
.Type(HighlighterType.Fvh),
105+
.Field(p => p.LeadDeveloper.FirstName)
106+
.Type(HighlighterType.Fvh)
107+
.PreTags("<name>")
108+
.PostTags("</name>")
109+
.HighlightQuery(q => q
110+
.Match(m => m
111+
.Field(p => p.LeadDeveloper.FirstName)
112+
.Query("Kurt Edgardo Naomi Dariana Justice Felton")
113+
)
114+
),
50115
fs => fs
51-
.Field(p => p.Tags)
116+
.Field(p => p.State.Suffix("offsets"))
52117
.Type(HighlighterType.Postings)
118+
.PreTags("<state>")
119+
.PostTags("</state>")
120+
.HighlightQuery(q => q
121+
.Terms(t => t
122+
.Field(f => f.State.Suffix("offsets"))
123+
.Terms(
124+
StateOfBeing.Stable.ToString().ToLowerInvariant(),
125+
StateOfBeing.BellyUp.ToString().ToLowerInvariant()
126+
)
127+
)
128+
)
53129
)
54130
);
55131

56132
protected override SearchRequest<Project> Initializer =>
57133
new SearchRequest<Project>
58134
{
135+
Query = new MatchQuery
136+
{
137+
Query = "Upton Sons Shield Rice Rowe Roberts",
138+
Field = "name.standard"
139+
},
59140
Highlight = new Highlight
60141
{
61142
PreTags = new[] { "<tag1>" },
62143
PostTags = new[] { "</tag1>" },
63144
Fields = new Dictionary<Field, IHighlightField>
64145
{
65-
{ "name", new HighlightField
66-
{
67-
Type = HighlighterType.Plain,
68-
ForceSource = true,
69-
FragmentSize = 150,
70-
NumberOfFragments = 3,
71-
NoMatchSize = 150
72-
}
73-
},
74-
{ "leadDeveloper", new HighlightField
146+
{ "name.standard", new HighlightField
147+
{
148+
Type = HighlighterType.Plain,
149+
ForceSource = true,
150+
FragmentSize = 150,
151+
NumberOfFragments = 3,
152+
NoMatchSize = 150
153+
}
154+
},
155+
{ "leadDeveloper.firstName", new HighlightField
156+
{
157+
Type = HighlighterType.Fvh,
158+
PreTags = new[] { "<name>"},
159+
PostTags = new[] { "</name>"},
160+
HighlightQuery = new MatchQuery
75161
{
76-
Type = HighlighterType.Fvh,
162+
Field = "leadDeveloper.firstName",
163+
Query = "Kurt Edgardo Naomi Dariana Justice Felton"
77164
}
78-
},
79-
{ "tags", new HighlightField
165+
}
166+
},
167+
{ "state.offsets", new HighlightField
168+
{
169+
Type = HighlighterType.Postings,
170+
PreTags = new[] { "<state>"},
171+
PostTags = new[] { "</state>"},
172+
HighlightQuery = new TermsQuery
80173
{
81-
Type = HighlighterType.Postings
174+
Field = "state.offsets",
175+
Terms = new [] { "stable", "bellyup" }
82176
}
83177
}
178+
}
84179
}
85180
}
86181
};
182+
183+
protected override void ExpectResponse(ISearchResponse<Project> response)
184+
{
185+
response.IsValid.Should().BeTrue();
186+
187+
foreach (var highlightsByDocumentId in response.Highlights)
188+
{
189+
foreach (var highlightHit in highlightsByDocumentId.Value)
190+
{
191+
if (highlightHit.Key == "name.standard")
192+
{
193+
foreach (var highlight in highlightHit.Value.Highlights)
194+
{
195+
highlight.Should().Contain("<tag1>");
196+
highlight.Should().Contain("</tag1>");
197+
}
198+
}
199+
else if (highlightHit.Key == "leadDeveloper.firstName")
200+
{
201+
foreach (var highlight in highlightHit.Value.Highlights)
202+
{
203+
highlight.Should().Contain("<name>");
204+
highlight.Should().Contain("</name>");
205+
}
206+
}
207+
else if (highlightHit.Key == "state.offsets")
208+
{
209+
foreach (var highlight in highlightHit.Value.Highlights)
210+
{
211+
highlight.Should().Contain("<state>");
212+
highlight.Should().Contain("</state>");
213+
}
214+
}
215+
else
216+
{
217+
Assert.True(false, $"highlights contains unexpected key {highlightHit.Key}");
218+
}
219+
}
220+
}
221+
}
87222
}
88223
}

0 commit comments

Comments
 (0)