序文の代わりに
私は一般的なコースから離れた場所で何かをやりたいと述べたので、ラボの作業やコースワークを行うのではなく、「Webプログラミングの基礎」のテーマの枠組みの中でプロジェクトに参加するよう提案されたという事実から始まりました(そのため、すでに十分な知識がありました) DRF + Vueの束で、私は何か新しいものが欲しかった)。そして、githubの私のPRの1つで、全文検索(これを示唆する割り当て)を使用してコンテンツをフィルタリングすることにしました。これにより、Djangoのドキュメントを参照しましたこのビジネスを実装する最善の方法を求めて。そこで提案されているほとんどのメソッド(contains、icontains、trigram_similar)はご存知だと思います。これらはすべて、特定のタスクに適していますが、全文検索はあまり得意ではありません。少し下にスクロールすると、ドキュメントベースの検索を実装するためのDjangoとPgsqlの相互作用について説明しているセクションがあり、postgreにはこの[全文]検索を実装するための組み込みツールがあるため、私を惹きつけました。そして、私はdjangoがこの検索のためのAPIを提供するだけであり、他のどのオプションよりも正確で高速であると判断しました。先生は私をあまり信じていなかったので、私たちは彼と議論しました、そして彼はこのトピックに関する研究を行うことを申し出ました。そして、私はここにいます。
仕事の始まり
私の前に最初に発生した問題は、データベースモックアップを検索することでした。自分で理解できないことを思い付かないようにするためです。Googleにアクセスしてpostgres wikiを読みました。その結果、私は彼らのデモ基地にロシアを横断する飛行について落ち着きました。
さて、ベースが見つかりました。次に、比較に使用するフィルタリング方法を決定する必要があります。もちろん、最初に使用したいのは、django.contrib.postgres.searchの標準的な検索方法です。2つ目は、contains(文字列内の単語の検索)とicontains(アクセントを無視してデータを提供します。例:クエリ "Helen"の場合、結果は<Author:Helen Mirren>、<Author:Helena Bonham Carter>、<Author:HélèneJoy>)になります。ジャンゴ自体が提供しています。また、これらすべてのフィルタリング方法をpostgresql内の組み込み検索と比較したいと思います。小さなバージョンでticketテーブルを検索することにしました。これには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
含む
containsから始めましょう。基本的に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による実装の違い。