Po mnoha šetřeních jsem zjistil, že problém pochází z toho, jak je vyhledávací dotaz vytvořen pro vyhledávací pole správce (v ChangeList
třída). Při vyhledávání více výrazů (slova oddělená mezerou) je každý výraz přidán do QuerySet zřetězením nového filter()
. Když je v search_fields
jedno nebo více souvisejících polí , vytvořený SQL dotaz bude mít hodně JOIN
řetězené jeden po druhém s mnoha JOIN
pro každé související pole (viz můj související otázka
pro některé příklady a další informace). Tento řetězec JOIN
je zde tak, že každý výraz bude vyhledáván pouze v podmnožině datového filtru podle předchozího výrazu A, co je nejdůležitější, že související pole musí mít pouze jeden výraz (vs. potřeba mít VŠECHNY výrazy), aby bylo dosaženo shody. Viz Překlenutí vztahů s více hodnotami v dokumentech Django, kde najdete další informace na toto téma. Jsem si docela jistý, že je to chování, které bylo většinou požadováno pro vyhledávací pole správce.
Nevýhodou tohoto dotazu (se zapojenými souvisejícími poli) je, že rozdíly ve výkonu (čas na provedení dotazu) mohou být opravdu velké. Záleží na spoustě faktorů:počet hledaných výrazů, hledané výrazy, druh vyhledávání polí (VARCHAR atd.), počet vyhledávání polí, údaje v tabulkách, velikost tabulek atd. Se správnou kombinací je to snadné mít dotaz, který bude trvat většinou věčnost (dotaz, který trvá déle než 10 minut, je pro mě dotaz, který v kontextu tohoto vyhledávacího pole trvá věčnost).
Důvodem, proč to může trvat tak dlouho, je to, že databáze potřebuje pro každý výraz vytvořit dočasnou tabulku a většinou ji celou prohledat, aby hledala další výraz. Takže tohle se sčítá opravdu rychle.
Možná změna ke zlepšení výkonu je SAND všechny výrazy ve stejném filter()
. Tímto způsobem jejich bude pouze jeden JOIN
podle souvisejícího pole (nebo 2, pokud je to mnoho k mnoha) namísto mnoha dalších. Tento dotaz bude mnohem rychlejší a se skutečně malými změnami výkonu. Nevýhodou je, že související pole budou muset mít VŠECHNY výrazy, aby se shodovaly, takže v mnoha případech můžete získat méně shod.
AKTUALIZACE
Jak se zeptal trinchet zde je to, co je potřeba ke změně chování při vyhledávání (pro Django 1.7). Musíte přepsat get_search_results()
z tříd správců, kde chcete tento druh vyhledávání. Musíte zkopírovat veškerý kód metody ze základní třídy (ModelAdmin
) do své vlastní třídy. Potom musíte tyto řádky změnit:
for bit in search_term.split():
or_queries = [models.Q(**{orm_lookup: bit})
for orm_lookup in orm_lookups]
queryset = queryset.filter(reduce(operator.or_, or_queries))
K tomu:
and_queries = []
for bit in search_term.split():
or_queries = [models.Q(**{orm_lookup: bit})
for orm_lookup in orm_lookups]
and_queries.append(Q(reduce(operator.or_, or_queries)))
queryset = queryset.filter(reduce(operator.and_, and_queries))
Tento kód není testován. Můj původní kód byl pro Django 1.4 a zde jsem ho jen přizpůsobil pro 1.7.