Django, Создание GIN-индекса для дочернего элемента в поле массива JSON
У меня есть модель, которая использует PostgreSQL и имеет поля следующего вида:
class MyModel(models.Model):
json_field = models.JSONField(default=list)
Это поле содержит следующие данные:
[
{"name": "AAAAA", "product": "11111"},
{"name": "BBBBB", "product": "22222"},
]
Теперь я хочу индексировать по полю json_field -> product, потому что оно используется в качестве идентификатора. Затем я хочу создать GinIndex следующим образом:
class Meta:
indexes = [
GinIndex(name='product_json_idx', fields=['json_field->product'], opclasses=['jsonb_path_ops'])
]
Когда я пытаюсь создать миграцию, я получаю ошибку, подобную этой:
'indexes' refers to the nonexistent field 'json_field->product'.
Как создать GinIndex, который будет использоваться для дочернего атрибута в массиве Json?
Пожалуйста, не используйте JSONField [Django-doc] для хорошо структурированных данных: если структура ясна, как здесь, где у нас есть список объектов, где у каждого объекта есть название и продукт, то имеет больше смысла работать с дополнительными моделями, например:
class MyModel(models.Model):
# …
pass
class Product(models.Model):
# …
pass
class Entry(models.Model):
my_model = models.ForeignKey(MyModel, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
Это автоматически добавит индексы на ForeignKeys, но также сделает запрос более простым и, как правило, более эффективным.
While databases like PostgreSQL indeed have put effort into making JSON columns easier to query, aggregate, etc. usually it is still beter to perform database normalization [wiki], especially since it has more means for referential integrity, and a lot of aggregates are simpeler on linear data.
Если, например, позже продукт будет удален, то потребуется много работы по проверке JSON блобов для удаления этого продукта. Однако это сценарий, который и Django, и базы данных PostgreSQL покрывают триггерами ON DELETE, и который, вероятно, будет более эффективным и безопасным при использовании инструментария Django для этого.