使用LINQ Where / Take返回最相关的搜索结果

时间:2013-10-24 14:57:40

标签: c# linq list .net-4.0 where

我有一个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并将两个列表组合起来。

5 个答案:

答案 0 :(得分:8)

在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();

答案 1 :(得分:4)

我创建了一个new github project,它使用表达式树来搜索任意数量的属性

中的文本

它还有一个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/

答案 2 :(得分:1)

Raphaël的解决方案可行,但如果您说要搜索Warwick,您可能会发现,如果Warwickshire也是一个可能的位置,它可能不会把Warwick放在首位,使用这些分数你也可以通过更多匹配的方法无限延伸,以及调整分数值以优化搜索顺序

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(r=>Match(r)).Take(10);

答案 3 :(得分:1)

所有解决方案都能正常运行,但更好的分数可以更容易获得,如下所示

return locations.Where(o => o.Name.Contains(term))
            .OrderBy(m => m.Name.IndexOf(term))
            .Take(10)
            .ToList();

因此,每个名称包含 term ,最接近开始,首先显示。

答案 4 :(得分:0)

这个怎么样?

return locations.Where(o => o.Name.Contains(term))
                .OrderBy(m => m.Length)
                .Take(10)
                .ToList();