/
SearchResult.cs
259 lines (241 loc) · 11.1 KB
/
SearchResult.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Core.Serialization;
#pragma warning disable SA1402 // File may only contain a single type
namespace Azure.Search.Documents.Models
{
// Hide the untyped SearchResult
[CodeGenModel("SearchResult")]
internal partial class SearchResult { }
/// <summary>
/// Contains a document found by a search query, plus associated metadata.
/// </summary>
/// <typeparam name="T">
/// The .NET type that maps to the index schema. Instances of this type can
/// be retrieved as documents from the index.
/// </typeparam>
public class SearchResult<T>
{
/// <summary>
/// The relevance score of the document compared to other documents
/// returned by the query.
/// </summary>
public double? Score { get; internal set; }
/// <summary>
/// Text fragments from the document that indicate the matching search
/// terms, organized by each applicable field; null if hit highlighting
/// was not enabled for the query.
/// </summary>
public IDictionary<string, IList<string>> Highlights { get; internal set; }
/// <summary>
/// Gets the semantic search result.
/// </summary>
public SemanticSearchResult SemanticSearch { get; internal set; }
/// <summary>
/// Contains debugging information that can be used to further explore your search results.
/// </summary>
public IList<DocumentDebugInfo> DocumentDebugInfo { get; internal set; }
/// <summary>
/// The document found by the search query.
/// </summary>
public T Document { get; internal set; }
/// <summary>
/// Initializes a new instance of the SearchResult class.
/// </summary>
internal SearchResult() { }
#pragma warning disable CS1572 // Not all parameters will be used depending on feature flags
/// <summary>
/// Deserialize a SearchResult and its model.
/// </summary>
/// <param name="element">A JSON element.</param>
/// <param name="serializer">
/// Optional serializer that can be used to customize the serialization
/// of strongly typed models.
/// </param>
/// <param name="options">JSON serializer options.</param>
/// <param name="async">Whether to execute sync or async.</param>
/// <param name="cancellationToken">
/// Optional <see cref="CancellationToken"/> to propagate notifications
/// that the operation should be canceled.
/// </param>
/// <returns>Deserialized SearchResults.</returns>
internal static async Task<SearchResult<T>> DeserializeAsync(
JsonElement element,
ObjectSerializer serializer,
JsonSerializerOptions options,
bool async,
CancellationToken cancellationToken)
#pragma warning restore CS1572
{
Debug.Assert(options != null);
SearchResult<T> result = new SearchResult<T>();
result.SemanticSearch = new SemanticSearchResult();
foreach (JsonProperty prop in element.EnumerateObject())
{
if (prop.NameEquals(Constants.SearchScoreKeyJson.EncodedUtf8Bytes) &&
prop.Value.ValueKind != JsonValueKind.Null)
{
result.Score = prop.Value.GetDouble();
}
else if (prop.NameEquals(Constants.SearchHighlightsKeyJson.EncodedUtf8Bytes))
{
result.Highlights = new Dictionary<string, IList<string>>();
foreach (JsonProperty highlight in prop.Value.EnumerateObject())
{
// Add the highlight values
List<string> values = new List<string>();
result.Highlights[highlight.Name] = values;
foreach (JsonElement highlightValue in highlight.Value.EnumerateArray())
{
values.Add(highlightValue.GetString());
}
}
}
else if (prop.NameEquals(Constants.SearchRerankerScoreKeyJson.EncodedUtf8Bytes) &&
prop.Value.ValueKind != JsonValueKind.Null)
{
result.SemanticSearch.RerankerScore = prop.Value.GetDouble();
}
else if (prop.NameEquals(Constants.SearchCaptionsKeyJson.EncodedUtf8Bytes) &&
prop.Value.ValueKind != JsonValueKind.Null)
{
List<QueryCaptionResult> captionResults = new List<QueryCaptionResult>();
foreach (JsonElement captionValue in prop.Value.EnumerateArray())
{
captionResults.Add(QueryCaptionResult.DeserializeQueryCaptionResult(captionValue));
}
result.SemanticSearch.Captions = captionResults;
}
else if (prop.NameEquals(Constants.SearchDocumentDebugInfoKeyJson.EncodedUtf8Bytes) &&
prop.Value.ValueKind != JsonValueKind.Null)
{
result.DocumentDebugInfo = new List<DocumentDebugInfo>();
foreach (JsonElement documentDebugInfoValue in prop.Value.EnumerateArray())
{
result.DocumentDebugInfo.Add(Models.DocumentDebugInfo.DeserializeDocumentDebugInfo(documentDebugInfoValue));
}
}
}
// Deserialize the model
if (serializer != null)
{
using Stream stream = element.ToStream();
T document = async ?
(T)await serializer.DeserializeAsync(stream, typeof(T), cancellationToken).ConfigureAwait(false) :
(T)serializer.Deserialize(stream, typeof(T), cancellationToken);
result.Document = document;
}
else
{
T document;
if (async)
{
using Stream stream = element.ToStream();
document = await JsonSerializer.DeserializeAsync<T>(stream, options, cancellationToken).ConfigureAwait(false);
}
else
{
document = JsonSerializer.Deserialize<T>(element.GetRawText(), options);
}
result.Document = document;
}
return result;
}
}
/// <summary>
/// Semantic search result.
/// </summary>
public class SemanticSearchResult
{
/// <summary>
/// The relevance score computed by the semantic ranker for the top search results.
/// <para>Search results are sorted by the <see cref="RerankerScore"/> first and then by the <see cref="SearchResult{T}.Score"/>.
/// <see cref="RerankerScore"/> is only returned for queries of type <see cref="SearchQueryType.Semantic"/>.</para>
/// </summary>
public double? RerankerScore { get; internal set; }
/// <summary>
/// Captions are the most representative passages from the document relatively to the search query.
/// <para>They are often used as document summary. <see cref="Captions"/> are only returned for queries of type <see cref="SearchQueryType.Semantic"/>.</para>
/// </summary>
public IReadOnlyList<QueryCaptionResult> Captions { get; internal set; }
}
public static partial class SearchModelFactory
{
/// <summary> Initializes a new instance of SearchResult. </summary>
/// <typeparam name="T">
/// The .NET type that maps to the index schema. Instances of this type can
/// be retrieved as documents from the index.
/// </typeparam>
/// <param name="document">The document found by the search query.</param>
/// <param name="score">
/// The relevance score of the document compared to other documents
/// returned by the query.
/// </param>
/// <param name="highlights">
/// Text fragments from the document that indicate the matching search
/// terms, organized by each applicable field; null if hit highlighting
/// was not enabled for the query.
/// </param>
/// <returns>A new SearchResult instance for mocking.</returns>
[EditorBrowsable(EditorBrowsableState.Never)]
public static SearchResult<T> SearchResult<T>(
T document,
double? score,
IDictionary<string, IList<string>> highlights) =>
new SearchResult<T>()
{
Score = score,
Highlights = highlights,
Document = document
};
/// <summary> Initializes a new instance of SearchResult. </summary>
/// <typeparam name="T">
/// The .NET type that maps to the index schema. Instances of this type can
/// be retrieved as documents from the index.
/// </typeparam>
/// <param name="document">The document found by the search query.</param>
/// <param name="score">
/// The relevance score of the document compared to other documents
/// returned by the query.
/// </param>
/// <param name="highlights">
/// Text fragments from the document that indicate the matching search
/// terms, organized by each applicable field; null if hit highlighting
/// was not enabled for the query.
/// </param>
/// <param name="semanticSearch">The semantic search result.</param>
/// <returns>A new SearchResult instance for mocking.</returns>
public static SearchResult<T> SearchResult<T>(
T document,
double? score,
IDictionary<string, IList<string>> highlights,
SemanticSearchResult semanticSearch) =>
new SearchResult<T>()
{
Score = score,
Highlights = highlights,
Document = document,
SemanticSearch = semanticSearch
};
/// <summary> Initializes a new instance of <see cref="SemanticSearchResult"/>. </summary>
/// <param name="rerankerScore"> The relevance score computed by the semantic ranker for the top search results. </param>
/// <param name="captions"> Captions are the most representative passages from the document relatively to the search query. </param>
/// <returns> A new <see cref="Models.SemanticSearchResult"/> instance for mocking. </returns>
public static SemanticSearchResult SemanticSearchResult(
double? rerankerScore,
IReadOnlyList<QueryCaptionResult> captions) =>
new SemanticSearchResult()
{
RerankerScore = rerankerScore,
Captions = captions
};
}
}