بدلا من مقدمة
بدأ كل شيء بحقيقة أنه عرض علي المشاركة في المشروع في إطار موضوع "أساسيات برمجة الويب" ، بدلاً من القيام بالمختبرات والدورات الدراسية ، حيث ذكرت أنني أردت أن أفعل شيئًا بعيدًا عن الدورة العامة (وبالتالي كان هناك بالفعل ما يكفي من المعرفة على مجموعة من DRF + Vue ، أردت شيئًا جديدًا). وهكذا قررت في أحد PRs الخاصة بي على github استخدام البحث عن النص الكامل (المهمة التي تم التلميح إليها في هذا) لتصفية المحتوى ، مما جعلني أنتقل إلى وثائق Djangoبحثًا عن أفضل السبل لتنفيذ هذا العمل. أعتقد أنك تعرف معظم الأساليب المقترحة هناك (تحتوي على icontains و trigram_similar). جميعها مناسبة لبعض المهام المحددة ، ولكنها ليست جيدة جدًا في البحث عن نص كامل. بالتمرير قليلاً ، صادفت قسمًا تحدث عن تفاعل Django و Pgsql لتنفيذ البحث المستند إلى المستندات ، الأمر الذي جذبني ، نظرًا لأن postgre يحتوي على أداة مدمجة لتنفيذ هذا البحث [النص الكامل]. وقررت أنه على الأرجح ، يوفر django ببساطة واجهة برمجة تطبيقات لهذا البحث ، بناءً على ما يجب أن يعمل عليه هذا الحل وأكثر دقة وأسرع من أي خيارات أخرى. جادلنا المعلم ، لم يصدقني كثيرًا ، وعرض عليه إجراء بحث حول هذا الموضوع. وها أنا ذا.
بداية العمل
كانت المشكلة الأولى التي نشأت قبلي هي البحث عن نموذج لقاعدة البيانات ، حتى لا أتوصل إلى أي أشياء غير مفهومة بنفسي ، وذهبت إلى google وأقرأ postgres wiki . ونتيجة لذلك ، استقرت في قاعدتهم التجريبية حول الرحلات الجوية عبر روسيا.
حسنًا ، تم العثور على القاعدة. تحتاج الآن إلى تحديد طرق التصفية التي سيتم استخدامها للمقارنة. أول شيء أود استخدامه هو بالطبع طريقة البحث القياسية من django.contrib.postgres.search. يحتوي الثاني على (يبحث عن كلمة في سلسلة) ويحتوي على icontains (يوفر بيانات ، ويتجاهل اللهجات ، على سبيل المثال: بالنسبة لطلب البحث "Helen" ، ستكون النتيجة: <Author: Helen Mirren> ، <Author: Helena Bonham Carter> ، <Author: Hélène Joy>) الذي يوفره جانغو نفسه. أريد أيضًا مقارنة جميع طرق التصفية هذه بالبحث المدمج داخل postgresql. قررت البحث عن جدول التذاكر في النسخة الصغيرة التي تحتوي على 366733 إدخالاً. سيتم إجراء البحث في حقل اسم_الراكب ، والذي ، كما قد تتوقع ، يحتوي على اسم الراكب. هو مكتوب في الترجمة الصوتية.
دع django يعمل مع قاعدة بيانات موجودة
— django . django , , :
$ python manage.py inspectdb > models.py
, , settings.py. . , . , ( ), , 300+ , 10, , . , , curl. .
, , , curl, , . , ( ).
django
, — , queryset - . .
إن QuerySet قابل للتكرار ، ويقوم بتنفيذ استعلام قاعدة البيانات الخاص به في المرة الأولى التي تقوم فيها بالتكرار فوقه. على سبيل المثال ، سيؤدي هذا إلى طباعة العنوان الرئيسي لجميع الإدخالات في قاعدة البيانات:
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
يحتوي على
لنبدأ مع يحتوي على ، فهو يعمل في الأساس مثل 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%
من أجل الحصول على النتيجة من curl ، قمت بتنفيذ الطلب على النحو التالي (محسوب بالثواني):
$ curl -w "%{time_total}\n" -o /dev/null -s http://127.0.0.1:8000/api/tickets/?name=IVAN
1,242888
أضع كل شيء في جدول على الورقة المناسبة.
— , 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) , , , , .
بشكل عام ، استقر فهمي لبعض الأعمال الداخلية لجانغو بفضل هذا البحث. وأخيرًا ، أدرك الفرق بين البحث عن سلسلة فرعية والبحث عن النص الكامل. الفرق في تنفيذها من خلال Django ORM.