Au lieu d'une préface
Tout a commencĂ© par le fait que l'on m'a proposĂ© de participer au projet dans le cadre du sujet «Bases de la programmation Web», au lieu de faire des travaux de laboratoire et des cours, puisque j'ai dĂ©clarĂ© que je voulais faire quelque chose Ă distance du cours gĂ©nĂ©ral (et donc il y avait dĂ©jĂ assez de connaissances sur un tas de DRF + Vue, je voulais quelque chose de nouveau). Et donc dans l'un de mes PR sur github, j'ai dĂ©cidĂ© d'utiliser la recherche en texte intĂ©gral (l'affectation y faisait allusion) pour filtrer le contenu, ce qui m'a fait me tourner vers la documentation DjangoĂ la recherche de la meilleure façon de mettre en Ćuvre cette entreprise. Je pense que vous connaissez la plupart des mĂ©thodes suggĂ©rĂ©es ici (contient, icontains, trigram_similar). Tous conviennent Ă certaines tĂąches spĂ©cifiques, mais pas trĂšs bons, Ă savoir la recherche de texte intĂ©gral. En descendant un peu, je suis tombĂ© sur une section qui parlait de l'interaction de Django et Pgsql pour implĂ©menter la recherche basĂ©e sur des documents, ce qui m'a attirĂ©, car postgre a un outil intĂ©grĂ© pour implĂ©menter cette recherche trĂšs [plein texte]. Et j'ai dĂ©cidĂ© que trĂšs probablement, django fournissait simplement une API pour cette recherche, sur la base de laquelle une telle solution devrait fonctionner et plus prĂ©cise et plus rapide que toute autre option. Le professeur ne m'a pas trop cru, nous nous sommes disputĂ©s avec lui, et il a proposĂ© de mener des recherches sur ce sujet. Et me voici.
DĂ©but des travaux
Le premier problĂšme qui s'est posĂ© devant moi Ă©tait la recherche d'une maquette de base de donnĂ©es, afin de ne pas trouver moi-mĂȘme des choses incomprĂ©hensibles, et je suis allĂ© sur google et j'ai lu le wiki postgres . En consĂ©quence, je me suis installĂ© sur leur base de dĂ©monstration sur les vols Ă travers la Russie.
, . , . , , , 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 est itĂ©rable et il exĂ©cute sa requĂȘte de base de donnĂ©es la premiĂšre fois que vous l'itĂ©rez. Par exemple, cela imprimera le titre de toutes les entrĂ©es de la base de donnĂ©es:
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
Contient
Commençons par contient, cela fonctionne essentiellement comme un 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%
Afin d'obtenir le rĂ©sultat de curl, j'ai exĂ©cutĂ© la requĂȘte comme suit (comptĂ© en secondes):
$ curl -w "%{time_total}\n" -o /dev/null -s http://127.0.0.1:8000/api/tickets/?name=IVAN
1,242888
Je mets tout dans un tableau sur la feuille appropriée.
â , 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) , , , , .
Dans l'ensemble, ma compréhension de certains des fonctionnements internes de django s'est stabilisée grùce à cette recherche. Et finalement vint la réalisation de la différence entre la recherche de sous-chaßnes et la recherche de texte intégral. La différence dans leur implémentation via Django ORM.