服务器评估使用 linq 和 c# 搜索多个关键字
Server evaluated search for multiple keywords using linq and c#
看似微不足道的小事,在Whosebug和网上搜索都没有找到好的解决方案。
我正在实现简单的搜索功能,将字符串作为输入并提取关键字。下一步是将关键字应用到查询中。我尝试在下面编写代码,但 linq 抛出一个错误,使用 .AsEnumerable
从服务器评估更改为客户端评估
但我想在服务器上进行评估,并且我想在一次往返中尽可能多地进行评估。我想我的代码中的问题与 .Any/.Contains 模式有关,但我希望有人能提供帮助。
public class Person
{
public string Name { get; set; }
public string Email { get; set; }
}
List<string> keywords = new List<string> { "John", "Doe" };
IEnumerable<Person> persons = await context.PersonDbSet
.Where(person => keywords.Any(keyword => person.Name.Contains(keyword))
|| keywords.Any(keyword => person.Email.Contains(keyword)))
.ToListAsync();
您可以将 .Contains 与名称一起使用,但不能同时与电子邮件一起使用,试试这个
var persons = await context.PersonDbSet
.Where(person =>
EF.Functions.Like(person.Name, "%John%")
|| EF.Functions.Like(person.Name, "%Doe%")
|| EF.Functions.Like(person.Email, "%John%")
|| EF.Functions.Like(person.Email, "%Doe%")
).ToListAsync();
It seems like a trivial task
,它不是,它是最低效的查询之一。这就是几乎所有自动完成过滤器都使用 StartsWith 而不是 Contains 的原因。或者使用像 Elastic 这样的专用全文搜索引擎。
看起来您尝试做的是生成一系列由 OR
组合的 LIKE '%..%'
子句。像这样的子句不能利用任何索引,所以最终会扫描整个 table.
而 Any
在布尔代数中。相当于多个 OR
表达式,EF Core 无法进行这样的翻译。 SQL 有自己的 ALL
和 ANY
关键字,它们引用结果集中的所有元素,而不是所有条件。
要生成您想要的查询,您必须明确指定表达式,或者使用像 LINQKit 这样的库来使代码更简洁。它仍将执行完整的 table 扫描:
IQueryable<Person> SearchPersons (params string[] keywords)
{
var predicate = PredicateBuilder.New<Person>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Name.Contains (temp))
.Or (p => p.Email.Contains (temp));
}
return dataContext.Persons.AsExpandable().Where (predicate);
}
...
var persons=await SearchPersons(keywords).ToListAsync();
使用全文搜索
要使此查询高效,您必须使用 SQL 服务器的 Full Text Search indexes. The CONTAINS 运算符可用于搜索一个或多个字段中的单词、另一个字段附近的单词等。示例表明以下查询是可能的:
SELECT * FROM PERSONS WHERE CONTAINS( (Name,Email), 'John')
和
SELECT * FROM PERSONS WHERE CONTAINS( Name , 'John OR Doe')
所以我想两者可以结合
SELECT * FROM PERSONS WHERE CONTAINS((Name,Email) , 'John OR Doe')
在 EF Core 中执行相同操作可以使用 EF.Contains 并将所有关键字与 OR
组合:
IQueryable<Person> SearchPersons (params string[] keywords)
{
var term=String.Join(" OR ",keywords);
return dataContext.Where( p => EF.Contains(p.Email,term)
|| EF.Contains(p.Name,term));
}
您也可以使用FromSqlRaw
来准确指定您想要的条件:
var query="SELECT * FROM PERSONS WHERE CONTAINS((Name,Email) , @term)";
var term=String.Join(" OR ",keywords);
var query=dataContext.Persons.FromSqlRaw(query,term);
看似微不足道的小事,在Whosebug和网上搜索都没有找到好的解决方案。
我正在实现简单的搜索功能,将字符串作为输入并提取关键字。下一步是将关键字应用到查询中。我尝试在下面编写代码,但 linq 抛出一个错误,使用 .AsEnumerable
从服务器评估更改为客户端评估但我想在服务器上进行评估,并且我想在一次往返中尽可能多地进行评估。我想我的代码中的问题与 .Any/.Contains 模式有关,但我希望有人能提供帮助。
public class Person
{
public string Name { get; set; }
public string Email { get; set; }
}
List<string> keywords = new List<string> { "John", "Doe" };
IEnumerable<Person> persons = await context.PersonDbSet
.Where(person => keywords.Any(keyword => person.Name.Contains(keyword))
|| keywords.Any(keyword => person.Email.Contains(keyword)))
.ToListAsync();
您可以将 .Contains 与名称一起使用,但不能同时与电子邮件一起使用,试试这个
var persons = await context.PersonDbSet
.Where(person =>
EF.Functions.Like(person.Name, "%John%")
|| EF.Functions.Like(person.Name, "%Doe%")
|| EF.Functions.Like(person.Email, "%John%")
|| EF.Functions.Like(person.Email, "%Doe%")
).ToListAsync();
It seems like a trivial task
,它不是,它是最低效的查询之一。这就是几乎所有自动完成过滤器都使用 StartsWith 而不是 Contains 的原因。或者使用像 Elastic 这样的专用全文搜索引擎。
看起来您尝试做的是生成一系列由 OR
组合的 LIKE '%..%'
子句。像这样的子句不能利用任何索引,所以最终会扫描整个 table.
而 Any
在布尔代数中。相当于多个 OR
表达式,EF Core 无法进行这样的翻译。 SQL 有自己的 ALL
和 ANY
关键字,它们引用结果集中的所有元素,而不是所有条件。
要生成您想要的查询,您必须明确指定表达式,或者使用像 LINQKit 这样的库来使代码更简洁。它仍将执行完整的 table 扫描:
IQueryable<Person> SearchPersons (params string[] keywords)
{
var predicate = PredicateBuilder.New<Person>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Name.Contains (temp))
.Or (p => p.Email.Contains (temp));
}
return dataContext.Persons.AsExpandable().Where (predicate);
}
...
var persons=await SearchPersons(keywords).ToListAsync();
使用全文搜索
要使此查询高效,您必须使用 SQL 服务器的 Full Text Search indexes. The CONTAINS 运算符可用于搜索一个或多个字段中的单词、另一个字段附近的单词等。示例表明以下查询是可能的:
SELECT * FROM PERSONS WHERE CONTAINS( (Name,Email), 'John')
和
SELECT * FROM PERSONS WHERE CONTAINS( Name , 'John OR Doe')
所以我想两者可以结合
SELECT * FROM PERSONS WHERE CONTAINS((Name,Email) , 'John OR Doe')
在 EF Core 中执行相同操作可以使用 EF.Contains 并将所有关键字与 OR
组合:
IQueryable<Person> SearchPersons (params string[] keywords)
{
var term=String.Join(" OR ",keywords);
return dataContext.Where( p => EF.Contains(p.Email,term)
|| EF.Contains(p.Name,term));
}
您也可以使用FromSqlRaw
来准确指定您想要的条件:
var query="SELECT * FROM PERSONS WHERE CONTAINS((Name,Email) , @term)";
var term=String.Join(" OR ",keywords);
var query=dataContext.Persons.FromSqlRaw(query,term);