Anstelle eines Vorworts
Alles begann damit, dass mir angeboten wurde, im Rahmen des Fachs "Grundlagen der Webprogrammierung" an dem Projekt teilzunehmen, anstatt Laborarbeit und Kursarbeit zu leisten, da ich erklärte, dass ich etwas außerhalb des allgemeinen Kurses machen wollte (und daher bereits genügend Wissen vorhanden war) Auf ein paar DRF + Vue wollte ich etwas Neues. Und so entschied ich mich in einer meiner PRs auf Github, die Volltextsuche (die darauf angedeutete Zuordnung) zu verwenden, um den Inhalt zu filtern, was mich dazu brachte, auf die Django-Dokumentation zu verweisenauf der Suche nach der besten Umsetzung dieses Geschäfts. Ich denke, Sie kennen die meisten der dort vorgeschlagenen Methoden (enthält icontains, trigram_similar). Alle von ihnen sind für bestimmte Aufgaben geeignet, aber nicht sehr gut in der Volltextsuche. Als ich ein wenig nach unten scrollte, stieß ich auf einen Abschnitt, in dem es um die Interaktion von Django und Pgsql bei der Implementierung einer dokumentbasierten Suche ging, was mich anzog, da postgre über ein integriertes Tool zur Implementierung dieser [Volltextsuche] verfügt. Und ich entschied, dass django höchstwahrscheinlich einfach eine API für diese Suche bereitstellt, auf deren Grundlage eine solche Lösung funktionieren sollte und genauer und schneller als alle anderen Optionen. Der Lehrer glaubte mir nicht zu sehr, wir stritten uns mit ihm und er bot an, zu diesem Thema zu recherchieren. Und hier bin ich.
Arbeitsbeginn
Das erste Problem, das vor mir auftrat, war die Suche nach einem Datenbankmodell, um selbst keine unverständlichen Dinge zu finden, und ich ging zu Google und las das Postgres-Wiki . Infolgedessen entschied ich mich für ihre Demobasis über Flüge durch Russland.
, . , . , , , 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 - . .
Ein QuerySet ist iterierbar und führt seine Datenbankabfrage aus, wenn Sie es zum ersten Mal durchlaufen. Dies gibt beispielsweise die Überschrift aller Einträge in der Datenbank aus:
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
Enthält
Beginnen wir mit enthält, es funktioniert im Grunde wie ein 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%
Um das Ergebnis von curl zu erhalten, habe ich die Anforderung wie folgt ausgeführt (in Sekunden gezählt):
$ curl -w "%{time_total}\n" -o /dev/null -s http://127.0.0.1:8000/api/tickets/?name=IVAN
1,242888
Ich habe alles in eine Tabelle auf dem entsprechenden Blatt gelegt.
— , 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) , , , , .
Insgesamt hat sich mein Verständnis einiger innerer Funktionsweisen von Django dank dieser Forschung beruhigt. Und schließlich wurde der Unterschied zwischen Teilstringsuche und Volltextsuche erkannt. Der Unterschied in ihrer Implementierung durch das Django ORM.