Friday, December 31, 2010

Search a text in all properties of an object in collection (FullTextSearch) using LINQ

Recently in our project, we had a requirement to search for particular character in the List. The search is similar to FullTextSearch which means, we need to search for a specific character in each and every property of an object contained in the list..
We initial thought the only way is to use LINQ to add condition for every property with OR statement. Then while googling got one idea from this link;
http://manfred-ramoser.blogspot.com/2009/09/full-text-search-for-entity-framework.html

This exactly does the same what we needed except failed in one scenario. (i.e.) this failed when any of the property is NULL. So I modified the code a little bit which take cares of the NULL situation as well. Below is that code;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;


namespace TestXML
{
public static class QuerableExtensionMethods
{
public static IQueryable<T> FullTextSearch<T>(this IQueryable<T> querytable, string searchkey)
{
return FullTextSearch<T>(querytable.AsQueryable<T>(), searchkey, false);
}


public static IQueryable<T> FullTextSearch<T>(this IQueryable<T> querytable, string searchkey,bool exactMatch)
{


ParameterExpression parameter = Expression.Parameter(typeof(T), "c");
ParameterExpression stringParameter = Expression.Parameter(typeof(string), "d");
MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
MethodInfo nullMethod = typeof(string).GetMethod("IsNullOrEmpty", new Type[] { typeof(string) });
var publicProperties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(p => p.PropertyType == typeof(string));
Expression orExpression = null;


string[] searchKeyParts = null;


if (exactMatch)
searchKeyParts = new[] { searchkey };
else
searchKeyParts = searchkey.Split(' ');


foreach (var property in publicProperties)
{
Expression nameProperty = Expression.Property(parameter, property);
foreach (var searchKeyPart in searchKeyParts)
{


Expression searchkeyExpression = Expression.Constant(searchKeyPart);
Expression nullCheckExpression = Expression.Constant(null);
Expression callContainsMethod = Expression.Call(nameProperty, containsMethod, searchkeyExpression);
Expression nullCheckMethod = Expression.Call(stringParameter, nullMethod, nameProperty);
Expression notExpression = Expression.Not(nullCheckMethod);


if (orExpression == null)
orExpression = Expression.AndAlso(notExpression, callContainsMethod);
else
orExpression = Expression.Or(orExpression, Expression.AndAlso(notExpression, callContainsMethod));
}
}
MethodCallExpression whereCallExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { querytable.ElementType }, querytable.Expression,
Expression.Lambda<Func<T, bool>>(orExpression, new ParameterExpression[] { parameter }));


return querytable.Provider.CreateQuery<T>(whereCallExpression);
}


}
}

Happy coding and wishing all the viewers of my blog happy and prosperous new year!!!!