Introduction
When using the fluent API the Filter method provides a powerful way of filtering out certain matching documents by accepting an expression that returns a filter. These expressions are easily created by using a number of extension methods such as the Match method. These extension methods are however limited to operating on value types and strings but not on complex types. Luckily, extending the filtering functionality is easy.
Extension methods that returns a FilterExpression
The filtering functionality can be extended in two ways. The most common is to create a custom extension method that returns a FilterExpression. A FilterExpression is an object that encapsulates an expression that returns a Filter. Once the query is executed the
FilterExpression is found and replaced by the expression it encapsulates. Finally the field names in that expression is replaced with the corresponding field names on server side.
That sounded awfully complex, but it is really quite easy. Let us say that we have two classes Author and BlogPost:
C#
public class Author
{
public int Id { get; set; }
public string Name { get; set; }
}
public class BlogPost
{
public string Title { get; set; }
public Author { get; set; }
}
Let us assume that we want to find blog posts that have authors. Without a custom extension method we cannot do that directly as there is no Exists method for the Author type. Given however that we know that authors always have an Id (which is obviously true in this case as the Id it a value type and will be 0 if not specified) we could still get the job done with the below code:
C#
client.Search<BlogPost>()
.Filter(x => x.Author.Id.Exists());
That gets the job done, but we can make queries like this more convenient by adding a custom extension method.
C#
public static class AuthorFilters
{
public static FilterExpression<Author> Exists(this Author author)
{
return new FilterExpression<Author>(
x => x.Id.Exists());
}
}
We can now rewrite our original query to check for the existance of an author
instead of the existance of the author ID:
C#
client.Search<BlogPost>()
.Filter(x => x.Author.Exists());
Alternatively, we could add a custom method for the BlogPost class that did the same job:
C#
public static class BlogPostFilters
{
public static FilterExpression<BlogPost> HasAuthor(this BlogPost blogPost)
{
return new FilterExpression<BlogPost>(
x => x.Author.Id.Exists());
}
}
Our query could then instead be:
C#
client.Search<BlogPost>()
.Filter(x => x.HasAuthor());
Extending the filtering functionality by creating extension methods that return FilterExpression is of course not limited to checking for existance, or to single filters. Assuming that we have a logged in user and a publication date on the BlogPost class we could create a custom method for finding blog posts that should be visible to the user:
C#
public static class BlogPostFilters
{
public static FilterExpression<BlogPost> VisibleToUser(this BlogPost blogPost, User user)
{
return new FilterExpression<BlogPost>(x =>
x.PublicationDate.InRange(DateTime.Now, DateTime.MaxValue)
| x.Author.Id.Match(user.Id));
}
}
Extension methods that returns a DelegateFilterBuilder
The second way of extending the filtering functionality is to create extension methods that return an instance of the
DelegateFilterBuilder class. In the previous example we've seen
how we could create extension methods that returned expressions, which could in
turn use other extension methods for filtering. Extension methods returning an
instance of DelegateFilterBuilder is one level closer to the metal as the
DelegateFilterBuilder constructor requires an expression that returns an actuall
filter. This means that they aren't as useful for extending the filtering
functionality for complex types. Instead they are usefull for extending the
filtering functionality in ways that utilize filters that are not already exposed by the fluent API.
Do you find this information helpful? Please log in to provide feedback.