背景:
我正在为网站使用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进行全字匹配并进行结果分页?
我想出了但不喜欢的想法:
结果。返回100个结果,删除20个部分关键字匹配,再获取20个,重复。这可能导致在一次获取所有数据时执行多次查询会更快。这也意味着它将窃取下一页的潜在结果,这些结果必须以某种偏移量进行跟踪。
获取所有行(无SQL分页),然后在C#中处理和分页。每次都得到所有结果似乎很糟糕。
答案 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));
}