2012-10-31 30 views
7

Tôi đang cố thực hiện tìm kiếm đối với một DB mà tôi đã kế thừa. Yêu cầu nói rằng người dùng phải có khả năng tìm kiếm một đối tượng theo tên. Thật không may, một đối tượng có thể có nhiều tên liên kết với nó. Ví dụ:Tha thứ/tìm kiếm mờ với LINQ

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

Thật dễ dàng đủ để thực hiện một tìm kiếm khi một cái tên duy nhất tồn tại trong mỗi bản ghi:

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

Tuy nhiên, khi nhiều tên tồn tại, cách tiếp cận này không hoạt động.

Câu hỏi: Có thể viết một phương pháp tìm kiếm mà sẽ quay trở lại kỷ lục một (John và Jane Doe) khi ai đó sử dụng các thuật ngữ tìm kiếm John Doe hoặc Jane Doe?

+0

Bạn có thể làm một String.split trên không gian màu trắng để phá vỡ chuỗi tìm kiếm ra và sau đó chỉ cần chạy nhiều truy vấn với.Chứa và trả lại tất cả kết quả? –

+0

Nếu có "John Smith" thì sao? Bạn có chia nó ra và tìm kiếm từng phần của tên không? Điều gì tạo nên tên và họ? Những gì tôi nhận được ở là ở dạng hiện tại của nó tên có vẻ không có cấu trúc. – hometoast

Trả lời

3

này sẽ làm tổn thương thực hiện, nhưng làm thế nào về nhanh chóng này:

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

Nó có vẻ trở lại những gì bạn mong đợi. Bây giờ bạn sẽ điều chỉnh nó để cư xử tốt đẹp khi bạn cũng có một bản ghi "John Doe" cũng như "John và Jane Doe".

Tính năng này có phù hợp với bạn không?

+0

+1, nó chắc chắn không hoạt động! Mối quan tâm của tôi là hiệu suất '.All()'. Có lẽ đây là cách duy nhất để làm điều đó cho các thiết lập DB hiện tại. Tôi rất muốn nhìn thấy phản ứng từ cộng đồng đến phương pháp này trước khi tôi kích hoạt nó ... –

+1

Cũng không có tất cả các bạn chỉ cần thực hiện tìm kiếm chuẩn (tốc độ tùy thuộc vào cấu hình DB của bạn và tất cả). Với giải pháp All(), bạn nhân với 2-3 trung bình (nếu tên thường có một FirstName và một LastName). Vì vậy, trong trường hợp khi bạn có một trận đấu chính xác, bạn sẽ không phải chia chuỗi, điều đó sẽ bị tổn thương. Làm thế nào để giảm thiểu với một tìm kiếm đơn giản lúc đầu và nếu không có gì đi lên, sử dụng tất cả() bit? Chỉ cần ném suy nghĩ – Vladimir

0

Bạn cần phải kéo tên ra vào cột First/LastName hoặc vào một bảng khác có lẽ nếu có nhiều bí danh.

Nhưng những gì tôi thực sự nghĩ rằng bạn nên nhìn vào nó một cái gì đó giống như Lucene nếu bạn cần một cái gì đó 'tha thứ' hoặc 'mờ'

Câu hỏi: Có thể viết một phương pháp tìm kiếm sẽ trở lại ghi lại (John và Jane Doe) khi ai đó sử dụng cụm từ tìm kiếm John Doe hoặc Jane Doe?

Để có rất cụ thể cho câu hỏi của bạn, bạn có thể chuyển đổi "John Doe" để LIKE '%John%Doe' hay "Jane Doe" để LIKE '%Jane%Doe' và điều này sẽ truy lục hồ sơ đó. Tuy nhiên tôi có thể thấy vấn đề với những cái tên như "Johnathan Poppadoe".

7

Bạn có thể tạo ra một phương pháp mở rộng thông thường có tên là "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 
} 

Sau đó LINQ của bạn sẽ ít nhất được dễ dàng hơn để đọc:

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

nhiên, nhược điểm là mỗi cuộc gọi đến ContainsFuzzy có nghĩa là tái tạo danh sách chia nhỏ của bạn, v.v., do đó, có một số chi phí liên quan. Bạn có thể tạo một lớp được gọi là FuzzySearch đó có ít nhất sẽ cung cấp cho bạn một số Hiệu quả tăng:

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 của bạn:

FuzzySearch _fuzz = new FuzzySearch("Foo McFoo"); 

var objects = from x in db.Foo 
       where _fuzz.IsMatch(x.Name) 
       select x; 
+0

Giúp tôi rất nhiều, cảm ơn! – BjarkeCK

+0

Nó phải là "(? I) (? =. *" + Chuỗi.Join (") (? =. *", _searchTerms) + ")"); – JPVenson

+0

@jpv thanks - chỉnh sửa được thực hiện. – JDB

Các vấn đề liên quan