执行通配符搜索
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<Func<...,bool>>
对象,或者使用LINQKit 之类的库来使这个可以忍受。
效率
能否编写高效的查询取决于搜索条件。子句field LIKE '%potato%'
不能使用任何索引,最终会扫描整个表。
另一方面,field LIKE 'potato%
可以利用覆盖field
的索引,因为它将转换为像field >='potato' and field<='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=>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 => true)
不会导致错误的查询执行计划
这不是你写的。整个 表达式 将被翻译成 SQL。您发布的内容将被翻译成类似WHERE @criteria is not null and len(@criteria)>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()”对标记值执行通配符搜索,以将其替换为公共值?