实体框架和全字匹配 - 分页和过滤结果

时间:2014-06-04 20:08:40

标签: c# sql sql-server asp.net-mvc entity-framework

背景

我正在为网站使用ASP.NET MVC4,SQL Server 2008 R2和Entity Framework 5.

该网站接受分隔的关键字列表以搜索数据库内容。它还需要将结果分页给用户(目前每页100个结果)。

这是顺利进行的,直到请求关键字搜索不是通过部分匹配完成,而是整个单词匹配。

问题

执行整个单词匹配后我已经有结果返回意味着我可能没有显示query.Pagesize个结果 - 这会弄乱UI分页。在第一页上来自SQL Server的100个部分匹配中,20个可能在整个文字处理中被删除。

我目前正在使用LINQ构建查询并对关键字进行AND搜索,如下所示:

// Start with all the MyItems
var results = UnitOfWork.MyItemRepository.GetAll();

// Loop the keywords to AND them together
foreach(var keyword in query.Keywords)
{
    var keywordCopy = keyword;

    // Look for a hit on the keyword in the MyItem
    results = results.Where(x => x.Title.Contains(keywordCopy));
}

稍后获取结果总数,分页和执行查询:

var totalCount = results.Count();

// Page the results
results = results.Skip((query.Page - 1) * query.Pagesize).Take(query.Pagesize);
...
// Finalize the query and execute it
var list = results.ToList();

因为我需要进行全字匹配而不是部分匹配,所以我使用正则表达式处理关键字并从list中删除不匹配。

var keywordsRegexPattern = "^" + string.Concat(query.Keywords.Select(keyword => string.Format(@"(?=.*\b{0}\b)", Regex.Escape(keyword))));

foreach(var item in list.ToList())
{
    var searchableData = some combined string of item data

    // See if the keywords are whole word matched in the combined content
    var isMatch = Regex.IsMatch(searchableData, keywordsRegexPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);

    // If not a match, remove the item from the results
    if(!isMatch)
    {
        list.Remove(item);
    }
}

// Change list into custom list of paged items for the UI
var pagedResult = new PagedList<MyItem>(list, query.Page, query.Pagesize, totalCount);

return pagedResult;

问题

有没有人知道如何与EF进行全字匹配并进行结果分页?

我想出了但不喜欢的想法:

  1. 结果。返回100个结果,删除20个部分关键字匹配,再获取20个,重复。这可能导致在一次获取所有数据时执行多次查询会更快。这也意味着它将窃取下一页的潜在结果,这些结果必须以某种偏移量进行跟踪。

  2. 获取所有行(无SQL分页),然后在C#中处理和分页。每次都得到所有结果似乎很糟糕。

1 个答案:

答案 0 :(得分:4)

嗯,我看到两种选择(我可能会更容易,但无论如何)

要么使用string.Contains(keyword),要从db中检索所有相应的数据,然后使用完全匹配进行过滤,并对枚举结果进行分页(因此您可能从db获得“不太多结果”)。

另一种方式:

foreach(var keyword in query.Keywords)
{
    //add space at start or end of keyword for contains
    var containsKeyword = string.Format(" {0} ", keyword);
    //add space at end only for startsWith
    var startsWithKeyword = string.Format("{0} ", keyword);
    //add space at start only for endsWith
    var endsWithKeyword = string.Format(" {0}", keyword);

    // Look for a hit on the keyword in the MyItem
    results = results.Where(x => x.Title.Contains(containsKeyword) || x.Title.StartsWith(startsWithKeyword) || x.Title.EndsWith(endsWithKeyword));
}