Después de muchas investigaciones, descubrí que el problema proviene de cómo se crea la consulta de búsqueda para el campo de búsqueda del administrador (en ChangeList
clase). En una búsqueda de varios términos (palabras separadas por espacios), cada término se agrega al QuerySet encadenando un nuevo filter()
. Cuando hay uno o más campos relacionados en search_fields
, la consulta SQL creada tendrá mucho JOIN
encadenados uno tras otro con muchos JOIN
para cada campo relacionado (consulte mi pregunta relacionada
para algunos ejemplos y más información). Esta cadena de JOIN
está ahí para que cada término se busque solo en el subconjunto del filtro de datos por el término precedente Y, lo que es más importante, que un campo relacionado solo debe tener un término (en lugar de tener TODOS los términos) para hacer una coincidencia. Consulte Ampliar relaciones de varios valores en los documentos de Django para obtener más información sobre este tema. Estoy bastante seguro de que es el comportamiento buscado la mayor parte del tiempo para el campo de búsqueda de administrador.
El inconveniente de esta consulta (con campos relacionados involucrados) es que la variación en el rendimiento (tiempo para realizar la consulta) puede ser realmente grande. Depende de muchos factores:número de términos buscados, términos buscados, tipo de campo de búsqueda (VARCHAR, etc.), número de campos de búsqueda, datos en las tablas, tamaño de las tablas, etc. Con la combinación adecuada es fácil tener una consulta que durará una eternidad (una consulta que demorará más de 10 minutos para mí es una consulta que demorará una eternidad en el contexto de este campo de búsqueda).
La razón por la que puede llevar tanto tiempo es que la base de datos necesita crear una tabla temporal para cada término y escanearla casi por completo para buscar el siguiente término. Entonces, esto se suma muy rápido.
Un posible cambio para mejorar el rendimiento es Yed todos los términos en el mismo filter()
. De esta manera habrá un solo JOIN
por campo relacionado (o 2 si es muchos a muchos) en lugar de muchos más. Esta consulta será mucho más rápida y con una variación de rendimiento realmente pequeña. El inconveniente es que los campos relacionados tendrán que tener TODOS los términos para coincidir, por lo que puede obtener menos coincidencias en muchos casos.
ACTUALIZAR
Tal como lo preguntó trinchet esto es lo que se necesita para hacer el cambio de comportamiento de búsqueda (para Django 1.7). Debe anular get_search_results()
de las clases de administración donde desea este tipo de búsqueda. Debe copiar todo el código del método de la clase base (ModelAdmin
) a su propia clase. Entonces necesitas cambiar esas líneas:
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))
A eso:
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))
Este código no está probado. Mi código original era para Django 1.4 y lo adapté para 1.7 aquí.