Search code examples
c#mongodblinq.net-coremongodb-.net-driver

How can MetaTextScore be "Injected" into a MongoDb Linq Query


To full text search an IMongoQueryable<T> we can use the following extension method.

public static IMongoQueryable<T> WhereText<T>(this IMongoQueryable<T> query, string search) where T: IScored
{
   var filter = Builders<T>.Filter.Text(search);
   return query.Where(_ => filter.Inject())
}

Similarly, how can the full text search score, which is normally sorted on using :

private static readonly ProjectionDefinition<ProductTypeSearchResult> TextMatchScoreProjection =
   Builders<ProductTypeSearchResult>.Projection.MetaTextScore("Score");
private static readonly SortDefinition<ProductTypeSearchResult> Sort =
   Builders<ProductTypeSearchResult>.Sort.MetaTextScore("score");

collection.Find(...).Project(TextMatchScoreProjection).Sort(Sort);

be implemented using IMongoQueryable<T>

The examples are blank in the documentation https://mongodb.github.io/mongo-csharp-driver/2.8/reference/driver/expressions/#text


Solution

  • Given a class member:

    private readonly IMongoCollection<SomeType> _someTypes;
    

    The code to inject the query score will look like:

    private IAggregateFluent<SomeType> CreateSearchQuery(string query, int skipCount, int limitCount)
    {
        var filter = Builders<SomeType>.Filter.Text(query);
        return _someTypes
            .Aggregate()
            // Sadly, we must create the query every time because we don't have a way to pass in
            // a filter to an already created query, or change the properties of the filter.
            .Match(filter)
            // Currently the null coalescing operator (??) is not supported in match expressions
            // see https://jira.mongodb.org/browse/CSHARP-2708
            // Currently there is no way to "inject" the text search score
            // see https://jira.mongodb.org/browse/CSHARP-2707
            .AppendStage<SomeType>("{$addFields: {score: {$meta:'textScore'}}}")
            .Sort(_sort)
            .Skip(skipCount)
            .Limit(limitCount);
    }