Knexjs PgSQL JSON запрос

1

У меня есть столбец в 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

Любые идеи о том, как я могу это сделать?

заранее спасибо!

Теги:
knex.js

2 ответа

1

Для поиска определенного ключа из поля 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 запросов.

0

Как работают привязки:

Прежде всего, вам нужно знать, как обязательная работа в используемой вами библиотеке.

Из отладочного журнала, который вы указали, показывает, что вы 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

Какой стиль вы предпочитаете, полностью зависит от ваших предпочтений, и я просто указываю, как еще вы можете это сделать :)

Надеюсь, помогает :)

  • 0
    Извините, я на самом деле сделал тип в моем вопросе. Второй пример не терпит неудачу, потому что я использую три привязки, я фактически использую только две, которые я хочу. Вы можете видеть это в логах knex:bindings [ 'admin', true ] +0ms . Это была просто ошибка во фрагменте кода: / И я понимаю, что на самом деле является результатом первого примера, я спрашиваю, есть ли какой-то способ обойти это и почему он также терпит неудачу во втором примере.
  • 0
    Вы можете попробовать один из приведенных мной примеров советов по SQL. Также во втором (ручное приведение) вы можете попытаться привести value также как ... = ?::boolean'. For the first one you may try ?? `связывание вместо ? для логической части также?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню