PostgreSQLデータベースデモの例でのさまざまなdjangoフィルターの比較

序文の代わりに



私は一般的なコースから離れた場所で何かをやりたいと述べたので、ラボの作業やコースワークを行うのではなく、「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のように機能します。



Django ORMでのクエリ/ SQLでのクエリ
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. .



Django ORM/ sql 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による実装の違い。




All Articles