Djangoからicotainsへの翻訳


サマリ

DjangoにおいてPostgreSQLが使用される場合、<something>.filter(<field>__icontains=<target>)ilikeではなくUPPER(field) like UPPER(target)に変換される.fieldにインデックスが付いている場合は、インデックスを付けることはできません.ilikeでインデックスを付ける必要があります.これらの部分は硬いSQLを使うべきです.

に期待

from django.db import models
from django.contrib.postgres.indexes import GinIndex

class Post(models.Model):
    title = models.CharField(blank=False, null=False, max_length=50)
    content = models.TextField(blank=True, null=False)
    
    class Meta:
        indexes = [
            GinIndex(
                name="title_idx", fields=["title"], opclasses=["gin_trgm_ops"]
            ),
        ]
前述のように、Postモデルのtitleにインデックスを作成し、次のSQLを解放します.
SELECT "board_post"."id",
       "board_post"."issued_date",
       "board_post"."last_modified",
       "board_post"."title",
       "board_post"."content"
  FROM "board_post"
 WHERE ("board_post"."issued_date" >= '2020-12-28T19:51:54.775082+09:00'::timestamptz AND "board_post"."issued_date" <= '2021-06-28T19:51:54.775025+09:00'::timestamptz AND "board_post"."title"::text ILIKE '%테스트%')
 ORDER BY "board_post"."issued_date" DESC
 LIMIT 21;
39ミリ秒もかかり、インデックスがよく利用されているのがわかります.

UPPERによるトラブルシューティング


前のクエリーと同じですが、表示されるクエリーのみが少し異なります.
SELECT "board_post"."id",
       "board_post"."issued_date",
       "board_post"."last_modified",
       "board_post"."title",
       "board_post"."content"
  FROM "board_post"
 WHERE ("board_post"."issued_date" >= '2020-12-28T19:51:54.775082+09:00'::timestamptz AND "board_post"."issued_date" <= '2021-06-28T19:51:54.775025+09:00'::timestamptz AND UPPER("board_post"."title"::text) ILIKE UPPER('%테스트%'))
 ORDER BY "board_post"."issued_date" DESC
 LIMIT 21;
以上の問い合わせはどのくらいかかりますか?285ミリ秒かかりました.どうして.

テーブルにインデックスが全く使用されていないため、フルスキャン中です.そして、張高は上記のようにicontains翻訳される.
q = Post.objects.filter(title__icontains="테스트")
print(q.query)
どんな結果になるのでしょうか.
UPPER("board_post"."title"::text) LIKE UPPER(%테스트%))
上図に示すように、両側にupperを適用するsqlが表示されます.したがって、インデックスは取得できません.

しゅうふく


以上の場合、extraを用いて条件文にクエリー文を直接挿入して訂正することができる.下に書いてください.
<queryset>.extra(
	where=[""""board_post"."title"::text ILIKE %s"""], params=[f"%테스트%"]
    )
前述したように、ilikeを強制的に使用して修正することができる.最初の予想と同じクエリーを使用してインデックスを作成できるようになりました.