Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Appending multiple bool filters to a NEST query

I'd like to append multiple bool filters with NEST, but I can't (practically) do it in a single statement as I want to build a set of Bool filters depending on different conditions.

Something like this pseudo code:

// Append a filter
searchDescriptor.Filter(f => f.Bool(b => b.Must(m => m.Term(i => i.SomeProperty, "SomeValue"))));

if (UserId.HasValue)
{
   // Optionally append another filter (AND condition with the first filter)
   searchDescriptor.Filter(f => f.Bool(b => b.Must(m => m.Term(i => i.AnotherProperty, "MyOtherValue"))));
}

var result = Client.Search(searchDescriptor);

Now it seems when the second optional filter is appended, it essentially replaces the first filter.

I'm sure I'm missing something syntactically, but I can't figure it out and the NEST documentation is a bit thin on the filter DSL. :)

like image 725
Ted Nyberg Avatar asked Oct 28 '13 16:10

Ted Nyberg


2 Answers

The section on writing queries pretty much applies to filters as well: https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/writing-queries.html

The solution you ended up with is less than ideal since you are wrapping bool filters inside an and filter which have very different caching mechanisms (and filters don't use the cached bitsets so in most cases perform worse then regular bool filters).

Highly recommend reading http://www.elastic.co/blog/all-about-elasticsearch-filter-bitsets/ as it explains really well what the differences between and/or/not filters vs bool filters are.

You can actually rewrite this like so:

Client.Search(s=>s
    .Filter(f=> { 
        BaseFilter ff = f.Term(i => i.MyProperty, "SomeValue");
        if (UserId.HasValue)
            ff &= f.Term(i => i.AnotherProperty, "AnotherValue");
        return ff;
    })
);

If the second term is searching using UserId you can take advantage of NEST's conditionless queries

Client.Search(s=>s
    .Filter(f=> 
        f.Term(i => i.MyProperty, "SomeValue") 
        && f.Term(i => i.AnotherProperty, UserId)
    )
);

If UserId is null then the term query won't be generated as part of the query, nest in this case won't even wrap the single remaining term in a bool filter but just send it as a plain term filter instead.

like image 107
Martijn Laarman Avatar answered Oct 26 '22 00:10

Martijn Laarman


Ah, something like this seems to work:

        var filters = new List<BaseFilter>();

        // Required filter
        filters.Add(new FilterDescriptor<MyType>().Bool(b => b.Must(m => m.Term(i => i.MyProperty, "SomeValue"))));

        if (UserId.HasValue)
        {
            filters.Add(new FilterDescriptor<MyType>().Bool(b => b.Must(m => m.Term(i => i.AnotherProperty, "AnotherValue"))));
        }

        // Filter with AND operator
        searchDescriptor.Filter(f => f.And(filters.ToArray()));

        var result = Client.Search(searchDescriptor);
like image 29
Ted Nyberg Avatar answered Oct 25 '22 23:10

Ted Nyberg