I am using Realm database for storing data locally on my device. In the application a user can see a list of books and apply Filters/Sorting on them. A book is a Pojo object which has a Title, Author(string), Status(Enum) and Publish-Date(time stamp). The user of the application can filter books based on those four fields, there are more fields on the object but for simplicity I am only using 4 at the moment.
I am trying to create a dynamic query builder where all the selected filters will be applied on the query.
What I have seen in the documentation is that we can query realm like this
var allBooks = realm.All<Book>().Where(book =>
book.Title== "Star wars" ||
book.Status== "Sold");
what I want is to construct the query on my own by passing the list of filters int the method. I was thinking of creating a wrapper class with Key/Value pair and assigning the name of the filter and the value. Then I could pass a list of those to a method. My question is how construct the query builder from a list of key/value where the key is the object field like "Title", "Status" and the value is "Star wars", "Sold".
Any help will be greatly appreciate.
You can create a dynamic predicate with Linq Expressions:
public static Expression<Func<T, bool>> CreatePredicate<T>(KeyValuePair<string, string>[] filters)
{
var type = typeof(T);
var parameter = Expression.Parameter(type, "t");
if (filters.Length == 0) // no filtering
return Expression.Lambda<Func<T, bool>>(Expression.Constant(true), parameter);
Expression body = Expression.Constant(false);
foreach (var filter in filters)
{
var member = Expression.PropertyOrField(parameter, filter.Key);
var value = Expression.Constant(filter.Value);
body = Expression.OrElse(body, Expression.Equal(member, value));
}
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
Now if All<Book>()
returns an IQueryable<Book>
use it like:
var predicate = CreatePredicate<Book>(new[]
{
new KeyValuePair<string, string>("Title", "Star wars"),
new KeyValuePair<string, string>("Status", "Sold"),
});
var allBooks = realm.All<Book>().Where(predicate);
In case if All<Book>()
returns IEnumerable<Book>
you still able to use it, just add call to Compile
:
var allBooks = realm.All<Book>().Where(predicate.Compile());