3

I have a list which contains the name of suppliers. Say

SuppId   Supplier Name
----------------------------------
1        Aardema & Whitelaw
2        Aafedt Forde Gray
3        Whitelaw & Sears-Ewald

using following LINQ query

supplierListQuery = supplierListQuery.Where(x => x.SupplierName.Contains(SearchKey));

I can return records correctly in the following conditions,

1) If i am using search string as "Whitelaw & Sears-Ewald" it will return 3rd record.

2) If i am using "Whitelaw" or "Sears-Ewald" it will return 3rd record.

But how can i return 3rd record if i am giving search string as "Whitelaw Sears-Ewald". It always returns 0 records.

Can i use ALL to get this result, but i dont know how to use it for this particular need.

Nithin Paul
  • 2,169
  • 3
  • 33
  • 55
  • 1
    Building an intelligent search engine is a non-trivial matter. There's *a lot* of language parsing and logic that goes into it, much more than a single LINQ statement. – David Jul 13 '15 at 12:10
  • 2
    What you need is fuzzy searching which LINQ doesn't do out of the box. Have a read of this http://stackoverflow.com/questions/13162830/forgiving-fuzzy-search-with-linq – timothyclifford Jul 13 '15 at 12:10
  • Is your requirement here to treat "Whitelaw" and "Sears-Ewald" as separate search terms and return any record which contains at least one search term? – Max Jul 13 '15 at 12:13
  • @Max Yes, ie sometimes the users may search using "Whitelaw Sears-Ewald" in the place of "Whitelaw & Sears-Ewald". But in both cases we need to return that 3rd record – Nithin Paul Jul 13 '15 at 12:15
  • I would suggest using RegEx for this. You can build a pattern given your input such as pattern= String.split(" ",input).select(s=>"("+s+")").join(" ") – Mark Jul 13 '15 at 12:15
  • You can use replace string function to both side – Mukesh Kalgude Jul 13 '15 at 12:17
  • @timothyclifford yes i will check in that way and will update you – Nithin Paul Jul 13 '15 at 12:19
  • Duplicate http://stackoverflow.com/questions/471502/what-is-linq-and-what-does-it-do – Croll Jul 13 '15 at 12:19

6 Answers6

8

What I usually do in this situation is split the words into a collection, then perform the following:

var searchopts = SearchKey.Split(' ').ToList();
supplierListQuery = supplierListQuery
    .Where(x => searchopts.Any(y=> x.SupplierName.Contains(y)));
Captain Kenpachi
  • 6,960
  • 7
  • 47
  • 68
  • Seems reasonable to me, would any of the -1 elaborate? – Max Jul 13 '15 at 12:25
  • 2
    That was too useful. Thanks – Nithin Paul Jul 13 '15 at 12:32
  • This approach is very good when words are separated by a space and the search request does not contain words longer than those in which the search occurs. For example it will not work for the search query: 1. "WhitelawSears-Ewald" because it has no space between words. 2. "Aafedtzzzzz" because it's longer then word 'Aafedt' so 'Aafedt' cannot contain it. – Fabjan Jul 13 '15 at 13:32
  • You can simply swap x.SupplierName.Contains(y) with y.Contains(x.SupplierName) to allow that – Captain Kenpachi Jul 13 '15 at 13:56
1

Thank you all for your quick responses. But the one which worked or a easy fix to handle this was timothyclifford's note on this. Like he said i alterd my answer to this

string[] filters = SearchKey.ToLower().Split(new[] { ' ' });
objSuppliersList = (from x in objSuppliersList
                    where filters.All(f => x.SupplierName.ToLower().Contains(f))
                    select x).ToList();

Now it returns the result for all my serach conditions.

Nithin Paul
  • 2,169
  • 3
  • 33
  • 55
  • Try to search for "Aafedtzzzzz" or "WhitelawSears-Ewald" and see if you get any results ;) – Fabjan Jul 13 '15 at 13:36
  • @Fabjan yes you are right, but when i followed Juann Strauss approach, i was not able to find the result when i use serach key as "Whitelaw Sears-Ewald PLLC". And problem with your approach is that i have more than 10000 supplier records in my db, each time user clicks on serach button i need to perform search on these records. I also need to consider filter queries too. So i am nervous about the overall performance. – Nithin Paul Jul 14 '15 at 04:38
  • By using timothyclifford's approach, now i can handle my basic search queries. So i am going with this approach for the time being. Thanks for notifying me on this. :) – Nithin Paul Jul 14 '15 at 04:50
  • LINQ is not really good in terms of performance. Use it carefully, avoid multiple queries when you can achieve the same result using conjunction / disjunction with single query, for example : .Where(condition1).Where(condition2).Where(condition3) you can just do .Where(condition1 || condition2 || condition3) with same result. – Fabjan Jul 14 '15 at 06:26
1

This works for me:

IEnumerable<string> keyWords = SearchKey.Split('');

supplierListQuery = supplierListQuery
      .AsParallel()
      .Where
      (
         x => keyWords.All
         (
              keyword => x.SupplierName.ContainsIgnoreCase(keyword)
         )
      );
kaan_93
  • 85
  • 1
  • 10
0

You need to use some sort of string comparer to create your own simple Search Engine and then you can find strings that are most likely to be included in your result :

public static class SearchEngine
{

    public static double CompareStrings(string val1, string val2)
    {
        if ((val1.Length == 0) || (val2.Length == 0)) return 0;
        if (val1 == val2) return 100;

        double maxLength = Math.Max(val1.Length, val2.Length);
        double minLength = Math.Min(val1.Length, val2.Length);
        int charIndex = 0;
        for (int i = 0; i < minLength; i++) { if (val1.Contains(val2[i])) charIndex++; }

        return Math.Round(charIndex / maxLength * 100);
    }

    public static List<string> Search(this string[] values, string searchKey, double threshold)
    {
        List<string> result = new List<string>();
        for (int i = 0; i < values.Length; i++) if (CompareStrings(values[i], searchKey) > threshold) result.Add(values[i]);
        return result;
    }
}

Example of usage :

string[] array = { "Aardema & Whitelaw", "Aafedt Forde Gray", "Whitelaw & Sears-Ewald" };

var result = array.Search("WhitelawSears-Ewald", 80);
// Results that matches this string with 80% or more

foreach (var item in result)
{
   Console.WriteLine(item);
}

Output: Whitelaw & Sears-Ewald

Fabjan
  • 13,506
  • 4
  • 25
  • 52
0

Because "Whitelaw" appears in both you will get both records. Otherwise there is no dynamic way to determine you only want the last one. If you know you only have these 3 then append .Last() to get the final record.

supplierListQuery = supplierListQuery.Where(x => x.SupplierName.Contains(SearchKey.Split(' ')[0]));
Stephen Brickner
  • 2,584
  • 1
  • 11
  • 19
  • The answer you chose only works because Whitelaw is first in your string. Doing a true search you would want it to find all records matching Whitelaw then filter from there to find the one you want. You could have just as easily done this: supplierListQuery = supplierListQuery..Where(x => x.SupplierName.StartsWith(SearchKey.Split()[0])); to get just the record that matches the first word in your query. – Stephen Brickner Jul 13 '15 at 13:24
-1

If you want an easy (not very handy) solution,

var result = supplierListQuery
                      .Select(x => normalize(x.SupplierName))
                      .Where(x => x.Contains(normalize(SearchKey)));

string normalize(string inputStr)
{
    string retVal = inputStr.Replace("&", "");
    while (retVal.IndexOf("  ") >= 0)
    {
        retVal = retVal.Replace("  ", " ");
    }
    return retVal;
}
serdar
  • 1,564
  • 1
  • 20
  • 30