【问题标题】:Searching for each word in string搜索字符串中的每个单词
【发布时间】:2017-04-05 13:21:01
【问题描述】:

我仍在努力了解 LINQ 及其功能。

我正在尝试构建搜索。例如,我想返回匹配搜索字符串(我们称之为搜索词)的结果。例如,在数据库中,我们有如下结果:

ID    ItemNum    CategoryType
1     2737       Full Length Dresses
2     5353       Full Length Dresses

如果有人搜索“Full Dresses”,我希望能够返回上面的两个结果。但是,使用Contains 不起作用。搜索“全长连衣裙”确实有效。我猜我需要以某种方式将搜索字符串拆分为一个数组,然后使用数组中的每个项目进行搜索,但我不知道该怎么做。

var results = db.Products.Where(p => p.CategoryType.Contains(searchString)).Select(p => p).Distinct();

谢谢。

【问题讨论】:

  • 如果 searchString 等于 "Full Foo" 两个项目都应该返回吗?
  • 这两个项目是什么意思?您的意思是每个项目与两个项目一起?
  • 如果搜索字符串是Full Foo,那么FullFull Length Dresses 的一部分,但Foo 不是。应该是结果的两个词的一部分或至少一个
  • 理想情况下,我想拥有它,这样它就可以是搜索词的 AND 或 OR。

标签: c# linq


【解决方案1】:

如果您的底层数据存储是 Sql Server,并且术语的顺序很重要,那么您可以使用:

searchString = searchString.Replace(" ", "%");
var results = db.Products.Where(p => SqlFunctions.PatIndex(searchString , p.CategoryType) > 0).Distinct();

否则,您可能必须拆分条款并分别处理:

var terms = searchTerms.Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var results = db.Products.Where(p => terms.All(x => p.CategoryType.Contains(x))).Distinct()

并且,如果您希望它返回 任何 匹配项(因此,OR 而不是 AND)使用 Any linq 函数而不是 All linq 函数:

var terms = searchTerms.Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var results = db.Products.Where(p => terms.Any(x => p.CategoryType.Contains(x))).Distinct()

【讨论】:

    【解决方案2】:

    由于您的 LINQ 将被转发到底层数据库执行,许多 .NET 函数不能直接在查询中使用(因为在 DB 级别没有规范的等效函数)。您应该首先将所有类别提取到内存中,然后使用全面的 LINQ-to-Objects 来过滤您的结果:

    应该这样做:

    var results = db.Products.Select(p => p.CategoryType).ToArray();
    var Matches = results.Where(p => searchString.Split(new []{' '}).Any(x => p.Contains(x)).Distinct();
    

    我假设您的搜索字符串中的单词将用空格分隔。如果还有其他标点符号,您可以将它们全部添加到上面的数组中。如果空格是您唯一的分隔符,则上面的第二行可以简化为:

    var Matches = results.Where(p => searchString.Split(' ').Any(x => p.Contains(x)).Distinct();
    

    【讨论】:

    • 我收到此错误:Additional information: LINQ to Entities does not recognize the method 'System.String[] Split(Char[])' method, and this method cannot be translated into a store expression.
    【解决方案3】:

    我会用一些用户友好的东西来扩展它:

    string[] keywords = searchString.Split(' ').Where(x=>!x.Equals(""));
        //split by space and leave double spaces (because might be an empty string between two spaces)
    var results = db.Products.Where(p => keywords.Any(x => p.CategoryType.Contains(x.ToLower())).Distinct();
       //users will tyle only lowercase characters, so you need `String.ToLower()`.
    

    【讨论】:

      【解决方案4】:

      试试这个(这将(当然)只适用于 LINQ to SQL):

      string[] str = searchString.split();
      string newSearchString ="%";
      
      foreach (string item in str)
      {
         newSearchString += item + "%"; 
      }
      
      var results = from p in db.products
                    where SqlMethods.Like(p.CategoryType , newSearchString  )
                    select p;
      

      【讨论】:

        【解决方案5】:
            IEnumerable<string> values = "Full,Dresses".Split(',');
        var matchingRows = from row in datatable.AsEnumerable()
                           join value in values
                           on row.Field<string>(CategoryType) equals value
                           select row;
        DataTable tblResult = matchingRows.CopyToDataTable();
        

        【讨论】:

          【解决方案6】:

          将此作为答案发布,评论太长了。

          请记住,如果表很大,这可能会导致性能很差,因为即使您在 CategoryType 上创建常规索引,它也永远不会被使用(除非您对简单的“开头”感到满意" 一种搜索,你不是)。

          我不知道您使用的是哪个 RDBMS,但如果全文搜索可用,我会调查它的使用。如果您使用的是 SQL Server,它看起来像 you can have Linq use a full-text index

          如果您在代码 like @dotNET suggested 中进行搜索,它会变得更好,并且您也有机会实现一些更奇特的东西(查看 Levenshtein 距离算法,它可以让您提供模糊字符串匹配),但是全文搜索是您可能需要牢记的事情,因为如果您最终拥有数百万行,这种方法可能无法很好地扩展。

          【讨论】:

          • 最多只能返回大约 80 行。这是一个相当小的应用程序。
          • @yondaimehokage 哦,好吧,那对你来说不是问题。只是要记住的事情。
          猜你喜欢
          • 2021-10-06
          • 2013-10-22
          • 1970-01-01
          • 2016-12-26
          • 1970-01-01
          • 2013-09-15
          • 1970-01-01
          • 2011-12-27
          相关资源
          最近更新 更多