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;