使用LINQ如何进行单词搜索?

3

我有一个包含供应商名称的列表。假设:

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

使用以下的LINQ查询:
supplierListQuery = supplierListQuery.Where(x => x.SupplierName.Contains(SearchKey));

我可以在以下情况下正确返回记录:
1)如果我使用搜索字符串“Whitelaw & Sears-Ewald”,它将返回第三条记录。
2)如果我使用“Whitelaw”或“Sears-Ewald”,它将返回第三条记录。
但是,如果我将搜索字符串设置为“Whitelaw Sears-Ewald”,如何才能返回第三条记录呢?它总是返回0条记录。
我可以使用ALL来获取此结果,但我不知道如何在此特定需求中使用它。

1
构建一个智能搜索引擎是一项非常复杂的任务。这需要大量的语言解析和逻辑处理,远远超过单个LINQ语句所能实现的范围。 - David
2
你所需的是模糊搜索,LINQ默认并不支持。看看这个链接:https://dev59.com/MmYs5IYBdhLWcg3wE_3e - timothyclifford
您的要求是将“Whitelaw”和“Sears-Ewald”视为单独的搜索词,并返回包含至少一个搜索词的任何记录吗? - Max
@Max 是的,有时用户可能会使用“Whitelaw Sears-Ewald”而不是“Whitelaw&Sears-Ewald”进行搜索。 但在这两种情况下,我们都需要返回第三条记录。 - Nithin Paul
我建议使用正则表达式来完成这个任务。你可以根据输入构建一个模式,例如 pattern= String.split(" ",input).select(s=>"("+s+")").join(" ")。 - Mark
显示剩余3条评论
6个回答

8

在这种情况下,我通常会将单词拆分成一个集合,然后执行以下操作:

var searchopts = SearchKey.Split(' ').ToList();
supplierListQuery = supplierListQuery
    .Where(x => searchopts.Any(y=> x.SupplierName.Contains(y)));

对我来说似乎很合理,-1 的任何人能详细说明吗? - Max
2
那太有用了。谢谢。 - Nithin Paul
当单词之间用空格分隔且搜索请求不包含比搜索出现的单词更长的单词时,此方法非常有效。例如,对于搜索查询1.“WhitelawSears-Ewald”,它没有单词之间的空格;2.“Aafedtzzzzz”,因为它比单词“Aafedt”更长,所以“Aafedt”不能包含它,因此该方法无法使用。 - Fabjan
你可以简单地将 x.SupplierName.Contains(y) 替换为 y.Contains(x.SupplierName),以允许这样做。 - Captain Kenpachi

1

感谢大家的快速反应。但是解决这个问题的方法之一,也是一个简单的修复方法,是由 timothyclifford 提供的说明。就像他所说的那样,我修改了我的答案为:

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

现在它会返回我所有搜索条件的结果。

尝试搜索"Aafedtzzzzz"或"WhitelawSears-Ewald"并查看是否有任何结果;) - Fabjan
@Fabjan 是的,你说得对。但是当我按照Juann Strauss的方法操作时,使用“Whitelaw Sears-Ewald PLLC”作为搜索关键字时,我无法找到结果。而你的方法存在问题,因为我的数据库中有超过10000条供应商记录,每次用户点击搜索按钮时,我都需要在这些记录上执行搜索。我还需要考虑筛选查询。所以我对整体性能感到紧张。 - Nithin Paul
通过使用timothyclifford的方法,现在我可以处理我的基本搜索查询。因此我暂时采用这种方法。谢谢你通知我这件事。 :) - Nithin Paul
LINQ在性能方面并不是很好。使用时需谨慎,尽可能避免多次查询,可以使用联合/离合的单个查询来达到同样的结果,例如:.Where(condition1).Where(condition2).Where(condition3),可以用 .Where(condition1 || condition2 || condition3) 来实现相同的结果。 - Fabjan

1
这对我有效:
IEnumerable<string> keyWords = SearchKey.Split('');

supplierListQuery = supplierListQuery
      .AsParallel()
      .Where
      (
         x => keyWords.All
         (
              keyword => x.SupplierName.ContainsIgnoreCase(keyword)
         )
      );

0

因为“Whitelaw”出现在两个记录中,所以您将获得这两个记录。否则,没有动态的方法可以确定您只想要最后一个记录。如果您知道您只有这3个记录,则附加.Last()以获取最终记录。

supplierListQuery = supplierListQuery.Where(x => x.SupplierName.Contains(SearchKey.Split(' ')[0]));

你选择的答案只能起作用是因为Whitelaw在你的字符串中排在第一位。如果要进行真正的搜索,你希望它找到所有与Whitelaw匹配的记录,然后从中过滤出你想要的记录。你也可以这样做:supplierListQuery = supplierListQuery..Where(x => x.SupplierName.StartsWith(SearchKey.Split()[0]));,以获取与查询的第一个单词匹配的记录。 - Stephen Brickner

0
你需要使用某种字符串比较器来创建自己的简单搜索引擎,然后你就可以找到最有可能包含在结果中的字符串:
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;
    }
}

使用示例:

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);
}

输出:Whitelaw&Sears-Ewald


-1
如果你想要一个简单(但不太方便)的解决方案,
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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接