代替序言
一切始于这样一个事实:我被安排在“ Web编程基础”主题的框架内参加该项目,而不是从事实验室工作和课程工作,因为我说过我想做一些远离一般课程的事情(因此,已经有足够的知识了)在一堆DRF + Vue上,我想要一些新东西)。因此,在github上的我的PR中,我决定使用全文搜索(在此提示的分配)来过滤内容,这使我转向Django文档寻找如何最好地实施这项业务。我认为您知道那里建议的大多数方法(包含,图标包含,trigram_like)。它们全都适合某些特定任务,但不是很擅长,即全文搜索。向下滚动一下,我遇到了一个部分,该部分讨论了Django和Pgsql的交互以实现基于文档的搜索,这吸引了我,因为postgre具有用于实现此[全文搜索]的内置工具。而且我认为django最有可能只是为这种搜索提供一个API,这是基于这种解决方案应该能起作用的原理,并且比任何其他选项都更准确,更快。老师不相信我太多,我们与他争论,他提议对此主题进行研究。我在这里。
开始工作
摆在我面前的第一个问题是搜索数据库模型,以免自己搞不清楚任何事情,于是我去google并阅读了postgres wiki。结果,我在他们的演示基地上确定了有关俄罗斯各地航班的信息。
好的,已经找到了基地。现在,您需要确定将使用哪些过滤方法进行比较。我首先要使用的是django.contrib.postgres.search中的标准搜索方法。第二个是包含(搜索字符串中的单词)和图标集(提供数据,忽略重音,例如:对于查询“ Helen”,结果将是:<作者:Helen Mirren>,<作者:Helena Bonham Carter>,<作者:HélèneJoy>) django本身提供的。我还想将所有这些过滤方法与postgresql中的内置搜索进行比较。我决定在小版本中搜索票证表,其中包含366733个条目。搜索将在passenger_name字段上执行,您可能会猜测其中包含乘客的姓名。它是用音译写的。
让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的一些内部工作方式的理解已经稳定下来。最终实现了子字符串搜索和全文搜索之间的区别。通过Django ORM在实现上的差异。