This content is archived. See latest version here

Last updated: Sep 26 2013

Introduction

Search requests are primarily made up of queries and filters. In many respects queries and filters are simillar but filters are better in terms of performance. On the other hand filters don't afftect scoring (sorting by relevance). Therefore filtering is typically done when either wanting to narrow search results down, such as limitingin a free text search for books to a specific category, or when doing database-like queries.

The .NET client API has support for most types of filters supported by the REST API. For further convienience it offers a fluent API for common filtering operations.

A first example

Given that we have a class named BlogPost that have a string property named Title we can find all BlogPosts titled "Find" with the following code.

C#
var searchQuery = client.Search<BlogPost>()
    .Filter(x => x.Title.Match("Find"));

The Search<TSource> method returns an instance of ITypeSearch<TSource> which acts as a builder class for a search request. Using the Filter extension method we can add a filter to the search request by supplying an expression that returns an instance of the Filter class. The client API comes with a number of extension methods for value types and strings that returns Filters, such as the Match method used in the example above.

Grouping filters

Filters can be grouped using boolean operators. We could for instance modify our example to find blog posts that are either titled "Find" or "Search" using the below code.

C#
var searchQuery = client.Search<BlogPost>()
    .searchQuery.Filter(
        x => x.Title.Match("Find") 
           | x.Title.Match("Search"));

Assuming the BlogPost class also has a Tag property we could use the below code to find blog posts that either are titled "Search" and are tagged with "Find" or blog posts that are titled "Find" (with no requirements for it is tags).

C#
var searchQuery = client.Search<BlogPost>()
    .Filter(
        x => (x.Title.Match("Search") & x.Tags.Match("Find")) 
            | x.Title.Match("Find"));

Calling Filter multiple times

The Filter method can be called multiple times which can be useful when what filters we want to apply is determined at runtime, such as depending on user input. As an example, the first part of the filter expression above (x.Title.Match("Search") & x.Tags.Match("Find)) could also be created using the below code:
C#
var searchQuery = client.Search<BlogPost>()
    .Filter(x => x.Title.Match("Search"))
    .Filter(x => x.Tags.Match("Find"));

The OrFilter method

When invoking the Filter method multiple times the latter invocations will combine their filters with the filters from previous invocations using AND. At times we may however want to instead combine them using OR. To complete the example of grouping filters by using multiple invocations, we could therefor use the below code.
C#
var searchQuery = client.Search<BlogPost>()
    .Filter(x => x.Title.Match("Search"))
    .Filter(x => x.Tags.Match("Find"))
    .OrFilter(x.Title.Match("Find"));

Note that if the OrFilter method before first having called the Filter method it will be equivalent to calling the Filter method as there is no existing filter to OR-group its filter with.

Complex and dynamically created filters

In some scenarios, especially when building complex search queries dynamically at runtime based on user input the Filter and OrFilter methods isnt enough. While they can handle just about all scenarios at compile time, creating for instance multiple grouped Or-filters at runtime is tricky and also results in complex code. What is needed is a way to build separate filters without regard for what other filters have been applied and later apply them to the search query. This can be achieved using an extension method for IClient called BuildFilter.

The BuildFilter method requires a generic type parameter for which type the filter should be applied and returns an object of type FilterBuilder. The FilterBuilder class has the methods And and Or with which filter expressions can be added, just like the Filter and OrFilter methods described above. The FilterBuilder class is also implicitly convertible to the Filter class, meaning that instances of it can be passed as arguments to the Filter and OrFilter methods as well as used in filter expressions.

To hopefully make things a bit clearer, lets look at an example. Imagine that we are searching for restaurants and that we have two variables named cuisines and countries, both of type IEnumerable<string>. Both may be null, but if either of them arent we would like to add a filter so that only restaurants matching those cuisines or countries are returned in the search result. The below code does this by using the BuildFilter method:

C#
var q = ... some search string

ITypeSearch<Restaurant> query = 
    client.Search<Restaurant>().For(q);

if (cuisines != null)
{
    var cuisineFilter = client.BuildFilter<Restaurant>();
    foreach (var cuisine in cuisines)
    {
        cuisineFilter = cuisineFilter.Or(
            x => x.Cuisine.Match(cuisine));
    }
    query = query.Filter(cuisineFilter);
}

if (countries != null)
{
    var countryFilter = client.BuildFilter<Restaurant>();
    foreach (var country in countries)
    {
        countryFilter = countryFilter.Or(
            x => x.Country.Match(country));
    }
    query = query.Filter(countryFilter);
}

var results = query.GetResult();

Filtering methods

When filtering with the Filter method as in the examples above we provide an expression that returns a filter. In order to do so we use extension methods for properties that return filters. In the above examples we used the Match extension method, but a number of other such methods exists. It is also possible to create custom extension methods to enhance the filtering functionality.

  • Filter methods for strings
  • Filter methods for numerical fields (int, int?, double etc)
  • Filter methods for DateTime fields
  • Filter methods for boolean fields
  • Filter methods for enum fields
  • Filter methods for GeoLocation fields
  • Filter methods for complex objects

Do you have feedback on this documentation? Send an email to documentation@episerver.com. For development-related questions and discussions, refer to our Forums on https://world.episerver.com/forum/