2012-10-31 51 views
7

我試圖對我繼承的數據庫實施搜索。該要求規定用戶必須能夠按名稱搜索對象。不幸的是,一個對象可能有多個與之關聯的名字。例如:使用LINQ的Forgiving/Fuzzy搜索

ID Name 
1  John and Jane Doe 
2  Foo McFoo 
3  Boo McBoo 

這是很容易實現搜索時一個名字在每個記錄存在:

var objects = from x in db.Foo 
       where x.Name.Contains("Foo McFoo") 
       select x; 

然而,當多個名稱存在,這種做法是行不通的。

問:是否可以寫一個搜索方法,將返回一個記錄(John和Jane Doe的),當有人使用的搜索詞John DoeJane Doe

+0

你知道白色空間string.split以打散搜索字符串和然後只需運行多個查詢。包含並返回所有結果? –

+0

如果有「John Smith」會怎麼樣?你是否將它分割並搜索名稱的每個部分?什麼使首字母和姓?我所知道的是,在目前的形式下,這個名字看起來沒有結構。 – hometoast

回答

3

這會影響性能,但如何對這種快速之一:

string[] filters = "John Doe".Split(new[] {' '}); 
var objects = from x in db.Foo 
       where filters.All(f => x.Name.Contains(f)) 
       select x; 

它似乎返回你所期望的。現在,如果您還有「John Doe」以及「John and Jane Doe」的唱片,那麼您會調整它以表現得很好。

這是否適合您?

+0

+1,它當然可以工作!我關心的是'.All()'的表現。也許這是在目前的數據庫設置下做到這一點的唯一方法。我很樂意看到社區對這種方法的反應,然後再拉動觸發器...... –

+1

沒有所有你會做標準搜索(速度取決於你的數據庫配置和所有)。使用All()解決方案,您平均乘以2-3(如果名稱通常包含一個FirstName和一個LastName)。所以,如果你有完全匹配的情況,你不必拆分字符串,那樣會很痛苦。如何通過一個簡單的搜索來緩解,如果什麼都沒有出現,請使用All()位?只是拋出想法 – Vladimir

0

您可能需要將名稱拉出到First/LastName列中,或者如果有多個別名,則可以將其放入另一個表中。

但我真的覺得你應該看看它像Lucene如果你需要的東西「寬容」或「模糊」

問題:是否可以寫一個搜索方法,將返回 當有人使用搜索條件時記錄一個(John和Jane Doe)John Doe或Jane Doe?

非常具體到你的問題,你可以「李四」轉換爲LIKE '%John%Doe'或「李四」,以LIKE '%Jane%Doe',這將檢索到的記錄。不過,我可以看到像「Johnathan Poppadoe」這樣的名字出現問題。

7

您可以創建一個名爲「ContainsFuzzy」自定義擴展方法:

public static bool ContainsFuzzy(this string target, string text){ 
    // do the cheap stuff first 
    if (target == text) return true; 
    if (target.Contains(text)) return true; 
    // if the above don't return true, then do the more expensive stuff 
    // such as splitting up the string or using a regex 
} 

那麼你的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; 
+0

幫助我很多,謝謝! – BjarkeCK

+0

它應該是 「(?i)(?=。*」+ String.Join(「)(?=。*」,_searchTerms)+「)」); – JPVenson

+0

@jpv謝謝 - 編輯。 – JDB