Заказ наборов запросов Django с использованием свойств JSONField

У меня есть модель, которая выглядит примерно так:

class Person(models.Model):
    data = JSONField()

Поле data имеет 2 свойства: name и age. Теперь предположим, что я хочу получить набор запросов с разбивкой на страницы (каждая страница содержит 20 человек) с фильтром, где age больше 25, и набор запросов должен быть упорядочен в порядке убывания. В обычной настройке, то есть в нормализованной базе данных, я могу написать этот запрос так:

person_list_page_1 = Person.objects.filter(age > 25).order_by('-age')[:20]

Теперь, какова эквивалентность вышеизложенного при фильтрации и упорядочении с использованием ключей, хранящихся в JSONField? Я исследовал это, и кажется, что это должно было быть функцией для 2.1, но я не могу найти ничего подходящего.

Ссылка на тикет о реализации в будущем

У меня также есть еще один вопрос. Допустим, мы фильтруем и упорядочиваем с помощью JSONField. Должен ли ORM в таком случае получать все объекты, фильтровать и упорядочивать их перед отправкой первых 20? То есть будет ли производительность законно медленнее?

Очевидно, я знаю, что нормализованная база данных намного лучше подходит для таких вещей, но у меня немного связаны руки.


person darkhorse    schedule 07.10.2018    source источник
comment
Вы пробовали .order_by('-data__age')?   -  person Antoine Pinsard    schedule 08.10.2018
comment
@AntoinePinsard Да, я получаю эту ошибку: FieldError: Не удается преобразовать ключевое слово «возраст» в поле. Присоединение к «данным» не разрешено.   -  person darkhorse    schedule 09.10.2018
comment
Если вам нужно индексировать, фильтровать или упорядочивать по полю, почему бы не сделать его правильным полем столбца/модели вместо того, чтобы злоупотреблять jsonfields.   -  person Håken Lid    schedule 09.10.2018
comment
@HåkenLid Довольно лениво предполагать, что это злоупотребление jsonfields, не зная ничего другого о том, что это такое, и это сработает. Как я сказал в вопросе, это должно быть jsonfield, и да, я полностью осознаю преимущества нормализации.   -  person darkhorse    schedule 09.10.2018


Ответы (1)


Вы можете использовать синтаксис postgresql sql для извлечения подполей. Затем их можно использовать так же, как любое другое поле модели в фильтрах набора запросов.

from django.db.models.expressions import RawSQL
Person.objects.annotate(
    age=RawSQL("(data->>'age')::int", [])
).filter(age__gte=25).order_by('-age')[:20]

См. документы postgresql для других операторов и функций. В некоторых случаях может потребоваться добавить явное приведение типов (например, ::int).

https://www.postgresql.org/docs/current/static/functions-json.html

Производительность будет ниже, чем при правильном поле, но это неплохо.

person Håken Lid    schedule 08.10.2018
comment
Что касается приведения типов, что, если это строка в формате -10,50, то есть десятичное число, хранящееся в виде строки. Я не смог найти числовое приведение типов для Postgres. - person darkhorse; 09.10.2018
comment
Должно быть возможно. Попробуйте ::numeric(99,2), например. Числовой тип Postgresql будет приведен Django к типу python Decimal. postgresql.org/docs/current/static/datatype-numeric.html< /а> - person Håken Lid; 09.10.2018
comment
Большое спасибо, это в основном решило все мои проблемы. И только что проверил его на 10 000+ строк, это удивительно быстро! - person darkhorse; 09.10.2018