Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/MongoDB.Driver/AggregateFluent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,10 @@ public override IAggregateFluent<TResult> Search(
SearchHighlightOptions<TResult> highlight = null,
string indexName = null,
SearchCountOptions count = null,
bool returnStoredSource = false)
bool returnStoredSource = false,
bool scoreDetails = false)
{
return WithPipeline(_pipeline.Search(searchDefinition, highlight, indexName, count, returnStoredSource));
return WithPipeline(_pipeline.Search(searchDefinition, highlight, indexName, count, returnStoredSource, scoreDetails));
}

public override IAggregateFluent<SearchMetaResult> SearchMeta(
Expand Down
3 changes: 2 additions & 1 deletion src/MongoDB.Driver/AggregateFluentBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ public virtual IAggregateFluent<TResult> Search(
SearchHighlightOptions<TResult> highlight = null,
string indexName = null,
SearchCountOptions count = null,
bool returnStoredSource = false)
bool returnStoredSource = false,
bool scoreDetails = false)
{
throw new NotImplementedException();
}
Expand Down
7 changes: 6 additions & 1 deletion src/MongoDB.Driver/IAggregateFluent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -365,13 +365,18 @@ IAggregateFluent<BsonDocument> SetWindowFields<TWindowFields>(
/// Flag that specifies whether to perform a full document lookup on the backend database
/// or return only stored source fields directly from Atlas Search.
/// </param>
/// <param name="scoreDetails">
/// Flag that specifies whether to return a detailed breakdown
/// of the score for each document in the result.
/// </param>
/// <returns>The fluent aggregate interface.</returns>
IAggregateFluent<TResult> Search(
SearchDefinition<TResult> searchDefinition,
SearchHighlightOptions<TResult> highlight = null,
string indexName = null,
SearchCountOptions count = null,
bool returnStoredSource = false);
bool returnStoredSource = false,
bool scoreDetails = false);

/// <summary>
/// Appends a $searchMeta stage to the pipeline.
Expand Down
9 changes: 7 additions & 2 deletions src/MongoDB.Driver/Linq/MongoQueryable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,18 +1149,23 @@ public static IMongoQueryable<TSource> Sample<TSource>(this IMongoQueryable<TSou
/// Flag that specifies whether to perform a full document lookup on the backend database
/// or return only stored source fields directly from Atlas Search.
/// </param>
/// <param name="scoreDetails">
/// Flag that specifies whether to return a detailed breakdown
/// of the score for each document in the result.
/// </param>
/// <returns>The queryable with a new stage appended.</returns>
public static IMongoQueryable<TSource> Search<TSource>(
this IMongoQueryable<TSource> source,
SearchDefinition<TSource> searchDefinition,
SearchHighlightOptions<TSource> highlight = null,
string indexName = null,
SearchCountOptions count = null,
bool returnStoredSource = false)
bool returnStoredSource = false,
bool scoreDetails = false)
{
return AppendStage(
source,
PipelineStageDefinitionBuilder.Search(searchDefinition, highlight, indexName, count, returnStoredSource));
PipelineStageDefinitionBuilder.Search(searchDefinition, highlight, indexName, count, returnStoredSource, scoreDetails));
}

/// <summary>
Expand Down
9 changes: 7 additions & 2 deletions src/MongoDB.Driver/PipelineDefinitionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,10 @@ public static PipelineDefinition<TInput, TOutput> ReplaceWith<TInput, TIntermedi
/// Flag that specifies whether to perform a full document lookup on the backend database
/// or return only stored source fields directly from Atlas Search.
/// </param>
/// <param name="scoreDetails">
/// Flag that specifies whether to return a detailed breakdown
/// of the score for each document in the result.
/// </param>
/// <returns>
/// A new pipeline with an additional stage.
/// </returns>
Expand All @@ -1188,10 +1192,11 @@ public static PipelineDefinition<TInput, TOutput> Search<TInput, TOutput>(
SearchHighlightOptions<TOutput> highlight = null,
string indexName = null,
SearchCountOptions count = null,
bool returnStoredSource = false)
bool returnStoredSource = false,
bool scoreDetails = false)
{
Ensure.IsNotNull(pipeline, nameof(pipeline));
return pipeline.AppendStage(PipelineStageDefinitionBuilder.Search(searchDefinition, highlight, indexName, count, returnStoredSource));
return pipeline.AppendStage(PipelineStageDefinitionBuilder.Search(searchDefinition, highlight, indexName, count, returnStoredSource, scoreDetails));
}

/// <summary>
Expand Down
8 changes: 7 additions & 1 deletion src/MongoDB.Driver/PipelineStageDefinitionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1320,13 +1320,18 @@ public static PipelineStageDefinition<TInput, TOutput> Project<TInput, TOutput>(
/// Flag that specifies whether to perform a full document lookup on the backend database
/// or return only stored source fields directly from Atlas Search.
/// </param>
/// <param name="scoreDetails">
/// Flag that specifies whether to return a detailed breakdown
/// of the score for each document in the result.
/// </param>
/// <returns>The stage.</returns>
public static PipelineStageDefinition<TInput, TInput> Search<TInput>(
SearchDefinition<TInput> searchDefinition,
SearchHighlightOptions<TInput> highlight = null,
string indexName = null,
SearchCountOptions count = null,
bool returnStoredSource = false)
bool returnStoredSource = false,
bool scoreDetails = false)
{
Ensure.IsNotNull(searchDefinition, nameof(searchDefinition));

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

var document = new BsonDocument(operatorName, renderedSearchDefinition);
return new RenderedPipelineStageDefinition<TInput>(operatorName, document, s);
Expand Down
29 changes: 29 additions & 0 deletions src/MongoDB.Driver/ProjectionDefinitionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,23 @@ public static ProjectionDefinition<TDocument> MetaSearchScore<TDocument>(
return builder.Combine(projection, builder.MetaSearchScore(field));
}

/// <summary>
/// Combines an existing projection with a search score details projection.
/// </summary>
/// <typeparam name="TDocument">The type of the document.</typeparam>
/// <param name="projection">The projection.</param>
/// <param name="field">The field.</param>
/// <returns>
/// A combined projection.
/// </returns>
public static ProjectionDefinition<TDocument> MetaSearchScoreDetails<TDocument>(
this ProjectionDefinition<TDocument> projection,
string field)
{
var builder = Builders<TDocument>.Projection;
return builder.Combine(projection, builder.MetaSearchScoreDetails(field));
}

/// <summary>
/// Combines an existing projection with a text score projection.
/// </summary>
Expand Down Expand Up @@ -455,6 +472,18 @@ public ProjectionDefinition<TSource> MetaSearchScore(string field)
return Meta(field, "searchScore");
}

/// <summary>
/// Creates a search score details projection.
/// </summary>
/// <param name="field">The field.</param>
/// <returns>
/// A search score details projection.
/// </returns>
public ProjectionDefinition<TSource> MetaSearchScoreDetails(string field)
{
return Meta(field, "searchScoreDetails");
}

/// <summary>
/// Creates a text score projection.
/// </summary>
Expand Down
60 changes: 60 additions & 0 deletions src/MongoDB.Driver/Search/SearchScoreDetails.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace MongoDB.Driver.Search
{
/// <summary>
/// Represents the scoreDetails object for a document in the result.
/// </summary>
public sealed class SearchScoreDetails
{
/// <summary>
/// Initializes a new instance of the <see cref="SearchScoreDetails"/> class.
/// </summary>
/// <param name="value">Contribution towards the score by a subset of the scoring formula.</param>
/// <param name="description">Subset of the scoring formula.</param>
/// <param name="details">Breakdown of the score for each match in the document.</param>
public SearchScoreDetails(double value, string description, SearchScoreDetails[] details)
{
Value = value;
Description = description;
Details = details;
}

/// <summary>
/// Gets the contribution towards the score by a subset of the scoring formula.
/// </summary>
[BsonElement("value")]
public double Value { get; }

/// <summary>
/// Gets the subset of the scoring formula including details about how the document
/// was scored and factors considered in calculating the score.
/// </summary>
[BsonElement("description")]
public string Description { get; }

/// <summary>
/// Breakdown of the score for each match in the document based on the subset of the scoring formula.
/// (if any).
/// </summary>
[BsonDefaultValue(null)]
[BsonElement("details")]
public SearchScoreDetails[] Details { get; }
}
}
12 changes: 12 additions & 0 deletions tests/MongoDB.Driver.Tests/PipelineDefinitionBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,18 @@ public void Search_should_add_expected_stage_with_return_stored_source()
stages[0].Should().Be("{ $search: { text: { query: 'foo', path: 'bar' }, returnStoredSource: true } }");
}

[Fact]
public void Search_should_add_expected_stage_with_score_details()
{
var pipeline = new EmptyPipelineDefinition<BsonDocument>();
var builder = new SearchDefinitionBuilder<BsonDocument>();

var result = pipeline.Search(builder.Text("bar", "foo"), scoreDetails: true);

var stages = RenderStages(result, BsonDocumentSerializer.Instance);
stages[0].Should().Be("{ $search: { text: { query: 'foo', path: 'bar' }, scoreDetails: true } }");
}

[Fact]
public void Search_should_throw_when_pipeline_is_null()
{
Expand Down
17 changes: 15 additions & 2 deletions tests/MongoDB.Driver.Tests/Search/AtlasSearchTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,15 @@ public void Phrase()
.Search(Builders.Search.Phrase(x => x.Body, "life, liberty, and the pursuit of happiness"),
new SearchHighlightOptions<HistoricalDocument>(x => x.Body),
indexName: "default",
returnStoredSource: true)
returnStoredSource: true,
scoreDetails: true)
.Limit(1)
.Project<HistoricalDocument>(Builders.Projection
.Include(x => x.Title)
.Include(x => x.Body)
.MetaSearchScore("score")
.MetaSearchHighlights("highlights"))
.MetaSearchHighlights("highlights")
.MetaSearchScoreDetails("scoreDetails"))
.ToList();

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

var highlightRangeStr = string.Join(string.Empty, highlightTexts.Skip(1).Select(x => x.Value));
highlightRangeStr.Should().Be("Life, Liberty and the pursuit of Happiness.");

result.ScoreDetails.Description.Should().Contain("life liberty and the pursuit of happiness");
result.ScoreDetails.Value.Should().NotBe(0);

var scoreDetail = result.ScoreDetails.Details.Should().ContainSingle().Subject;
scoreDetail.Description.Should().NotBeNullOrEmpty();
scoreDetail.Value.Should().NotBe(0);
scoreDetail.Details.Should().NotBeEmpty();
}

[Fact]
Expand Down Expand Up @@ -493,6 +503,9 @@ public class HistoricalDocument

[BsonElement("metaResult")]
public SearchMetaResult MetaResult { get; set; }

[BsonElement("scoreDetails")]
public SearchScoreDetails ScoreDetails { get; set; }
}

[BsonIgnoreExtraElements]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ public void MetaSearchScore()
AssertRendered(subject.MetaSearchScore("a"), "{ a : { $meta: 'searchScore' } }");
}

[Fact]
public void MetaSearchScoreDetails()
{
var subject = CreateSubject<BsonDocument>();

AssertRendered(subject.MetaSearchScoreDetails("a"), "{ a : { $meta: 'searchScoreDetails' } }");
}

[Fact]
public void SearchMeta()
{
Expand Down