Reputation: 4500
I need to search products through products in a database and I'm looking to set this up in the right way so that I can have solid performance when there are a lot of rows (1,000,000). I'm somewhat experienced with LINQ and EF, but never written any search algos and I've got the following code, but just have a few lingering questions.
context.products.Where(i => i.Name.ToLower().Contains(searchText.ToLower());
I also need to search the description.
context.products.Where(i => i.Description.ToLower().Contains(searchText.ToLower());
Is .ToLower()
in this context going to reduce performance?
I have a regular index on Name
and FullText
on Description? Is this appropriate and does a regular index work well with .contains()
?
Should I be using LINQ or some other method?
Is there way to do this where I can get the number of times the search text occurs in the name/description?
Thank you
Upvotes: 4
Views: 6168
Reputation: 20004
In my case I didn't want to use stored procedures. I was using Entity Framework and that's what I wanted to use!
See if this method might help you.
public static IQueryable<T> LikeOr<T>(this IQueryable<T> source, string columnName, string searchTerm)
{
IEnumerable<string> words =
searchTerm.Split(new[] {" "}, StringSplitOptions.RemoveEmptyEntries).Where(x => x.Length > 1);
var sb = new StringBuilder();
for (int i = 0; i < words.Count(); i++)
{
if (i != 0)
sb.Append(" || ");
sb.Append(string.Format("{0}.Contains(@{1})", columnName, i));
}
return source.Where(sb.ToString(), words.ToArray());
}
All the above method does it build up a SQL string and then pass it to a Dynamic LINQ Where method. On a high level all this does is allow you to use straight sql only where you need it, rather than writing the entire query in SQL. It's called by something like this:
public List<Book> SearchForBooks(string phrase)
{
return _db.Books.Include(x=> x.Images).LikeOr("Title", phrase).OrderBy(x => x.Title)
.Take(6).Select(x => x).ToList()
.OrderByCountDescending("Title", phrase);
}
It's made possible by dynamic LINQ dll created by Microsoft but not included in the framework. Dynamic LINQ This will allow you to be more flexible in some areas.
Upvotes: 1
Reputation: 3385
Please do not use lower, since it will significantly impact performance.
use one of the following:
StringComparison.OrdinalIgnoreCase
StringComparison.CurrentCultureIgnoreCase
Since you're using contains, which will be translated as like'%text%' it is unlikely that sql server will use indexes. if you implement FullText search, you have to use a stored procedure to use advantages of a fulltext search.
Generally, if you're using the latest entity framework, you should get a pretty good performance since they've had some significant performance improvements in version 5.
Upvotes: 1
Reputation: 4999
I would seriously consider writing a stored procedure to do this, or raw sql in the DbContext using SqlQuery
. If I was writing this code, that is what I would do. To me EntityFramework and performant never really went together.
Upvotes: 1