EF过滤/搜索多个单词

时间:2014-10-18 22:05:46

标签: c# linq entity-framework

我有一个带有搜索/过滤字段的简单自定义表。我将搜索的实现留给每次使用表。

所以,让我们说我的桌面上有用户,我想搜索它们。我想在用户的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"然后只有比利 - 詹姆斯应该出现。

如果建议更好,我很乐意放弃目前的方法。

3 个答案:

答案 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既有趣又强大。

More on deferred execution

需要解释的一件事是变量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));
}
相关问题