6
\$\begingroup\$

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())));
\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

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));
\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Mar 20, 2013 at 9:07
1
\$\begingroup\$

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);
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ "AU" must be in the beginning of any word in the ManufactuerName || ModelName || Name \$\endgroup\$ Commented Mar 20, 2013 at 9:08
  • \$\begingroup\$ @Chuck - Ah, there could be multiple words in each field? That wasn't clear from your example. Edited. \$\endgroup\$
    – Bobson
    Commented Mar 20, 2013 at 13:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.