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)```
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.
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. .
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.