ElasticSearch Nest Query with Dynamic Match

1.4k views Asked by At

I want to build an ElasticSearch Nest SearchDescriptor dynamically using conditional Match. The UI has two check boxes "Exclude Email" and "Exclude Loose Files". If both are checked, the search result will match 0 hits.

These 4 queries work, but I can't figure out how to make the Match parts dynamic based on conditions (other than just having 'if-elseif-else' statements for each permutation of UI check-boxes).

Thank you for any suggestions you have.

                var searchDescriptor = new SearchDescriptor<MyDocument>()
                        .Query(q => q
                            .Match(m => m.OnField(p => p.isemail).Query("true"))
                            && q.Match(m => m.OnField(p => p.isemail).Query("false"))
                            && q.QueryString(p => p.Query(searchParameters.query).DefaultOperator(Operator.And))
                            && q.Term(p => p.Author, searchParameters.author == null ? null : searchParameters.author.ToLower())
                        );

                var searchDescriptor = new SearchDescriptor<MyDocument>()
                        .Query(q => q
                            .Match(m => m.OnField(p => p.isemail).Query("true"))
                            && q.QueryString(p => p.Query(searchParameters.query).DefaultOperator(Operator.And))
                            && q.Term(p => p.Author, searchParameters.author == null ? null : searchParameters.author.ToLower())
                        );

                var searchDescriptor = new SearchDescriptor<MyDocument>()
                        .Query(q => q
                            .Match(m => m.OnField(p => p.isemail).Query("false"))
                            && q.QueryString(p => p.Query(searchParameters.query).DefaultOperator(Operator.And))
                            && q.Term(p => p.Author, searchParameters.author == null ? null : searchParameters.author.ToLower())
                        );

                var searchDescriptor = new SearchDescriptor<MyDocument>()
                        .Query(q => q
                            .QueryString(p => p.Query(searchParameters.query).DefaultOperator(Operator.And))
                            && q.Term(p => p.Author, searchParameters.author == null ? null : searchParameters.author.ToLower())
                        );
1

There are 1 answers

2
Daniel Hoffmann-Mitscherling On BEST ANSWER

There are multiple ways to conglomerate various calls into one call that will return different results depending on boolean inputs.

You have 2 variables and 4 different outcomes, you have to implement logic that checks all of these somewhere, so you have build if/elseif/else blocks with the SearchDescriptors, however with nest Filters this becomes slightly more elegant (although still similar). The following with Filters will be easier to amend later if changes are needed and slightly more readable than large if/else blocks.

Func<FilterDescriptor<T>, FilterContainer> filterName = null;

if (foo && bar) { 

filterName = af => af.Query(
    q => q.Match(m => m.OnField(p => p.isemail).Query("true")) && 
    q.Match(m => m.OnField(p => p.isemail).Query("false"))
)}

if (foo) {
filterName = af => af.Query(
    q => q.Match(m => m.OnField(p => p.isemail).Query("true"))
)}

if (bar) {
filterName = af => af.Query(
    q => q.Match(m => m.OnField(p => p.isemail).Query("false"))
)}

//Any Filter that will always return true, so Filter != null
else {filterName = af => af.Exists("id");}

Create your search descriptor with the Filter. Only results that also fulfill the requirements of the filter will return. Elasticsearch excludes items that do not fulfill Filter requirements before running your query, which is something to keep in mind.

var searchDescriptor = new SearchDescriptor<MyDocument>()
    .Query(q => q
        .QueryString(p => p.Query(searchParameters.query).DefaultOperator(Operator.And))
        && q.Term(p => p.Author, searchParameters.author == null ? null : searchParameters.author.ToLower())
    ).Filter(filterName);