mer*_*kel 3 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));
}
Run Code Online (Sandbox Code Playgroud)
稍后获取结果总数、分页和执行查询:
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();
Run Code Online (Sandbox Code Playgroud)
因为我需要进行全字匹配而不是部分匹配,所以我正在使用正则表达式处理关键字并从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;
Run Code Online (Sandbox Code Playgroud)
题
有谁知道用 EF 进行全词匹配并进行结果分页的方法吗?
我想出但不喜欢的想法:
将结果分块。返回 100 个结果,删除 20 个部分关键字匹配,再获取 20 个,重复。当一次获取所有数据会更快时,这可能会导致执行多个查询。这也意味着它会窃取下一页的潜在结果,这些结果必须用某种偏移量进行跟踪。
取回所有行(无 SQL 分页),然后在 C# 中处理和分页。每次都返回所有结果似乎很糟糕。
好吧,我看到了两种选择(我可能会错过一些更简单的东西,但无论如何)
要么使用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));
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1731 次 |
| 最近记录: |