Knexjs PgSQL json查询

tia*_*ive 5 javascript postgresql json node.js knex.js

我在 Postgres 中有一个列存储一些 JSON 数据。JSON 没有定义模式,但应该可以搜索具有某些指定键的所有记录。

我正在使用 KnexJS 构建查询,到目前为止我想出了这个:

tx.select('*').from('table')
.whereRaw('cast(data->>? as ?) = ?', [key, type, JSON.parse(value)]));
Run Code Online (Sandbox Code Playgroud)

但它不起作用,因为我认为不可能指定类型。

不过,当我尝试像这样手动指定它时:

tx.select('*').from('table')
.whereRaw('cast(data->>? as boolean) = ?', [key, JSON.parse(value)]));
Run Code Online (Sandbox Code Playgroud)

还是不行啊!这是使用时控制台的日志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
Run Code Online (Sandbox Code Playgroud)

关于如何实现这一目标有什么想法吗?

提前致谢!

Mik*_*stö 6

要从 JSONB 字段搜索特定键,您可以使用?,?|?&运算符,但从问题来看,我相信您实际上是在尝试查找某些键具有特定值的所有行。

PostgreSQL 协议不支持将类型作为绑定传递,因此您需要将其作为原始字符串传递,就像在第二个示例中所做的那样。

然而你仍然在那里做一些非常奇怪的事情:

tx.select('*').from('table')
  .whereRaw('cast(data->>? as boolean) = ?', [key, JSON.parse(value)]));
Run Code Online (Sandbox Code Playgroud)

data->>?以字符串形式返回您的 json 属性值。然后将其转换为布尔值并将其与 JSON.parse(value) 的某个值进行比较,该值可能是任何值。

从错误{ key: 'admin', value: 'true', type: 'boolean' }看来,您的值实际上已经是一个字符串,所以这应该有效:

tx.select('*').from('table')
  .whereRaw('data->>? = ?', [key, JSON.parse(value)]));
Run Code Online (Sandbox Code Playgroud)

无论如何,由于对“true”字符串进行了显式转换,您的第二个示例也应该有效。我添加了 knex 示例,表明您的案例应该有效:

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 } } ]
Run Code Online (Sandbox Code Playgroud)

更多如何使用 postgresql 进行 jsonb 查询的信息可以在这里找到https://www.vincit.fi/en/blog/objection-js-postgresql-power-json-queries/并且基于 knex 的 ORM objection.js 也明确支持postgres jsonb 查询。


小智 1

绑定如何工作:

首先,您需要知道绑定在您使用的库中如何工作。

从您提供的调试日志中,它显示您的值为type字符串:

key: 'admin', value: 'true', type: 'boolean'
Run Code Online (Sandbox Code Playgroud)

因此第一个查询及其实际的 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;
Run Code Online (Sandbox Code Playgroud)

这提供了清晰的语法错误,该错误也可以在 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'
Run Code Online (Sandbox Code Playgroud)

这自然会失败。

当您使用它时,一些 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
Run Code Online (Sandbox Code Playgroud)

您喜欢什么风格完全取决于您的喜好,我只是指出您还可以如何做到这一点:)

希望有帮助:)