Search code examples
c#elasticsearchnest

Elastic Search using NEST Field Boosting


I am using Elastic Search in C# using the NEST strongly typed client. I have an index containing Entries:

[ElasticType(Name = "Entry", IdProperty = "Id")]
public class Entry
{
    public string Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public string Award { get; set; }
    public int Year { get; set; }
}

Where Year is the year of the entry, eg 2012, and Award is the type of Award the Entry won, which can be null.

I then want to search these Entries using boosting for different properties. In the following code, I want results to be ranked higher that match on the Title, than those that match on the Description.

private IQueryResponse<Entry> GetMatchedEntries(string searchText)
{
    return _elasticClient.Search<Entry>(
                body =>
                body.Query(q => 
                           q.QueryString(qs => 
                                         qs.OnFieldsWithBoost(d => 
                                                              d.Add(entry => entry.Title, 5.0)
                                                              .Add(entry => entry.Description, 2.0))
                           .Query(searchText))));
}

I have now been asked to Boost the results by those which have won Awards, and also Boost newer Entries (ie by the Year).

How do I do this? Is it something that needs to be done as part of the indexing service, or as part of the search?


Solution

  • You can achieve this through a combination of a boosting query and custom_score query

    instead of boosting year we alter the score based on the year because:

    (_score + 2013) > (_score + 1999)
    

    Newer results will float to the top.

    By using a boosting query we can effectively demote results that are missing the award field.

    see: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-boosting-query.html https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html

    _client.Search<Entry>(s=>s
        .Query(q =>q
            .Boosting(bq=>bq
                .Positive(pq=>pq
                    .CustomScore(cbf=>cbf
                        .Query(cbfq=>cbfq
                            .QueryString(qs => qs
                                .OnFieldsWithBoost(d =>
                                    d.Add(entry => entry.Title, 5.0)
                                    .Add(entry => entry.Description, 2.0)
                                )
                                .Query(searchText)
                            )
                        )
                        .Script("_score + doc['year'].value")
                    )
                )
                .Negative(nq=>nq
                    .Filtered(nfq=>nfq
                        .Query(qq=>qq.MatchAll())
                        .Filter(f=>f.Missing(p=>p.Award))
                    )
                )
                .NegativeBoost(0.2)
            )
        )
    );