2010-11-17 32 views
5

Tôi có một ứng dụng cho phép một thực thể nhất định được tìm kiếm dựa trên một số tiêu chí khác nhau (một nơi nào đó theo thứ tự tổng cộng 20 phương pháp khác nhau). Tôi muốn có thể kết hợp các kết quả của một số tìm kiếm để tạo ra một tập kết quả duy nhất.Chiến lược chung cho tìm kiếm nhiều giai đoạn phức tạp

Ví dụ:

results = (entities from search 1 AND entities from search 2) OR (entities from search 3) 

Chúng ta hãy giả định rằng tìm kiếm là đủ phức tạp trong tự nhiên như vậy mà kết hợp chúng thành một truy vấn logic duy nhất là không thể (do các mối quan hệ phức tạp mà cần phải được truy vấn, v.v.)

Chúng ta cũng giả định rằng số lượng thực thể có liên quan (có khả năng) làm cho bất kỳ loại chiến lược trong bộ nhớ nào khả thi.

những suy nghĩ ban đầu của tôi là một cái gì đó dọc theo dòng:

1) Thực hiện các tìm kiếm riêng rẽ, có được một danh sách các phù hợp với "id tổ chức" từ mỗi trong số họ, và sau đó thực hiện một "gốc cấp" tìm kiếm dựa trên những cái này.

Ví dụ:

select * from entity e 
where 
(e.Id in (search 1 id list) AND e.Id in(search 2 id list)) 
OR e.Id in (search 3 id list) 

2) Thực hiện một truy vấn bên ngoài mà chọn các thực thể dựa trên các kết quả trả về bởi tôi) truy vấn con (phức tạp.

Ví dụ:

select * from entity e 
where (e.Id in (select e1.id from entity e1 where ...) AND e.Id in (select e2.id from entity e2 where...)) 
OR e.Id in (select e3.id from entity e3 where...) 

Rõ ràng những ví dụ được đơn giản hóa đáng kể cho mục đích minh hoạ; các truy vấn riêng lẻ sẽ liên quan nhiều hơn và sự kết hợp của chúng sẽ tùy ý (tôi vừa minh họa một ví dụ điển hình ở đây).

Tôi rất muốn nghe các đề xuất về cách người khác xử lý tình huống này. Tôi chắc chắn sẽ mở ra bất kỳ khả năng nào mà tôi chưa từng khám phá ở trên.

Để tham khảo, đây là một ứng dụng .NET sử dụng một NHibernate ORM được hỗ trợ bởi cơ sở dữ liệu SQL Server 2008 R2.

Tôi đã quyết định sử dụng sql bản địa hoặc hql cho điều này vì ICriteria hoặc LINQ không cung cấp tính linh hoạt cần thiết để thực hiện các truy vấn riêng lẻ cũng như các hoạt động kết hợp được yêu cầu.

Trả lời

2

Tôi đã thực hiện việc này bằng cách giữ các bộ đếm hiệu suất tìm kiếm trong một bảng. Về cơ bản, theo dõi tỷ lệ phần trăm trung bình của các hàng mà bộ lọc tìm kiếm và thời gian chạy.

Sau đó, tôi tạo một con số hiệu suất dựa trên TotalNumberOfRowsToSearch * Percent_Not_Matched/RunTimeInSeconds Con số này là sự tương quan trực tiếp của các hàng trên giây mà nó có thể lọc ra. Trung bình trên hàng nghìn lượt chạy, đó là một dự đoán khá tốt.

Sau đó, tôi chạy từng truy vấn để có con số hiệu suất cao nhất đầu tiên.

Nếu bạn đang thực hiện logic VÀ trên tổng kết quả, chỉ chạy mỗi truy vấn tiếp theo trên kết quả của truy vấn trước đó.

Nếu bạn đang thực hiện một OR hợp lý, hãy chạy mỗi truy vấn tiếp theo chỉ trên kết quả KHÔNG TRONG kết quả tìm kiếm kết hợp trước đó.

Bằng cách thực hiện theo cách này, truy vấn của bạn sẽ thay đổi dựa trên các chỉ mục và loại dữ liệu.

Nếu bạn muốn một giải pháp ít năng động hơn, chỉ cần tính toán số liệu hiệu suất cho từng phần của tìm kiếm và sử dụng các hiệu suất hoạt động tốt hơn trước. Hãy nhớ truy vấn chạy trong 55ms nhưng phù hợp với 99% kết quả không hữu ích như kết quả chạy trong 1 giây và phù hợp với 1% kết quả, vì vậy hãy cảnh giác rằng kết quả có thể đi ngược lại ý tưởng ban đầu của bạn.

Chỉ cần tìm lỗi chia cho 0 khi tính số liệu hiệu suất.

+0

Cảm ơn bạn đã tham gia, điều này rất thông tin từ khía cạnh hiệu suất (đây là một xem xét chính rõ ràng). – DanP

0

Nếu bạn có thể sử dụng ICriteria, tôi khuyên bạn nên sử dụng nó. Nó có thể cắt giảm đáng kể số lượng mã với các tìm kiếm phức tạp. Ví dụ, sự khác biệt giữa việc sử dụng một tìm kiếm của chính nó, và sử dụng nó như một truy vấn phụ trong tìm kiếm tổng hợp của bạn, sẽ là một phép chiếu được thêm vào.

Tôi chưa cố gắng chia các tìm kiếm phức tạp và chạy chúng một cách riêng biệt. Kết hợp toàn bộ tìm kiếm vào một cuộc gọi đến cơ sở dữ liệu, theo ví dụ thứ hai của bạn, cho đến nay đã làm việc cho tôi. Nếu tôi không nhận được một thời gian phản hồi tốt (phút như trái ngược với giây), thì Database Engine Tuning Advisor đã chứng minh vô giá với các chỉ mục và thống kê được đề xuất.

+0

Nói chung, tôi hoàn toàn đồng ý với điều này ... không may tôi cần thực hiện một số phép thuật sql do một số thừa kế phức tạp được sử dụng trong mô hình - vì vậy nó thực sự có lợi cho việc sử dụng sql thô ở một số nơi (và có khả năng thực hiện nhiều hơn kể từ khi tôi có thể tránh tham gia không cần thiết, vv) – DanP

2

Cách tiếp cận của tôi bằng cách sử dụng LINQ là xây dựng danh sách các biểu thức xây dựng các tiêu chí phức tạp và áp dụng chúng cùng nhau cuối cùng.

Something như thế:

List<Expression<Func<WorkItem, bool>>> whereExpressions = new List<Expression<Func<WorkItem, bool>>>(); 
if (!string.IsNullOrEmpty(searchMask)) 
      { 
       whereExpressions.Add(
             x => 
             (x.Name.ToLower().IndexOf(searchMask.ToLower()) > -1 || 
             x.Id.ToString().IndexOf(searchMask) > -1 || 
             (x.Description != null && 
              x.Description.ToLower().IndexOf(searchMask.ToLower()) > -1))); 
      } 

whereExpressions.Add(x => (x.Status == status)); 

Cuối cùng sau khi xây dựng danh sách biểu bạn áp dụng các biểu thức:

IQueryable<WorkItem> result = Session.Linq<WorkItem>(); 
foreach (Expression<Func<WorkItem, bool>> whereExpression in whereExpressions) 
      { 
       result = result.Where(whereExpression); 
      } 

Bạn cũng có thể cung cấp sự linh hoạt trong phương pháp phân loại và cho phép phân trang:

IQueryable<WorkItem> items; 
      if (ascOrDesc == "asc") 
      { 
       items = result.OrderBy(DecideSelector(indexer)).Skip(startPoint - 1).Take(numOfrows); 
      } 
      else 
      { 
       items = result.OrderByDescending(DecideSelector(indexer)).Skip(startPoint - 1).Take(numOfrows); 
      } 

Nơi DecideSelector được định nghĩa như sau:

private Expression<Func<WorkItem, object>> DecideSelector(string fieldCode) 
     { 
      switch (fieldCode) 
      { 
       case "Deadline": 
        return item => item.Deadline; 
       case "name": 
        return item => item.Name; 
       case "WiStatus": 
        return item => item.Status; 
       case "WiAssignTo": 
        return item => item.AssignedUser; 
       default: 
        return item => item.Id; 
      } 
     } 
+0

Đây là những gì tôi thường làm; khả năng linq của nhibernate không đủ cho nhu cầu của tôi (khi tôi nói các tìm kiếm 'phức tạp'; ý tôi là nó;)) – DanP

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