我有一个带有搜索/过滤字段的简单自定义表。我将搜索的实现留给每次使用表。
所以,让我们说我的桌面上有用户,我想搜索它们。我想在用户的firstname,lastname以及他们所处的任何角色中搜索。
这可能会成功
searchString = searchString.ToLower();
query = query.Where(
x =>
x.FirstName.ToLower().Contains(searchString)
||
x.LastName.ToLower().Contains(searchString)
||
x.Roles.Any(
role =>
role.Name.ToLower().Contains(searchString)
)
);
但现在我想搜索/过滤多个单词。首先,我得到一系列单独的单词。
var searchStrings = searchString.ToLower().Split(null);
我尝试了以下操作,但它不符合我的要求,因为它返回任何字段在任何字段中匹配的任何用户。我需要所有的单词都匹配(但可能在不同的领域)。有关详细信息,请参阅下文。
query = query.Where(
x =>
searchStrings.Any(word => x.FirstName.ToLower().Contains(word))
||
searchStrings.Any(word => x.LastName.ToLower().Contains(word))
//snipped away roles search for brevity
);
首先让我制作一些数据
Billy-James Carter是管理员兼经理
James Carter是经理
Billy Carter没有任何作用
如果我的搜索字符串是" billy car"我希望比利 - 詹姆斯和比利回来但不是詹姆斯卡特(所以所有的话必须匹配,但不能在相同的领域)。
如果我的搜索字符串是" bil jam"甚至是“堵车”#34;我只希望Billy-James回来,因为他是唯一一个匹配所有词汇/单词的人。因此,在FirstName字段中找到了bil和jam这两个词,而在LastName字段中找到了car term。只得到"汽车"部分正确是不够的,詹姆斯不会回来。
如果我搜索"汽车人"比利 - 詹姆斯和詹姆斯都是经理(人),并且名叫卡特,应该出现。我应该搜索" car man admi"然后只有比利 - 詹姆斯应该出现。
如果建议更好,我很乐意放弃目前的方法。
答案 0 :(得分:4)
我想不出一种方法可以将您正在寻找的内容包装到单个LINQ语句中。可能有一种方法,但我知道EF的选项比对象集合上的LINQ更有限。有了这个,为什么不用分割中的第一个单词从数据库中获取结果集,然后进一步过滤生成的集合?
var searchWords = searchString.ToLower().split(' ');
var results = dataBase.Where(i => i.FirstName.ToLower().Contains(searchWords[0])
|| i.LastName.ToLower().Contains(searchWords[0])
|| i.Role.ToLower().Contains(searchWords[0]));
if(searchWords.Length > 1) {
for(int x = 1; x < searchWords.Length; x++) {
results = results.Where(i => i.FirstName.ToLower().Contains(searchWords[x])
|| i.LastName.ToLower().Contains(searchWords[x])
|| i.Role.ToLower().Contains(searchWords[x]));
}
}
结果集的最终内容将是您正在寻找的内容。
免责声明:我没有准备好对此进行测试,因此可能需要使用.ToList()来完成这项工作,但它基本上是可用的。
更新:有关EF和延迟执行以及字符串集搜索的更多信息
鉴于我们有架构:
Employee:
FirstName - String
Last Name - String
Roles - One to Many
Role:
Name - String
以下内容将为您要查找的所有内容构建查询
var searchTerms = SearchString.ToLower().Split(null);
var term = searchTerms[0];
var results = from e in entities.Employees
where (e.FirstName.Contains(term)
|| e.LastName.Contains(term)
|| e.Roles.Select(r => r.Name).Any(n => n.Contains(term)))
select e;
if (searchTerms.Length > 1)
{
for (int i = 1; i < searchTerms.Length; i++)
{
var tempTerm = searchTerms[i];
results = from e in results
where (e.FirstName.Contains(tempTerm)
|| e.LastName.Contains(tempTerm)
|| e.Roles.Select(r => r.Name).Any(n => n.Contains(tempTerm)))
select e;
}
}
此时查询仍未执行。在循环中过滤结果集时,实际上是在搜索条件中添加了其他AND子句。查询不会执行,直到您运行一个命令,该命令对结果集执行某些操作,如ToList(),迭代集合等。在构建查询的所有内容之后放置一个断点并查看它。 LINQ to SQL既有趣又强大。
需要解释的一件事是变量tempTerm
。我们需要一个在循环中作用域的变量,这样我们就不会为引用变量term
的查询中的所有参数设置一个值。
答案 1 :(得分:0)
我简化了一下
//we want to search/filter
if (!string.IsNullOrEmpty(request.SearchText))
{
var searchTerms = request.SearchText.ToLower().Split(null);
foreach (var term in searchTerms)
{
string tmpTerm = term;
query = query.Where(
x =>
x.Name.ToLower().Contains(tmpTerm)
);
}
}
我构建了一个更大的查询,其中搜索只是一个部分,从这个开始
var query = _context.RentSpaces.Where(x => x.Property.PropertyId == request.PropertyId).AsQueryable();
以上搜索仅使用一个字段,但对于更复杂的字段应该可以正常工作。就像在我的用户示例中一样。
答案 2 :(得分:0)
我通常采用apporach排序查询。如果您查看诊断工具,它们都会在数据库中一步执行:
IQueryable<YourEntity> entityQuery = context.YourEntity.AsQueryable();
foreach (string term in serchTerms)
{
entityQuery = entityQuery.Where(a => a.YourProperty.Contains(term));
}