SearchExtensions : Search extension method for multiple properties against multiple terms

by John Nye

29 Oct
2013

Now available as a nuget package. Search for 'SearchExtensions' or run the following:

PM> Install-Package NinjaNye.SearchExtensions

Source code can be found here: https://github.com/ninjanye/searchextensions


I've recently updated my IQueryable search extension method library to include a search method that allows searching multiple terms against multiple properties. Here's the code with some example output when connected to sql

The code

public static class QueryableExtensions
{
    /// <summary>
    /// Search multiple properties for multiple search terms
    /// </summary>
    /// <param name="source">Source data to query</param>
    /// <param name="searchTerms">search term to look for</param>
    /// <param name="stringProperties">properties to search against</param>
    /// <returns>Collection of records matching the search term</returns>
    public static IQueryable<T> Search<T>(this IQueryable<T> source, 
                                          IList<string> searchTerms, 
                                          params Expression<Func<T, string>>[] stringProperties)
    {
        if (!searchTerms.Any())
        {
            return source;
        }

        // The below is the equivalent lamda that is being constructed:
        // source.Where(x => x.[property1].Contains(searchTerm1)
        //                || x.[property1].Contains(searchTerm2)
        //                || x.[property2].Contains(searchTerm1)
        //                || x.[property2].Contains(searchTerm2)
        //                || x.[property3].Contains(searchTerm1)
        //                || x.[property3].Contains(searchTerm2)...)

        //Variable to hold merged 'OR' expression
        Expression orExpression = null;
        //Retrieve first parameter to use accross all expressions
        var singleParameter = stringProperties[0].Parameters.Single();

        foreach (var searchTerm in searchTerms)
        {
            //Create expression to represent x.[property].Contains(searchTerm)
            ConstantExpression searchTermExpression = Expression.Constant(searchTerm);

            //Build a contains expression for each property
            foreach (var stringProperty in stringProperties)
            {
                //Syncronize single parameter accross each property
                var swappedParamExpression = SwapExpressionVisitor.Swap(stringProperty, stringProperty.Parameters.Single(), singleParameter);

                //Build expression to represent x.[propertyX].Contains(searchTerm)
                var containsExpression = BuildContainsExpression(swappedParamExpression, searchTermExpression);

                orExpression = BuildOrExpression(orExpression, containsExpression);
            }
        }

        var completeExpression = Expression.Lambda<Func<T, bool>>(orExpression, singleParameter);
        return source.Where(completeExpression);
    }

The helper methods and SwapExpressionVisitor can all be found on my SearchExtension github project.

Using it

The new extension method can be used as follows (given source variable is of type IQueryable<Person> with FirstName and Nickname string properties)

var searchTerms = new List<string>{ "john", "fred", "barry" };
var result = source.Search(searchTerms, p => p.FirstName, 
                                        p => p.Nickname).ToList();

The result

When using a sql provider, the above produces the following sql

SELECT [Columns]
FROM [dbo].[People] AS [Extent1]
WHERE ([Extent1].[FirstName] LIKE N'%john%') 
   OR ([Extent1].[Nickname] LIKE N'%john%') 
   OR ([Extent1].[FirstName] LIKE N'%fred%') 
   OR ([Extent1].[Nickname] LIKE N'%fred%') 
   OR ([Extent1].[FirstName] LIKE N'%barry%') 
   OR ([Extent1].[Nickname] LIKE N'%barry%')    

Perfect...

Don't forget to download the package via nuget. I'll be releasing regular updates so please get in touch if you would like particular functionality added to the library.

If you found this post useful please share using the icons above or get in touch if you have any questions.

Comments 2

alex says: 3737 days ago

Very cool! Thought about building something similar a few times but never had the time to do so. Now i just have to type nuget. Great work

John says: 3736 days ago

Hi Alex,

Thanks for your support. Stay tuned as I plan to extend SearchExtensions to accommodate some additional search functionality

Leave a message...

20 Apr
2024