使用Sequelize.js和PostgreSQL查询关联模型上的JSONB字段

时间:2017-09-26 08:09:48

标签: node.js postgresql typescript sequelize.js

我有两个模型FooBarFoo有一个字段barId,因此有一个Bar对象与之关联。 我可以查询我的所有Foo个对象并将其关联的Bar对象包含在内(我使用的是带有sequelize-typescript的TypeScript):

Foo.findAll<Foo>({
  include: [{ model: Bar }]
});

Bar对象具有结构

的JSONB字段jsonb_field
{ inner_field1: 'some text', inner_field2: 'some more text' }

我可以查询Bar个对象并按inner_field1进行过滤:

Bar.findAll<Bar>({
  where: { 'jsonb_field': { inner_field1: 'text to find' } }
});

这会生成以下SQL查询:

SELECT ... FROM "Bar" AS "Bar" 
WHERE ("Bar"."jsonb_field"#>>'{inner_field1}') = 'text to find'

到目前为止一切顺利。现在让我们尝试查询Foo个对象,包括Bar个对象并按inner_field1过滤:

Foo.findAll<Foo>({
  where: { '$bar.jsonb_field$': { inner_field1: 'text to find' } },
  include: [{ model: Bar }]
});

现在这引发了一个异常:

Error: Invalid value [object Object]
    at Object.escape ({project_root}\node_modules\sequelize\lib\sql-string.js:50:11)
    at Object.escape ({project_root}\node_modules\sequelize\lib\dialects\abstract\query-generator.js:917:22)
    at Object.whereItemQuery ({project_root}\node_modules\sequelize\lib\dialects\abstract\query-generator.js:2095:41)
    at _.forOwn ({project_root}\node_modules\sequelize\lib\dialects\abstract\query-generator.js:1937:25)
    ...

为了记录,我正确地包含了Bar对象,因为我可以按照其他非JSONB属性进行过滤:

Foo.findAll<Foo>({
  where: { '$bar.number_field$': 5 },
  include: [{ model: Bar }]
});

据我所知,问题在于Sequelize没有意识到jsonb_field的类型,因此在将对象传递给where查询时会抛出错误。

是否可以解决此错误,可能使用sequelize.literal()sequelize.json()

1 个答案:

答案 0 :(得分:0)

使用Kukodas-MBP:~ kukodajanos$ ./oc new-app nodejs~git@bitbucket.org:j4nos/nodejs.git --name mysite --> Found image cd02d02 (3 weeks old) in image stream "openshift/nodejs" under tag "6" for "nodejs" Node.js 6 --------- Node.js 6 available as docker container is a base platform for building and running various Node.js 6 applications and frameworks. Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices. Tags: builder, nodejs, nodejs6 * A source build using source code from ssh://git@bitbucket.org/j4nos/nodejs.git will be created * The resulting image will be pushed to image stream "mysite:latest" * Use 'start-build' to trigger a new build * This image will be deployed in deployment config "mysite" * Port 8080/tcp will be load balanced by service "mysite" * Other containers can access this service through the hostname "mysite" --> Creating resources ... imagestream "mysite" created buildconfig "mysite" created deploymentconfig "mysite" created sequelize.cast运营商:

$contains

或者按照您的建议使用Foo.findAll<Foo>({ where: { '$bar.jsonb_field$': { $contains: sequelize.cast('{ "inner_field1": "text to find" }', 'jsonb') }, include: [{ model: Bar }] }); 执行此操作:

sequelize.literal

这两个解决方案都容易受SQL Injections攻击,以确保转义或删除可能导致问题的所有字符(Foo.findAll<Foo>({ where: { '$bar.jsonb_field$': { $contains: sequelize.literal(`'{ "inner_field1": "text to find" }'::json`) }, include: [{ model: Bar }] }); ",...)

是的,我刚刚回答了我自己的问题。不客气。