После многих исследований я обнаружил, что проблема заключается в том, как устроен поисковый запрос для поля поиска администратора (в классе 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.