C# 基于LINQ的宽恕/模糊搜索
我正在尝试对继承的数据库执行搜索。该要求规定用户必须能够按名称搜索对象。不幸的是,一个对象可能有多个与之关联的名称。例如: 当每个记录中都有一个名称时,执行搜索就很容易了:C# 基于LINQ的宽恕/模糊搜索,c#,linq,C#,Linq,我正在尝试对继承的数据库执行搜索。该要求规定用户必须能够按名称搜索对象。不幸的是,一个对象可能有多个与之关联的名称。例如: 当每个记录中都有一个名称时,执行搜索就很容易了: var objects = from x in db.Foo where x.Name.Contains("Foo McFoo") select x; 但是,当存在多个名称时,该方法不起作用 问题:当有人使用搜索词John Doe或Jane Doe时,是否可以编
var objects = from x in db.Foo
where x.Name.Contains("Foo McFoo")
select x;
但是,当存在多个名称时,该方法不起作用
问题:当有人使用搜索词
John Doe
或Jane Doe
时,是否可以编写一个搜索方法,返回记录一(John和Jane Doe)?如果有多个别名,您可能需要将名称从First/LastName列或另一个表中拉出
但我真的认为,如果你需要一些“宽容”或“模糊”的东西,你应该这样看待它
问题:是否可以编写返回的搜索方法
当有人使用搜索词John时,记录一个(John和Jane Doe)
Doe还是Jane Doe
具体到您的问题,您可以将“John Doe”转换为
,如“%John%Doe”
,或将“Jane Doe”转换为如“%Jane%Doe”
,这将检索该记录。但是,我可以看到像“Johnathan Poppadoe”这样的名字有问题。这会影响性能,但是这个快速的名字怎么样:
string[] filters = "John Doe".Split(new[] {' '});
var objects = from x in db.Foo
where filters.All(f => x.Name.Contains(f))
select x;
它似乎回报了你所期望的。现在,当你还有一张“约翰·多伊”和“约翰和简·多伊”的唱片时,你会调整它,使它表现得很好
这对您有用吗?您可以创建一个名为“ContainsFuzzy”的自定义扩展方法: 那么你的LINQ至少会更容易阅读:
var objects = from x in db.Foo
where x.Name.ContainsFuzzy("Foo McFoo")
select x;
明显的缺点是,每次调用ContainsFuzzy都意味着重新创建分割列表,等等,因此会涉及一些开销。您可以创建一个名为FuzzySearch的类,该类至少可以提高效率:
class FuzzySearch{
private string _searchTerm;
private string[] _searchTerms;
private Regex _searchPattern;
public FuzzySearch( string searchTerm ){
_searchTerm = searchTerm;
_searchTerms = searchTerm.Split( new Char[] { ' ' } );
_searchPattern = new Regex(
"(?i)(?=.*" + String.Join(")(?=.*", _searchTerms) + ")");
}
public bool IsMatch( string value ){
// do the cheap stuff first
if ( _searchTerm == value ) return true;
if ( value.Contains( _searchTerm ) ) return true;
// if the above don't return true, then do the more expensive stuff
if ( _searchPattern.IsMatch( value ) ) return true;
// etc.
}
}
你的LINQ:
FuzzySearch _fuzz = new FuzzySearch( "Foo McFoo" );
var objects = from x in db.Foo
where _fuzz.IsMatch( x.Name )
select x;
我想知道为什么没有人提到Levenshtein距离算法 这是一种算法,通过一个int来表示两个字符串之间的距离 这是一篇文章,你可以找到这个算法的一些实现 因此,使用signature
int distance(string x,string y)
的距离函数,您可以过滤出高距离,并对结果进行排序,以便使用LINQ将低距离显示在结果的顶部。请注意,这将导致性能损失。是否可以在空白处执行string.split以将搜索字符串分开,然后使用.Contains运行多个查询并返回所有结果?如果有“John Smith”怎么办?您是否将其拆分并搜索名称的每个部分?名字和姓氏是什么构成的?我的意思是,在它当前的形式中,这个名字看起来没有任何结构。+1,它确实有效!我关心的是
.All()
性能。考虑到当前的数据库设置,这可能是唯一的方法。我希望在我触发这个方法之前,看到社区对它的反应……如果没有这些,你只需要做一个标准的搜索(速度取决于你是否是DB config和all)。使用All()解决方案,您可以将其平均乘以2-3(如果名称通常有一个名字和一个姓氏)。因此,如果你有一个精确的匹配,你就不必拆分字符串,这会造成伤害。首先用一个简单的搜索来缓解问题,如果没有结果,使用All()位怎么样?只是抛出一些想法,它应该是“(?i)(?=.*”+String.Join(“)(?=.*”,_searchTerms)+”);
class FuzzySearch{
private string _searchTerm;
private string[] _searchTerms;
private Regex _searchPattern;
public FuzzySearch( string searchTerm ){
_searchTerm = searchTerm;
_searchTerms = searchTerm.Split( new Char[] { ' ' } );
_searchPattern = new Regex(
"(?i)(?=.*" + String.Join(")(?=.*", _searchTerms) + ")");
}
public bool IsMatch( string value ){
// do the cheap stuff first
if ( _searchTerm == value ) return true;
if ( value.Contains( _searchTerm ) ) return true;
// if the above don't return true, then do the more expensive stuff
if ( _searchPattern.IsMatch( value ) ) return true;
// etc.
}
}
FuzzySearch _fuzz = new FuzzySearch( "Foo McFoo" );
var objects = from x in db.Foo
where _fuzz.IsMatch( x.Name )
select x;