在 Django PostgreSQL 中索引 JSONField

dar*_*rse 16 django postgresql

我正在使用一个简单的模型,该模型的属性将该对象的所有数据存储在 JSONField 中。将其视为将 NoSQL 数据传输到我的 PostgreSQL 数据库的方式。有点像这样:

from django.contrib.postgres.fields import JSONField   

class Document(models.Model):
    content = JSONField()
Run Code Online (Sandbox Code Playgroud)

每个Document对象在其content字段中(或多或少)具有相同的键,因此我正在使用这些键查询和排序这些文档。对于查询和排序,我使用的是 Django 的annotate()函数。我最近遇到了这个:

https://docs.djangoproject.com/en/2.1/ref/contrib/postgres/indexes/

我也知道 PostgreSQL 使用 JSONB,这显然是可索引的。所以我的问题是:我能否content以某种方式索引我的字段以加快复杂查询的读取操作?如果是这样,那么我该怎么做?我链接的文档页面没有示例。

kco*_*ntr 17

正如Tim Tisdall在另一个答案中评论的那样,Django 3.2 引入了对这些索引的本机支持。如果您要将这些添加到现有模型中,请务必添加makemigrationsmigrate


要索引特定键,您可以将F 表达式模型的 Meta选项上的JSONField 路径查找相结合:indexes

from django.contrib.postgres.fields import JSONField   

class Document(models.Model):
    content = JSONField()
    class Meta:
        indexes = [
            models.Index(models.F("content__example_field_name"), name="content__example_field_name_idx"),
        ]
Run Code Online (Sandbox Code Playgroud)

这将创建一个 B 树索引。


如果您想使用过滤器,例如has_key在顶级字段上(例如Document.objects.filter(content__has_key="example_field_name")),您可能需要像Mahesh H Viraktamath在另一条评论中建议的那样GinIndex

from django.contrib.postgres.fields import JSONField   
from django.contrib.postgres.indexes import GinIndex

class Document(models.Model):
    content = JSONField()
    
    class Meta:
        indexes = [
            GinIndex("content", name="content_idx"),
        ]
Run Code Online (Sandbox Code Playgroud)

最后,为了完整起见,您可以通过用表达式包装来选择您的 opclass OpClass

from django.contrib.postgres.fields import JSONField   
from django.contrib.postgres.indexes import GinIndex, OpClass

class Document(models.Model):
    content = JSONField()
    
    class Meta:
        indexes = [
            GinIndex(
                OpClass(models.F("content__example_field_name"), name="jsonb_path_ops"),
                name="content__example_field_name_idx",
            ),
        ]
Run Code Online (Sandbox Code Playgroud)


rad*_*tek 9

对于那些想要索引特定键的人,创建一个原始的 sql 迁移:

  1. 运行您要为其更改索引的模型的应用程序./manage.py makemigrations --empty yourApp所在的位置yourApp

  2. 编辑迁移即

operations = [
    migrations.RunSQL("CREATE INDEX idx_name ON your_table((json_field->>'json_key'));")
]
Run Code Online (Sandbox Code Playgroud)

哪里idx_name是索引的名称,your_table是你的表,json_field是你的 JSONField,json_key在这种情况下是你想要索引的键。

应该可以,但要验证一切顺利,请运行以下 sql:

SELECT
    indexname,
    indexdef
FROM
    pg_indexes
WHERE
    tablename = '<your-table>';
Run Code Online (Sandbox Code Playgroud)

看看你的索引是否在那里。

  • 我认为本机支持是在 3.2 中添加的,作为此 PR 的一部分:https://github.com/django/django/pull/11929 但我不清楚如何使用。根据该 PR,所有请求 JSONB 字段索引的 Django 票证均已关闭。 (2认同)