Comparación de diferentes filtros django en el ejemplo de la demostración de la base de datos PostgreSQL

En lugar de un prefacio



Todo comenzó con el hecho de que me ofrecieron participar en el proyecto en el marco de la asignatura "Conceptos básicos de programación web", en lugar de hacer trabajo de laboratorio y cursos, ya que dije que quería hacer algo alejado del curso general (y por lo tanto ya había suficiente conocimiento en un montón de DRF + Vue, quería algo nuevo). Y así, en uno de mis RP en github, decidí usar la búsqueda de texto completo (la tarea insinuó esto) para filtrar el contenido, lo que me hizo recurrir a la documentación de Djangoen busca de la mejor manera de implementar este negocio. Creo que conoce la mayoría de los métodos sugeridos allí (contiene, icontains, trigram_similar). Todos ellos son adecuados para algunas tareas específicas, pero no muy buenos para, a saber, la búsqueda de texto completo. Al desplazarme un poco hacia abajo, me encontré con una sección que hablaba sobre la interacción de Django y Pgsql para implementar la búsqueda basada en documentos, lo que me atrajo, ya que postgre tiene una herramienta incorporada para implementar esta búsqueda [de texto completo]. Y decidí que lo más probable es que django simplemente proporcione una API para esta búsqueda, sobre la base de la cual tal solución debería funcionar y de manera más precisa y rápida que cualquier otra opción. El profesor no me creyó demasiado, discutimos con él y se ofreció a investigar sobre este tema. Y aquí estoy yo.



Comienzo de trabajo



El primer problema que surgió ante mí fue la búsqueda de una maqueta de la base de datos, para no encontrarme con cosas incomprensibles, y fui a google y leí el wiki de postgres . Como resultado, me decidí por su base de demostración sobre vuelos a través de Rusia.



, . , . , , , search django.contrib.postgres.search. — contains ( ) icontains ( , , : "Helen" : <Author: Helen Mirren>, <Author: Helena Bonham Carter>, <Author: Hélène Joy>), django. postgresql. tickets small 366733 . passenger_name, , , . .



django



— django . django , , :



$ python manage.py inspectdb > models.py


, , settings.py. . , . , ( ), , 300+ , 10, , . , , curl. .





, , , curl, , . , ( ).



django



, — , queryset - . .



:



Un QuerySet es iterable, y ejecuta su consulta de base de datos la primera vez que itera sobre él. Por ejemplo, esto imprimirá el título de todas las entradas en la base de datos:



for e in Entry.objects.all():
       print(e.headline)```




Vista final para contiene
class TicketListView(g.ListAPIView):
    serializer_class = TicketSerializer

    def get_queryset(self):
        queryset = ''
        params = self.request.query_params

        name = params.get('name', None)

        if name:
            start_time = d.datetime.now()

            queryset = queryset.filter(passenger_name__contains=name)
            print('len of result is {} rows'.format(len(queryset)))

            end_time = d.datetime.now()

            time_diff = (end_time - start_time)
            execution_time = time_diff.total_seconds() * 1000

            print("Filter execution time {} ms".format(execution_time))

        return queryset


Contiene



Comencemos con contiene, básicamente funciona como un WHERE LIKE.



Consulta en Django ORM / Consulta en sql para contiene
queryset = queryset.filter(passenger_name__contains=name)


SELECT "tickets"."ticket_no", "tickets"."book_ref", "tickets"."passenger_id", "tickets"."passenger_name", "tickets"."contact_data" FROM "tickets" WHERE "tickets"."passenger_name"::text LIKE %IVAN%


Para obtener el resultado de curl, ejecuté la solicitud de la siguiente manera (contada en segundos):



$ curl -w "%{time_total}\n" -o /dev/null -s http://127.0.0.1:8000/api/tickets/?name=IVAN
1,242888


Puse todo en una mesa en la hoja correspondiente.



— , 140 1400 . , . ORM 73 600 , 55 100 .



Icontains



Icontains - ( , ). , contains — icontains. .



Django ORM/ sql icontains
queryset = queryset.filter(passenger_name__icontains=name)


SELECT "tickets"."ticket_no", "tickets"."book_ref", "tickets"."passenger_id", "tickets"."passenger_name", "tickets"."contact_data" FROM "tickets" WHERE UPPER("tickets"."passenger_name"::text) LIKE UPPER(%IVAN%)


, , ( 300 ), 200 1500 . ORM — 200 700 .



Full text search ( django.contrib.postgres)



, full text search . 1300 , 1000 1700 . , ORM — 1000 1450 .



class TicketListView(g.ListAPIView):
    serializer_class = TicketSerializer

    def get_queryset(self):
        # queryset = Tickets.objects.all()
        queryset = ''

        params = self.request.query_params

        name = params.get('name', None)

        if name:

            start_time = d.datetime.now()

            queryset = Tickets.objects.filter(passenger_name__search=name)

            end_time = d.datetime.now()

            time_diff = (end_time - start_time)
            execution_time = time_diff.total_seconds() * 1000

            print("Filter execution time {} ms".format(execution_time))

            f = open('results.txt', 'a')

            f.write('{}'.format(execution_time))
            f.write('\n')

            f.close()

        return queryset


Full text search ( rest_framework.filters, — SearchFilter)



FTS, FTS , , contains icontains. 200 1710 .



FTS , . , 800 1120 .



...
from rest_framework import filters as f

class TicketListView(g.ListAPIView):
    queryset = Tickets.objects.all()
    serializer_class = TicketSerializer
    filter_backends = [f.SearchFilter]
    search_fields = ['@passenger_name']


django-filter



contains icontains, . , django-filter - Django ORM.



?



— (, , ) , . — . , ( , , contains/icontains) , , , , .



En general, mi comprensión de algunos de los funcionamientos internos de django se ha establecido gracias a esta investigación. Y finalmente se dio cuenta de la diferencia entre la búsqueda de subcadenas y la búsqueda de texto completo. La diferencia en su implementación a través del Django ORM.




All Articles