Skip to content

Commit c56d8b0

Browse files
sboulemaSamir BoulemaBorisDog
authored
CSHARP-4703: Add implementation for ScoreDetails
Co-authored-by: Samir Boulema <[email protected]> Co-authored-by: BorisDog <[email protected]>
1 parent 4aadc9f commit c56d8b0

11 files changed

+156
-11
lines changed

src/MongoDB.Driver/AggregateFluent.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,10 @@ public override IAggregateFluent<TResult> Search(
244244
SearchHighlightOptions<TResult> highlight = null,
245245
string indexName = null,
246246
SearchCountOptions count = null,
247-
bool returnStoredSource = false)
247+
bool returnStoredSource = false,
248+
bool scoreDetails = false)
248249
{
249-
return WithPipeline(_pipeline.Search(searchDefinition, highlight, indexName, count, returnStoredSource));
250+
return WithPipeline(_pipeline.Search(searchDefinition, highlight, indexName, count, returnStoredSource, scoreDetails));
250251
}
251252

252253
public override IAggregateFluent<SearchMetaResult> SearchMeta(

src/MongoDB.Driver/AggregateFluentBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ public virtual IAggregateFluent<TResult> Search(
222222
SearchHighlightOptions<TResult> highlight = null,
223223
string indexName = null,
224224
SearchCountOptions count = null,
225-
bool returnStoredSource = false)
225+
bool returnStoredSource = false,
226+
bool scoreDetails = false)
226227
{
227228
throw new NotImplementedException();
228229
}

src/MongoDB.Driver/IAggregateFluent.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,13 +365,18 @@ IAggregateFluent<BsonDocument> SetWindowFields<TWindowFields>(
365365
/// Flag that specifies whether to perform a full document lookup on the backend database
366366
/// or return only stored source fields directly from Atlas Search.
367367
/// </param>
368+
/// <param name="scoreDetails">
369+
/// Flag that specifies whether to return a detailed breakdown
370+
/// of the score for each document in the result.
371+
/// </param>
368372
/// <returns>The fluent aggregate interface.</returns>
369373
IAggregateFluent<TResult> Search(
370374
SearchDefinition<TResult> searchDefinition,
371375
SearchHighlightOptions<TResult> highlight = null,
372376
string indexName = null,
373377
SearchCountOptions count = null,
374-
bool returnStoredSource = false);
378+
bool returnStoredSource = false,
379+
bool scoreDetails = false);
375380

376381
/// <summary>
377382
/// Appends a $searchMeta stage to the pipeline.

src/MongoDB.Driver/Linq/MongoQueryable.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,18 +1149,23 @@ public static IMongoQueryable<TSource> Sample<TSource>(this IMongoQueryable<TSou
11491149
/// Flag that specifies whether to perform a full document lookup on the backend database
11501150
/// or return only stored source fields directly from Atlas Search.
11511151
/// </param>
1152+
/// <param name="scoreDetails">
1153+
/// Flag that specifies whether to return a detailed breakdown
1154+
/// of the score for each document in the result.
1155+
/// </param>
11521156
/// <returns>The queryable with a new stage appended.</returns>
11531157
public static IMongoQueryable<TSource> Search<TSource>(
11541158
this IMongoQueryable<TSource> source,
11551159
SearchDefinition<TSource> searchDefinition,
11561160
SearchHighlightOptions<TSource> highlight = null,
11571161
string indexName = null,
11581162
SearchCountOptions count = null,
1159-
bool returnStoredSource = false)
1163+
bool returnStoredSource = false,
1164+
bool scoreDetails = false)
11601165
{
11611166
return AppendStage(
11621167
source,
1163-
PipelineStageDefinitionBuilder.Search(searchDefinition, highlight, indexName, count, returnStoredSource));
1168+
PipelineStageDefinitionBuilder.Search(searchDefinition, highlight, indexName, count, returnStoredSource, scoreDetails));
11641169
}
11651170

11661171
/// <summary>

src/MongoDB.Driver/PipelineDefinitionBuilder.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,10 @@ public static PipelineDefinition<TInput, TOutput> ReplaceWith<TInput, TIntermedi
11791179
/// Flag that specifies whether to perform a full document lookup on the backend database
11801180
/// or return only stored source fields directly from Atlas Search.
11811181
/// </param>
1182+
/// <param name="scoreDetails">
1183+
/// Flag that specifies whether to return a detailed breakdown
1184+
/// of the score for each document in the result.
1185+
/// </param>
11821186
/// <returns>
11831187
/// A new pipeline with an additional stage.
11841188
/// </returns>
@@ -1188,10 +1192,11 @@ public static PipelineDefinition<TInput, TOutput> Search<TInput, TOutput>(
11881192
SearchHighlightOptions<TOutput> highlight = null,
11891193
string indexName = null,
11901194
SearchCountOptions count = null,
1191-
bool returnStoredSource = false)
1195+
bool returnStoredSource = false,
1196+
bool scoreDetails = false)
11921197
{
11931198
Ensure.IsNotNull(pipeline, nameof(pipeline));
1194-
return pipeline.AppendStage(PipelineStageDefinitionBuilder.Search(searchDefinition, highlight, indexName, count, returnStoredSource));
1199+
return pipeline.AppendStage(PipelineStageDefinitionBuilder.Search(searchDefinition, highlight, indexName, count, returnStoredSource, scoreDetails));
11951200
}
11961201

11971202
/// <summary>

src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1320,13 +1320,18 @@ public static PipelineStageDefinition<TInput, TOutput> Project<TInput, TOutput>(
13201320
/// Flag that specifies whether to perform a full document lookup on the backend database
13211321
/// or return only stored source fields directly from Atlas Search.
13221322
/// </param>
1323+
/// <param name="scoreDetails">
1324+
/// Flag that specifies whether to return a detailed breakdown
1325+
/// of the score for each document in the result.
1326+
/// </param>
13231327
/// <returns>The stage.</returns>
13241328
public static PipelineStageDefinition<TInput, TInput> Search<TInput>(
13251329
SearchDefinition<TInput> searchDefinition,
13261330
SearchHighlightOptions<TInput> highlight = null,
13271331
string indexName = null,
13281332
SearchCountOptions count = null,
1329-
bool returnStoredSource = false)
1333+
bool returnStoredSource = false,
1334+
bool scoreDetails = false)
13301335
{
13311336
Ensure.IsNotNull(searchDefinition, nameof(searchDefinition));
13321337

@@ -1340,6 +1345,7 @@ public static PipelineStageDefinition<TInput, TInput> Search<TInput>(
13401345
renderedSearchDefinition.Add("count", () => count.Render(), count != null);
13411346
renderedSearchDefinition.Add("index", indexName, indexName != null);
13421347
renderedSearchDefinition.Add("returnStoredSource", returnStoredSource, returnStoredSource);
1348+
renderedSearchDefinition.Add("scoreDetails", scoreDetails, scoreDetails);
13431349

13441350
var document = new BsonDocument(operatorName, renderedSearchDefinition);
13451351
return new RenderedPipelineStageDefinition<TInput>(operatorName, document, s);

src/MongoDB.Driver/ProjectionDefinitionBuilder.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,23 @@ public static ProjectionDefinition<TDocument> MetaSearchScore<TDocument>(
190190
return builder.Combine(projection, builder.MetaSearchScore(field));
191191
}
192192

193+
/// <summary>
194+
/// Combines an existing projection with a search score details projection.
195+
/// </summary>
196+
/// <typeparam name="TDocument">The type of the document.</typeparam>
197+
/// <param name="projection">The projection.</param>
198+
/// <param name="field">The field.</param>
199+
/// <returns>
200+
/// A combined projection.
201+
/// </returns>
202+
public static ProjectionDefinition<TDocument> MetaSearchScoreDetails<TDocument>(
203+
this ProjectionDefinition<TDocument> projection,
204+
string field)
205+
{
206+
var builder = Builders<TDocument>.Projection;
207+
return builder.Combine(projection, builder.MetaSearchScoreDetails(field));
208+
}
209+
193210
/// <summary>
194211
/// Combines an existing projection with a text score projection.
195212
/// </summary>
@@ -455,6 +472,18 @@ public ProjectionDefinition<TSource> MetaSearchScore(string field)
455472
return Meta(field, "searchScore");
456473
}
457474

475+
/// <summary>
476+
/// Creates a search score details projection.
477+
/// </summary>
478+
/// <param name="field">The field.</param>
479+
/// <returns>
480+
/// A search score details projection.
481+
/// </returns>
482+
public ProjectionDefinition<TSource> MetaSearchScoreDetails(string field)
483+
{
484+
return Meta(field, "searchScoreDetails");
485+
}
486+
458487
/// <summary>
459488
/// Creates a text score projection.
460489
/// </summary>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using MongoDB.Bson;
17+
using MongoDB.Bson.Serialization.Attributes;
18+
19+
namespace MongoDB.Driver.Search
20+
{
21+
/// <summary>
22+
/// Represents the scoreDetails object for a document in the result.
23+
/// </summary>
24+
public sealed class SearchScoreDetails
25+
{
26+
/// <summary>
27+
/// Initializes a new instance of the <see cref="SearchScoreDetails"/> class.
28+
/// </summary>
29+
/// <param name="value">Contribution towards the score by a subset of the scoring formula.</param>
30+
/// <param name="description">Subset of the scoring formula.</param>
31+
/// <param name="details">Breakdown of the score for each match in the document.</param>
32+
public SearchScoreDetails(double value, string description, SearchScoreDetails[] details)
33+
{
34+
Value = value;
35+
Description = description;
36+
Details = details;
37+
}
38+
39+
/// <summary>
40+
/// Gets the contribution towards the score by a subset of the scoring formula.
41+
/// </summary>
42+
[BsonElement("value")]
43+
public double Value { get; }
44+
45+
/// <summary>
46+
/// Gets the subset of the scoring formula including details about how the document
47+
/// was scored and factors considered in calculating the score.
48+
/// </summary>
49+
[BsonElement("description")]
50+
public string Description { get; }
51+
52+
/// <summary>
53+
/// Breakdown of the score for each match in the document based on the subset of the scoring formula.
54+
/// (if any).
55+
/// </summary>
56+
[BsonDefaultValue(null)]
57+
[BsonElement("details")]
58+
public SearchScoreDetails[] Details { get; }
59+
}
60+
}

tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,18 @@ public void Search_should_add_expected_stage_with_return_stored_source()
182182
stages[0].Should().Be("{ $search: { text: { query: 'foo', path: 'bar' }, returnStoredSource: true } }");
183183
}
184184

185+
[Fact]
186+
public void Search_should_add_expected_stage_with_score_details()
187+
{
188+
var pipeline = new EmptyPipelineDefinition<BsonDocument>();
189+
var builder = new SearchDefinitionBuilder<BsonDocument>();
190+
191+
var result = pipeline.Search(builder.Text("bar", "foo"), scoreDetails: true);
192+
193+
var stages = RenderStages(result, BsonDocumentSerializer.Instance);
194+
stages[0].Should().Be("{ $search: { text: { query: 'foo', path: 'bar' }, scoreDetails: true } }");
195+
}
196+
185197
[Fact]
186198
public void Search_should_throw_when_pipeline_is_null()
187199
{

tests/MongoDB.Driver.Tests/Search/AtlasSearchTests.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,13 +256,15 @@ public void Phrase()
256256
.Search(Builders.Search.Phrase(x => x.Body, "life, liberty, and the pursuit of happiness"),
257257
new SearchHighlightOptions<HistoricalDocument>(x => x.Body),
258258
indexName: "default",
259-
returnStoredSource: true)
259+
returnStoredSource: true,
260+
scoreDetails: true)
260261
.Limit(1)
261262
.Project<HistoricalDocument>(Builders.Projection
262263
.Include(x => x.Title)
263264
.Include(x => x.Body)
264265
.MetaSearchScore("score")
265-
.MetaSearchHighlights("highlights"))
266+
.MetaSearchHighlights("highlights")
267+
.MetaSearchScoreDetails("scoreDetails"))
266268
.ToList();
267269

268270
var result = results.Should().ContainSingle().Subject;
@@ -280,6 +282,14 @@ public void Phrase()
280282

281283
var highlightRangeStr = string.Join(string.Empty, highlightTexts.Skip(1).Select(x => x.Value));
282284
highlightRangeStr.Should().Be("Life, Liberty and the pursuit of Happiness.");
285+
286+
result.ScoreDetails.Description.Should().Contain("life liberty and the pursuit of happiness");
287+
result.ScoreDetails.Value.Should().NotBe(0);
288+
289+
var scoreDetail = result.ScoreDetails.Details.Should().ContainSingle().Subject;
290+
scoreDetail.Description.Should().NotBeNullOrEmpty();
291+
scoreDetail.Value.Should().NotBe(0);
292+
scoreDetail.Details.Should().NotBeEmpty();
283293
}
284294

285295
[Fact]
@@ -493,6 +503,9 @@ public class HistoricalDocument
493503

494504
[BsonElement("metaResult")]
495505
public SearchMetaResult MetaResult { get; set; }
506+
507+
[BsonElement("scoreDetails")]
508+
public SearchScoreDetails ScoreDetails { get; set; }
496509
}
497510

498511
[BsonIgnoreExtraElements]

0 commit comments

Comments
 (0)