Выборка по наличию ключа в json

Здравствуйте.

В 9.2 появился долгожданный JSON, а в 9.3 добавились функции обработки значений в нём, но мне так и не понятно из документации как можно сортировать по ключу в JSON-поле. Подход с hstore:
SELECT params->'a' AS a FROM test WHERE params ? 'a';
не подходит...

Допустим есть таблица:

CREATE TABLE "test" (
    "id" serial NOT NULL PRIMARY KEY,
    "params" json,
);

Засунем в неё данные:

INSERT INTO test(params) VALUES ('"test"');
INSERT INTO test(params) VALUES ('[1,2,3]');
INSERT INTO test(params) VALUES ('{"a":"test", "b":123}');
INSERT INTO test(params) VALUES ('{"a":"test", "c":{"d":1}}');

Как выбрать строки, где есть ключ "a" и "b"?
Как выбрать строки, где есть ключ "a" и ("b" или "c")?
Как выбрать строки, где в ключе "c" есть ключ "d"?
Как выбрать строки, где полем является строка?
Как выбрать строки, где полем является список?
Как выбрать строки, где полем является объект?
Это вообще возможно и можно ли строить индекс по этим вещам?

UPD: Вопрос открыт всё же... И как строить индекс на наличие ключа, если ключей, собственно, ну очень много?

Опции просмотра комментариев

Выберите предпочитаемый вами способ показа комментариев и нажмите "Сохранить настройки" для активации изменений.

Только нужно учесть, что

Только нужно учесть, что данные в поле params всё же в json, а не в string или array.

>>Как выбрать строки, где есть ключ "a" и "b"? + сортировка

SELECT * 
FROM test
WHERE (params->>'a') IS NOT NULL AND (params->>'b') IS NOT NULL
ORDER BY params->>'a' DESC

>>Как выбрать строки, где есть ключ "a" и ("b" или "c")?

SELECT *
FROM test
WHERE (params->>'a') IS NOT NULL AND 
	((params->>'b') IS NOT NULL OR (params->>'c') IS NOT NULL)
ORDER BY params->>'a' DESC

>>Как выбрать строки, где в ключе "c" есть ключ "d"?

SELECT *
FROM test
WHERE ((params->'c')->>'d') IS NOT NULL 
ORDER BY params->>'a' DESC

>>Это вообще возможно и можно ли строить индекс по этим вещам?
Возможно, как показано выше, индекс по условию.

Что касается запихивания в json string и array - это, мне кажется, извращение. Способ, определённо есть, например в ветке dev сейчас есть функция json_typeof - которая и определяет тип вашего json. Наверное есть и способы "через задний проход", или написать функцию самому, но, считаю, это не целесообразным и это приведёт к тому, что в каждом верхнем запросе придётся всегда делать поправку на тип данных, которым вы пытаетесь воспользоваться в данный момент.

Работает но не так

Вернее не совсем так ка хотелось бы.
Если в таблице есть такие разношёрстные данные, то любой запрос выше вообще выпадает в осадок:

SELECT * 
FROM test
WHERE (params->>'a') IS NOT NULL AND (params->>'b') IS NOT NULL
ORDER BY params->>'a' DESC;
 
ОШИБКА:  извлечь поле из не объекта нельзя
 
********** Ошибка **********

В этом то и вся соль, что все перечисленные данные - вполне себе JSON согласно спеки...

Насчёт string согласен, а вот насчет незапихиввания array - что делать со вложенными?

Как видно, команда PostgreSQL

Как видно, команда PostgreSQL особо не заморачивалась над полнейшем покрытии всех нужд связанных с json, при этом как бы показало: что делать с этой штукой можно всё, что угодно (OpenSource же). Но это ведь не освобождает от следования простой логике и хоть какой-нибудь культуре проектирования? Не стоит запихивать arr и str в поле json, моё мнение.
А как же тогда быть?
Можно договориться о типе данных, например:

'{"type": "AClass", "a":"test", "b":123}'
'{"type": "ArrayClass", "array":[1,2,3]}'

>> а вот насчет незапихиввания array - что делать со вложенными?
Ты имеешь в виду под "вложенными" вот такой массив:
'{"type": "ArrayClass", "array":[1,2,3]}'
?

Другой

матрицу:
[[1,2,3,4],[1,2,3,4]]

В тему

Опишу чуть подробнее реальную задачу.
Нужно не дожидаясь выхода 9.4 решить нюансы:

CREATE TABLE "good" (
    "id" serial NOT NULL PRIMARY KEY,
    "supplier_id" integer, -- foreign key
    "title" varchar(255),
    "prices" json,
);

В ценах нужно хранить такой приблизительный JSON:
{"all":10, "client1": 10.99, "client2": 11.01,  ...}

Товаров около полумиллиона для всех поставщиков. Цены для каждого товара разнятся от клиента к клиенту по каждому поставщику. Клиентов около 300. Но при этом у многих клиентов цена для него == цене для всех. Это облегчает ситуацию, но ненамного.

То есть заводить отдельную таблицу с ценами - это ооочень много строк в этой табличке (более 20 лимонов). И добавление/обновление прайса одного поставщика становится непреодолимой задачей (на несколько десятков минут с большой нагрузкой сервера). А прайсы обновляются каждые полчаса как минимум...
Другое дело писать цены товара в одно поле - это очень быстро. Но...

Hstore для поля prices не вариант (нет правильной сортировки по float, я так понимаю). Или я не правильно понимаю?

Остаётся JSON. Но по нему нужно создавать индекс для сортировки (какой можно сделать в 9.3?) и как сделать проверку по наличию ключа (например так: 'client1' in prices OR 'all' in prices).
Конечно, часть вопросов берёт на себя plv8, но вот как ускорить индексами - не понимаю...
Индекс по условию - не вариант, т.к. индексов будет по количеству клиентов - то есть около 300...
Может быть кто предложит вариант класса операторов для "единственного" индекса на это поле?

Свалить на Монго не предлагать ;) - другие задачи от этого усложнятся...

P.S.: Если это прочитают НАШИ разработчики JSONB - надеюсь к релизу 9.4 внесут необходимое (ребята, я в вас верю!).

UPD: hstore по моим тестам не подходит:

dev=> SELECT prices FROM test ORDER BY (prices->'a')::float;
 
                   prices                    
---------------------------------------------
 "a"=>"-0.98"
 "a"=>"-0.2"
 "a"=>"-0.1"
 "a"=>"0.98"
 "a"=>"1", "b"=>"2.99", "c"=>"2.0", "d"=>"2"
 "a"=>"1.0"
 "a"=>"1", "b"=>"2.99", "c"=>"2.0", "d"=>"2"
 "a"=>"1.36"
 "a"=>"1.99"
 "a"=>"2"
 "a"=>"10.01"
 "a"=>"10.03"
 "a"=>"10.05"
 "a"=>"10.11"
 "a"=>"10.12"
 "a"=>"10.56"
 "a"=>"20.56"
 "a"=>"101.56"
 "a"=>"201.56"
 "a"=>"203.56"
(20 строк)

psql (9.4beta2, сервер 9.3.5) из apt.postgresql.org
То же самое в pgAdmin3.
Это нормально (строки 1 и 2) или ошибка в реализации преобразования hstore?

Опции просмотра комментариев

Выберите предпочитаемый вами способ показа комментариев и нажмите "Сохранить настройки" для активации изменений.

Back to top

(С) Виктор Вислобоков, 2008-2023