Comparação de diferentes filtros de django no exemplo da demonstração do banco de dados PostgreSQL

Em vez de um prefácio



Tudo começou com o fato de me oferecerem a participação no projeto no âmbito da disciplina "Noções básicas de programação na Web", em vez de trabalhar em laboratório e cursos, pois afirmei que queria fazer algo distante do curso geral (e, portanto, já havia conhecimento suficiente em um monte de DRF + Vue, eu queria algo novo). E assim, em um dos meus PRs no github, eu decidi usar a pesquisa de texto completo (a tarefa sugerida aqui) para filtrar o conteúdo, o que me fez consultar a documentação do Djangoem busca da melhor forma de implementar esse negócio. Eu acho que você conhece a maioria dos métodos sugeridos lá (contém, icontains, trigram_similar). Todos eles são adequados para algumas tarefas específicas, mas não muito bons, a saber, pesquisa de texto completo. Percorrendo um pouco, me deparei com uma seção que falava sobre a interação do Django e Pgsql para implementar a pesquisa baseada em documentos, o que me atraiu, pois o postgre possui uma ferramenta interna para implementar essa pesquisa em [texto completo]. E eu decidi que, provavelmente, o django simplesmente fornece uma API para essa pesquisa, com base na qual essa solução deve funcionar e mais precisa e rápida do que qualquer outra opção. O professor não acreditou muito em mim, discutimos com ele, e ele se ofereceu para realizar pesquisas sobre esse assunto. E aqui estou eu.



Início do trabalho



O primeiro problema que surgiu diante de mim foi a busca de uma maquete de banco de dados, para não me apresentar coisas incompreensíveis, e eu fui ao google e li o wiki do postgres . Como resultado, decidi na base de demonstração deles sobre voos pela Rússia.



, . , . , , , 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 - . .



:



Um QuerySet é iterável e executa sua consulta ao banco de dados na primeira vez que você itera sobre ele. Por exemplo, isso imprimirá o título de todas as entradas no banco de dados:



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




Vista final para contém
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


Contém



Vamos começar com contém, basicamente funciona como um WHERE LIKE.



Consulta no Django ORM / Consulta no sql para contém
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 obter o resultado da curvatura, executei a solicitação da seguinte maneira (contada em segundos):



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


Coloquei tudo em uma mesa na folha apropriada.



— , 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) , , , , .



No geral, minha compreensão de alguns dos trabalhos internos do django se acalmou graças a esta pesquisa. E finalmente chegou a conclusão da diferença entre a pesquisa de substring e a pesquisa de texto completo. A diferença na sua implementação através do Django ORM.




All Articles