У меня есть столбец в Postgres, который хранит некоторые данные JSON. JSON не имеет определенной схемы, но должен быть возможен поиск всех записей с определенным ключом.
Я использую KnexJS для создания запросов, и до сих пор я придумал следующее:
tx.select('*').from('table')
.whereRaw('cast(data->>? as ?) = ?', [key, type, JSON.parse(value)]));
Но это не работает, потому что я не думаю, что можно будет указать тип.
Тем не менее, когда я пытаюсь указать его вручную следующим образом:
tx.select('*').from('table')
.whereRaw('cast(data->>? as boolean) = ?', [key, JSON.parse(value)]));
Это все еще не работает! Это журнал с консоли при использовании DEBUG:knex:*
{ key: 'admin', value: 'true', type: 'boolean' }
knex:tx trx1: Starting top level transaction +0ms
knex:pool INFO pool postgresql:pg:client0 - dispense() clients=1 available=0 +2ms
knex:client acquired connection from pool: __knexUid2 +38ms
knex:query BEGIN; +2ms
knex:bindings undefined +1ms
knex:query select * from "contexts" where cast(data->>? as boolean) = ? +18ms
knex:bindings [ 'admin', true ] +0ms
knex:query COMMIT; +9ms
knex:bindings undefined +0ms
knex:tx trx1: releasing connection +6ms
knex:client releasing connection to pool: __knexUid2 +1ms
knex:pool INFO pool postgresql:pg:client0 - dispense() clients=0 available=1 +1ms
Любые идеи о том, как я могу это сделать?
заранее спасибо!
Для поиска определенного ключа из поля JSONB вы можете использовать ?
, ?|
и ?&
операторов, но из вопроса я считаю, что вы на самом деле пытаетесь найти все строки, где определенный ключ имеет определенное значение.
Протокол PostgreSQL не поддерживает тип передачи как привязку, поэтому вам нужно передать его как необработанную строку, как это было сделано во втором примере.
Однако вы все еще делаете что-то действительно странное:
tx.select('*').from('table')
.whereRaw('cast(data->>? as boolean) = ?', [key, JSON.parse(value)]));
data->>?
возвращает значение json-атрибута в виде строки. Затем вы конвертируете его в boolean и сравниваете его с некоторым значением JSON.parse(value), которое может быть почти что угодно.
Из ошибки { key: 'admin', value: 'true', type: 'boolean' }
кажется, что ваше значение на самом деле уже является строкой, поэтому это должно работать:
tx.select('*').from('table')
.whereRaw('data->>? = ?', [key, JSON.parse(value)]));
В любом случае, также ваш второй пример должен был работать из-за явного приведения, сделанного для вашей "истинной" строки. Я добавил пример кнуса, показывающий, что ваше дело должно работать:
await knex.schema.createTable('test2', t => {
t.increments('id');
t.jsonb('test');
});
await knex('test2').insert([
{ test: '{ "a": true, "b": false }' },
{ test: '{ "b": true, "a": false }' }
]);
await knex('test2').whereRaw('cast(test->>? as boolean) = ?', ['a', 'true']);
// outputs: [ anonymous { id: 1, test: { a: true, b: false } } ]
Более подробную информацию о том, как выполнять jsonb-запросы с помощью postgresql, можно найти здесь: https://www.vincit.fi/ru/blog/objection-js-postgresql-power-json-queries/, а также ORM objection.js на основе комочков имеет явную поддержку для postgres jsonb запросов.
Как работают привязки:
Прежде всего, вам нужно знать, как обязательная работа в используемой вами библиотеке.
Из отладочного журнала, который вы указали, показывает, что вы type
как строку:
key: 'admin', value: 'true', type: 'boolean'
Поэтому первый запрос и его фактический перевод SQL:
// Your code:
tx.select('*').from('table')
.whereRaw('cast(data->>? as ?) = ?', [key, type, JSON.parse(value)]));
// Actual SQL:
SELECT * FROM "table" WHERE cast(data->>'admin' as 'boolean') = true;
Это обеспечивает четкую синтаксическую ошибку, которая также видна в ваших журналах Postgres.
Второй запрос (когда вы выполняете вручную) терпит неудачу, потому что вы сохранили 3 параметра привязки при использовании только 2 и второго параметра по type
порядка массива вместо value
:
// Your code:
tx.select('*').from('table')
.whereRaw('cast(data->>? as boolean) = ?', [key, JSON.parse(value)]));
// Actual SQL:
SELECT * FROM "table" WHERE cast(data->>'admin' as boolean) = 'undefined'
Что, естественно, не удается.
Пока вы на нем, некоторые советы SQL:
Postgres дает вам несколько других опций/стилей, как вы можете выполнить свой запрос, и вот несколько других примеров (все дают одинаковые результаты):
// Casting with '=' s
SELECT * FROM table WHERE (data ->> 'admin')::boolean = TRUE // finds all 'true' values
SELECT * FROM table WHERE NOT ( (data ->> 'admin')::boolean = FALSE ) // finds all 'false' values
// Since every condition in WHERE ends up boolean you can avoid
// using '=' comparsion and shorten the code
SELECT * FROM table WHERE (data ->> 'admin')::boolean // finds all 'true' values
SELECT * FROM table WHERE NOT ( (data ->> 'admin')::boolean ) // finds all 'false' values
// When using JSONB data type you can use '->' operator instead
SELECT * FROM table WHERE data -> 'admin' = 'true' // finds all 'true' values
SELECT * FROM table WHERE NOT ( data -> 'admin' = 'false' ) // finds all 'false' values
Какой стиль вы предпочитаете, полностью зависит от ваших предпочтений, и я просто указываю, как еще вы можете это сделать :)
Надеюсь, помогает :)
knex:bindings [ 'admin', true ] +0ms
. Это была просто ошибка во фрагменте кода: / И я понимаю, что на самом деле является результатом первого примера, я спрашиваю, есть ли какой-то способ обойти это и почему он также терпит неудачу во втором примере.value
также как... = ?::boolean'. For the first one you may try
?? `связывание вместо?
для логической части также?