2010-11-17 11 views
5

Mam aplikację, która pozwala na przeszukanie określonej jednostki w oparciu o kilka różnych kryteriów (w sumie w sumie 20 różnych metod). Chcę móc łączyć wyniki kilku wyszukiwań w celu utworzenia pojedynczego zestawu wyników.Ogólna strategia dla złożonych, wieloetapowych wyszukiwań

Na przykład:

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

Załóżmy, że wyszukiwanie jest dość złożony charakter taki, że łącząc je w jednym zapytaniu logicznej nie jest możliwe (ze względu na skomplikowane relacje, które muszą być sprawdzony, itp).

Załóżmy również, że liczba zaangażowanych podmiotów (prawdopodobnie) sprawia, że ​​strategia in-memory jest niewykonalna.

Moje pierwsze myśli były czymś wzdłuż linii:

1) Wykonaj wyszukiwanie osobno, otrzymać listę pasujących „identyfikatory jednostki” od każdego z nich, a następnie wykonać „root-level” szukaj w oparciu na tych.

Na przykład

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) Wykonaj zewnętrzną zapytanie, które wybiera się w oparciu o jednostkę wyników zwróconych moi (kompleks) podzapytaniach.

Na przykład:

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...) 

Oczywiście, te przykłady są znacznie uproszczone dla celów ilustracyjnych; poszczególne zapytania będą bardziej zaangażowane, a ich połączenie będzie arbitralne (właśnie zilustrowałem tutaj reprezentatywny przykład).

Byłbym bardzo zainteresowany słuchaniem sugestii, jak inni radzą sobie z tą sytuacją. Jestem otwarty na wszelkie możliwości, których nie odkryłem powyżej.

Dla porównania jest to aplikacja .NET wykorzystująca ORM NHibernate wspieraną przez bazę danych SQL Server 2008 R2.

Zdecydowałem się już na użycie sql lub natywnego sql, ponieważ ICriteria lub Linq nie zapewniają elastyczności potrzebnej do wykonywania poszczególnych zapytań ani wymaganych operacji łączenia.

Odpowiedz

2

Zrobiłem to, utrzymując liczniki wyników wyszukiwania w tabeli. Zasadniczo monitorowanie średniego odsetka wierszy filtrów wyszukiwania i czasu wykonywania.

Następnie tworzyć postać wykonania w oparciu o TotalNumberOfRowsToSearch * Percent_Not_Matched/RunTimeInSeconds ta bezpośrednia korelacja wierszy na sekundę może odfiltrować. Uśredniona w tysiącach przebiegów, jest to raczej dobra prognoza.

Następnie uruchamiam każde zapytanie w kolejności o najwyższej wydajności pierwszej.

Jeśli wykonujesz operację logiczną AND na łącznym wyniku, uruchom każde kolejne zapytanie tylko na wynikach poprzedniego zapytania.

Jeśli robisz logiczne OR, uruchom każde kolejne zapytanie tylko o wyniki NIE w połączonych poprzednich wynikach wyszukiwania.

Robiąc to w ten sposób, zapytanie zmieni się na podstawie indeksów i typów danych.

Jeśli potrzebujesz mniej dynamicznego rozwiązania, po prostu oblicz wyniki dla każdej części wyszukiwania i najpierw użyj lepszych wyników. Pamiętaj, że zapytanie jest uruchamiane w czasie 55 ms, ale odpowiada 99% wyników, nie jest tak użyteczne, jak to, które działa w ciągu 1 sekundy i pasuje do 1% wyników, więc bądź ostrożny, że wyniki mogą być sprzeczne z twoimi początkowymi pomysłami.

Po obliczeniu wartości wydajności wystarczy zwrócić uwagę na błąd dzielenia przez 0.

+0

Dziękuję za dzwonienie, jest to bardzo pouczające z punktu widzenia wydajności (co oczywiście jest głównym powodem). – DanP

0

Jeśli możesz użyć ICriteria, polecam go. Może radykalnie zmniejszyć ilość kodu przy złożonych wyszukiwaniach. Na przykład różnica między korzystaniem z jednego wyszukiwania a użyciem go jako podzapytania w wyszukiwaniu zbiorczym byłaby dodatkową projekcją.

Jeszcze nie próbowałem podzielić złożonych wyszukiwań i uruchamiać ich oddzielnie. Łącząc całe wyszukiwanie w jedno połączenie z bazą danych, jak na twój drugi przykład, jak dotąd zadziałało dla mnie. Jeśli nie dostaję przyzwoity czas odpowiedzi (minut w przeciwieństwie do sekund), Doradca dostrajania aparat bazy danych okazał się nieoceniony z sugerowanych indeksów i statystyk.

+0

Ogólnie rzecz biorąc, całkowicie zgadzam się z tym ... niestety muszę wykonać trochę magii sql ze względu na pewne złożone dziedziczenie używane w modelu - więc w niektórych miejscach korzystanie z surowego sql jest bardzo korzystne (i prawdopodobnie bardziej wydajne, ponieważ mogę unikaj niepotrzebnych dołączeń, itp.) – DanP

2

Moje podejście do korzystania z Linq polega na budowaniu listy wyrażeń, które budują złożone kryteria i na końcu ich stosowania.

Coś takiego:

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)); 

Ostatecznie po wybudowaniu listę ekspresji zastosować wyrażenia:

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

Można również zapewniają elastyczność w sposobie sortowania i pozwalają stronicowania:

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); 
      } 

Gdzie DecideSelector jest zdefiniowany następująco:

+0

To jest to, co zwykle robię; Niestety możliwości linq nhibernate nie są wystarczające dla moich potrzeb (kiedy mówię "skomplikowane" wyszukiwania, mam na myśli;)) – DanP