След много проучвания открих, че проблемът идва от това как е изградена заявката за търсене за полето за търсене на администратор (в ChangeList
клас). При търсене с няколко термина (думи, разделени с интервал) всеки термин се добавя към QuerySet чрез свързване на нов filter()
. Когато има едно или повече свързани полета в search_fields
, създадената SQL заявка ще има много JOIN
верижни един след друг с много JOIN
за всяко свързано поле (вижте моя свързан въпрос
за някои примери и повече информация). Тази верига от JOIN
има ли така, че всеки термин ще се търси само в подмножеството на филтъра за данни по предшестващия термин И най-важното е, че свързаното поле трябва да има само един термин (срещу необходимостта да има ВСИЧКИ термини), за да направи съвпадение. Вижте Обхващане на многостойностни взаимоотношения в документите на Django за повече информация по тази тема. Почти съм сигурен, че това е желаното поведение през повечето време за полето за търсене на администратор.
Недостатъкът на тази заявка (с включени свързани полета) е, че вариацията в производителността (време за изпълнение на заявката) може да бъде наистина голяма. Зависи от много фактори:брой търсени термини, търсени термини, вид търсене на поле (VARCHAR и т.н.), брой полета за търсене, данни в таблиците, размер на таблиците и т.н. С правилната комбинация е лесно да имате заявка, която ще отнеме почти цяла вечност (заявка, която отнема повече от 10 минути, за мен е заявка, която отнема цяла вечност в контекста на това поле за търсене).
Причината, поради която може да отнеме толкова време, е, че базата данни трябва да създаде временна таблица за всеки термин и да я сканира предимно изцяло, за да търси следващия термин. Така че това се добавя много бързо.
Възможна промяна, която да направите, за да подобрите производителността, е И всички термини в един и същи filter()
. По този начин те ще имат само едно JOIN
по свързано поле (или 2, ако е много към много) вместо много повече. Тази заявка ще бъде много по-бърза и с наистина малка вариация на производителността. Недостатъкът е, че свързаните полета ще трябва да имат ВСИЧКИ термини за съвпадение, така че можете да получите по-малко съвпадения в много случаи.
АКТУАЛИЗАЦИЯ
Както беше попитано от trinchet ето какво е необходимо за промяна на поведението при търсене (за Django 1.7). Трябва да замените get_search_results()
от администраторските класове, където искате този вид търсене. Трябва да копирате целия код на метода от основния клас (ModelAdmin
) към вашия собствен клас. След това трябва да промените тези редове:
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))
Към това:
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))
Този код не е тестван. Оригиналният ми код беше за Django 1.4 и просто го адаптирах за 1.7 тук.