使用 LINQ 位置/获取返回最相关的搜索结果
本文关键字:搜索结果 返回 LINQ 位置 获取 使用 | 更新日期: 2023-09-27 18:36:48
我有一个List<Locations>
,将被过滤以产生一组与搜索词相关的结果。
目前,我通过以下过滤来尝试这些"搜索结果":
return locations.Where(o => o.Name.Contains(term)).Take(10).ToList();
问题
如果我输入"切斯特"作为搜索词,我将永远不会看到"切斯特"项目,尽管它存在于locations
列表中。原因是列表中有 10 个或更多其他项目的名称中包含字符串"Chester"(曼彻斯特、多切斯特等)。
如何使用 LINQ 首先获取以搜索词开头的结果?
到目前为止我得到了什么
var startsWithList = list.Where(o => o.Name.StartsWith(term)).Take(10).ToList();
var containsList = list.Where(o => o.Name.StartsWith(term) && !startsWithList.Contains(o)).Take(10 - startsWithList.Count).ToList();
return startsWithList.AddRange(containsList);
我根本不喜欢上面的代码。我觉得这应该在一个Where
中实现,而不是执行两个 Where 和 Take's 并将两个列表结合起来。
只需在 Take 之前按升序排序,为以 term 开头的项目设置较低的值。
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.StartsWith(term) ? 0 : 1)
//or OrderByDescending(m => m.Name.StartsWith(term))
.Take(10)
.ToList();
随着MikeT的改进而改编(在StartsWith之前完全匹配),你可以做
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.StartsWith(term)
? (m.Name == term ? 0 : 1)
: 2)
.Take(10)
.ToList();
我创建了一个新的 github 项目,该项目使用表达式树在任意数量的属性中搜索文本
它还具有一个RankedSearch()
方法,该方法返回匹配的项目以及每条记录的命中次数,这意味着您可以执行以下操作:
return locations.RankedSearch(term, l => l.Name)
.OrderByDescending(x => x.Hits)
.Take(10)
.ToList();
如果需要,可以跨多个属性进行搜索
return locations.RankedSearch(term, l => l.Name, l => l.City)
。或多个术语,
return locations.RankedSearch(l => l.Name, term, "AnotherTerm" )
。或同时适用于多个属性和多个术语
return locations.RankedSearch(new[]{term, "AnotherTerm"},
l => l.Name,
l => l.City)
查看这篇文章,了解有关生成的SQL和其他用法的更多信息:http://jnye.co/Posts/27/searchextensions-multiple-property-search-support-with-ranking-in-c-sharp
可以将其作为 nuget 包下载到:https://www.nuget.org/packages/NinjaNye.SearchExtensions/
> Raphaël的解决方案将起作用,但如果你说搜索沃里克,你会发现如果沃里克郡也是一个可能的位置,它可能不会把沃里克放在列表的顶部,使用分数你也可以用更多的匹配方法无限扩展它,以及调整分数值来优化你的搜索顺序
return locations.Select(l => New {SearchResult=l,
Score=(L.Name == Term ?
100 :
l.Name.StartsWith(term) ?
10 :
l.Name.Contains(term) ?
1 :
0
)})
.OrderByDescending(r=>r.Score)
.Take(10)
.Select(r => r.SearchResult);
注意我可能会通过制作一个 Match 方法来做到这一点,并在那里而不是像我上面所做的那样在 linq 中执行逻辑,所以它只是
return locations.OrderByDescending(Match).Take(10);
所有解决方案都可以使用,但更好的分数可以更容易地获得如下
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Name.IndexOf(term))
.Take(10)
.ToList();
因此,包含最接近开头的术语的每个名称都首先显示。
这个呢?
return locations.Where(o => o.Name.Contains(term))
.OrderBy(m => m.Length)
.Take(10)
.ToList();