Opened 2 years ago

Last modified 2 years ago

#34107 closed Bug

Django ORM queries do not pick up indexes made on a key in JSONField in Postgres — at Initial Version

Reported by: Tadek Teleżyński Owned by: nobody
Component: Database layer (models, ORM) Version: 3.2
Severity: Normal Keywords: json, jsonfield, postgres, index
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Hey folks!

Been developing in Django for the past 5 years, excited to post my first ticket :)
Thank you from the bottom of my heart for developing and maintaining this amazing framework.
If you feel like the below is more of a feature request than a bug, feel free to recategorize it.

Consider a following setup (assuming Postgres database, version 13.8):

class Example(models.Model):
    data = models.JSONField(null=False, blank=True, default=dict)

And the following migration adding two indexes (note the difference in "->" and "->>") on datacategory key:

class Migration(migrations.Migration):

    dependencies = [
        (...),
    ]
    atomic = False
    operations = [
        migrations.RunSQL(
            "CREATE INDEX CONCURRENTLY CategoryIndex_default_cast ON app_example USING BTREE((data->>'category'));",
            reverse_sql="DROP INDEX IF EXISTS CategoryIndex_default_cast;"
        ),
        migrations.RunSQL(
            "CREATE INDEX CONCURRENTLY CategoryIndex ON app_example USING BTREE (((data->'category')::TEXT));",
            reverse_sql="DROP INDEX IF EXISTS CategoryIndex;"
        ),
    ]

Now if I use a regular filtering syntax on a query none of the indexes is used:

>>> print(Example.objects.filter(data__category='dog').explain())
Seq Scan on app_example  (cost=0.00..29.05 rows=6 width=36)
  Filter: ((data -> 'category'::text) = '"dog"'::jsonb)

In order to pick up the index I need to write a bit more complex query:

>>> print(Example.objects.annotate(_category=KeyTextTransform('category', 'data')).filter(_category=Value('dog')).explain())
Bitmap Heap Scan on app_example  (cost=4.20..13.70 rows=6 width=68)
  Recheck Cond: ((data ->> 'category'::text) = 'dog'::text)
  ->  Bitmap Index Scan on categoryindex_default_cast  (cost=0.00..4.20 rows=6 width=0)
        Index Cond: ((data ->> 'category'::text) = 'dog'::text)

To be honest I wasn't able to figure out a query that would pick up the second index ("CategoryIndex") that's using explicit type casting.

It would be very neat if the ORM could figure the type casting automatically.

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top