Left hand search in string

the requirement for our search query is to look for all search words in the beginning of vehicle manufacturer, model and variant name. Case insensitive. The word being a string of characters separated by space.

E.g. when search for "AU A3 TDI" you would find a vehicle with the following attributes: Vehicle { ManufacturerName = "Audi", ModelName = "A3", Name = "2.0L TDI" }

...but would not return this vehicle: Vehicle { ManufacturerName = "Renault", ModelName = "A3", Name = "2.0L TDI" } because no words start with au.

I'm using specification with LINQ Expression and the code I wrote works fine and is tested however I believe it smells a bit. It's not very readable IMHO but most importantly, there might be a clear performance improvement you could point out?

public string[] SearchTerms = searchText.Split();

Expression<Func<SearchVehicleVariant, bool>> expression =
c =>
SearchTerms.All(
s =>
string.Format(
" {0} {1} {2}",
c.ManufacturerName != null ? c.ManufacturerName.ToUpper() : string.Empty,
c.ModelName != null ? c.ModelName.ToUpper() : string.Empty,
c.Name != null ? c.Name.ToUpper() : string.Empty).Contains(
string.Concat(" ", s.Trim().ToUpper())));


Given your requirements, it seems you would be best using String.StartsWith and a StringComparison value which ignores case (i.e., CurrentCultureIgnoreCase, InvariantCultureIgnoreCase, or OrdinalIgnoreCase). This should be a little cleaner and faster than doing case conversions.

Expression<Func<SearchVehicleVariant, bool>> expression =
c => SearchTerms.All (
(c.ManufacturerName ?? string.Empty).StartsWith (s, StringComparison.InvariantCultureIgnoreCase)
|| (c.ModelName ?? string.Empty).StartsWith (s, StringComparison.InvariantCultureIgnoreCase)
|| (c.Name ?? string.Empty).StartsWith (s, StringComparison.InvariantCultureIgnoreCase));

• thanks. sorry but this wouldn't fulfil my requirements. The search is done on every word, not just the first word of each of the attributes. I've modified the search criterion in my q to make it clearer. – Иван Грозный Mar 20 '13 at 9:07

I'm not entirely sure how your second example fails to match, because the string "Renault A3 2.0L TDI" contains all of "AU", "A3", and "2.0".

That being said, here's an attempt at a cleaner rewrite:

Expression<Func<SearchVehicleVariant, bool>> expression =
c => SearchTerms.Select(s => s.Trim())
.All(s =>
TestField(c.ManufacturerName ?? "", s)
|| TestField(c.ModelName ?? "", s)
|| TestField(c.Name ?? "", s)
);

// Helper function
bool TestField(string field, string test)
{
return field.Split(' ').Any(x => x.StartsWith(s, StringComparison.InvariantCultureIgnoreCase);
}

• "AU" must be in the beginning of any word in the ManufactuerName || ModelName || Name – Иван Грозный Mar 20 '13 at 9:08
• @Chuck - Ah, there could be multiple words in each field? That wasn't clear from your example. Edited. – Bobson Mar 20 '13 at 13:47