执行通配符搜索

Posted

技术标签:

【中文标题】执行通配符搜索【英文标题】:Performing a wildcard search 【发布时间】:2022-01-11 10:38:57 【问题描述】:

我搜索了将 LINQ 与 EF 结合使用的地方。当搜索条件为空或为空时,我需要返回所有内容。目前我已经使用 if 条件作为解决方案。然后我转向了这样的解决方案。

data = data
.Where(p => !string.IsNullOrEmpty(searchriteria1)? p.field1.Contains(searchriteria1) : true)
.Where(p => !string.IsNullOrEmpty(searchriteria2)? p.field2.Contains(searchriteria2) : true);

有没有更好的方法来做到这一点?也许使用扩展或任何更好的方法?

【问题讨论】:

标准是什么?这很重要。如果不使用全文搜索索引,这样的搜索将不得不扫描整个表,即使在删除了Where 中的不必要检查之后也是如此。全文搜索只能使用完整的单词和句子,以及它们的各种形式。如果你想通过搜索ta 来检索potato,你需要一些不同的东西。 【参考方案1】:

您可以以前检查搜索条件字段并以这种方式构建查询:

IQueryable<Foo> data  = context.Foo.AsQueryable();

if(!string.IsNullOrEmpty(searchriteria1))

    data = data.Where(p => p.field1.Contains(searchriteria1));

if (!string.IsNullOrEmpty(searchriteria2))

    data = data.Where(p => p.field2.Contains(searchriteria2));

【讨论】:

【参考方案2】:

这个问题有两个部分。如何动态过滤,如何高效过滤。

动态标准

对于第一个问题,使用 LINQ 时无需进行全面查询。 Catch-all queries result in inefficient execution plans,所以最好避开它们。

虽然 LINQ 不是 SQL。您可以逐部分构建查询。只有当您尝试枚举最终查询时,它才会被转换为 SQL。这意味着你可以写:

if(!String.IsNullOrEmpty(searchCriteria1))

    query=query=.Where(p=>p.Field1.Contains(searchCriteria1);

您可以链接多个Where 调用以获得多个AND 条件的等效项。

要使用例如OR 生成更复杂的查询,您必须构造正确的Expression&lt;Func&lt;...,bool&gt;&gt; 对象,或者使用LINQKit 之类的库来使这个可以忍受。

效率

能否编写高效的查询取决于搜索条件。子句field LIKE '%potato%' 不能使用任何索引,最终会扫描整个表。

另一方面,field LIKE 'potato% 可以利用覆盖field 的索引,因为它将转换为像field &gt;='potato' and field&lt;='potatp 这样的范围搜索。

如果您想实现自动完成或拼写检查,您通常希望找到与标准差异最小的文本。

全文搜索

您可以使用Full-Text Search 索引和FTS 函数(如CONTAINS 或FREETEXT)有效地搜索单词、单词变体甚至完整短语。

FTS 类似于 Google 或 ... *** 搜索单词或句子的方式。

引用文档:

CONTAINS 可以搜索:

一个词或短语。 单词或短语的前缀。 一个词靠近另一个词。 从另一个词屈折产生的词(例如,单词 drive 是drives、driven、driving 和driven 的屈折词干)。 使用同义词库作为另一个词的同义词的词(例如,“金属”一词可以有“铝”和“钢”等同义词)。

另一方面,FREETEXT 更接近 Google/SO 的工作方式,通过搜索整个短语,返回紧密匹配,而不仅仅是精确匹配。

通过DbFunctions.Contains 和DbFunctions.FreeText 函数,EF Core 5 及更高版本中都可以使用 CONTAINS 和 FREETEXT。

这意味着如果你想搜索一个词或短语,你可以构造一个适当的 FTS 参数并使用:

var searchCriteria1="' Mountain OR Road '";
if(!String.IsNullOrEmpty(searchCriteria1))

    query=query=.Where(p=>DbFunctions.Contains(p.Field1.Contains(searchCriteria1));

这比使用 LINQKit 容易得多。

或搜索ride, ride, ridded with :

var searchCriteria1="' FORMSOF (INFLECTIONAL, ride) '";

【讨论】:

query=query=.Where(p=&gt;DbFunctions.Contains(p.Field1.Contains(,searchCriteria1)); 无法编译 糟糕。删除了多余的逗号 不错,还有 2 个错误需要编译【参考方案3】:

更短的语法

data.Where(p => (string.IsNullOrEmpty(searchriteria1) || p.field1.Contains(searchriteria1)) 
             && (string.IsNullOrEmpty(searchriteria2) || p.field2.Contains(searchriteria2)));

【讨论】:

Where 内部的检查只会导致错误的查询执行计划。 LINQ 根本不需要它。这是可以投票的临界点,尤其是因为正确的语法已经作为答案发布了 Where(x =&gt; true) 不会导致错误的查询执行计划 这不是你写的。整个 表达式 将被翻译成 SQL。您发布的内容将被翻译成类似WHERE @criteria is not null and len(@criteria)&gt;1 and field LIKE @criteria)=1 的内容。即使这被简化为WHEN (@criteria is not null and field LIKE @criteria),你也会有一半的时间得到一个糟糕的执行计划。那是因为第一次调用生成的执行计划,即使不合适,也会被缓存并重用。 problems with catch-all queries 众所周知【参考方案4】:
 public static List<Test> getAll(Expression<Func<Test, bool>> filter = null)
     
        return filter == null ? context.Set<Test>().ToList() : context.Set<Test>().Where(filter).ToList();
      
    

如果要过滤

var l=getAll(p => p.field1.Contains(searchriteria1)&&p.field2.Contains(searchriteria2));

没有过滤器

var l=getAll();

【讨论】:

以上是关于执行通配符搜索的主要内容,如果未能解决你的问题,请参考以下文章

在 Hive 中,如何使用“regexp_replace()”对标记值执行通配符搜索,以将其替换为公共值?

Elasticsearch上的短语和通配符查询

Linux常用命令-文件搜索命令find

SQL必知必会 -------- 通配符计算字段函数

Elasticsearch 前缀搜索、通配符搜索、正则搜索、不匹配搜索

DSE:通配符搜索