nodejs + sequelize :: 在需要的地方包含 false
Posted
技术标签:
【中文标题】nodejs + sequelize :: 在需要的地方包含 false【英文标题】:nodejs + sequelize :: include where required false 【发布时间】:2015-09-14 11:37:30 【问题描述】:我有以下对象层次结构:
-
用户
父母(hasOne)
儿童 (hasMany)
一旦我有了一个用户对象,我就会尝试加载关联的父对象及其一些子对象。以下作品:
user.getParent(
include: [
model: Child
]
).then(function(parent)
var children = parent.children;
);
但如果我想选择性地加载 一些 父母的孩子,像这样:
user.getParent(
include: [
model: Child,
where: gender: 'FEMALE'
]
).then(function(parent)
var daughters = parent.daughters;
);
如果父母有一个或多个女儿,则查询有效,我会得到父母数据以及所有女儿的数据。 但是,如果父对象只有儿子,则返回的父对象是null
..
我希望如果父对象没有女儿,父对象仍应使用children = null
或children = []
.. 解析。
另外,如果我想在加载父级时简单地加载子级计数,我该怎么做呢?
谢谢
【问题讨论】:
【参考方案1】:原来是我自己的代码有问题。如果嵌套包含有一个where
子句不返回任何模型,sequelize 无法加载模型。为确保它不会完全失败,您可以设置一个 @ 987654323@ 子句和 where
子句..这使得 sequelize 返回一个空白数组而不是完全失败加载..
更多信息https://github.com/sequelize/sequelize/issues/4019
【讨论】:
【参考方案2】:required: false
+ where
在 sequelize 5.0.2 SQLite vs PostgreSQL 中不一致/有问题
这是 Sequelize 关联错误/不一致的众多问题之一,这让我想在某个时候尝试找到另一个 ORM。报道于:https://github.com/sequelize/sequelize/issues/13809
在我们想要的地方考虑以下示例:
列出所有标签
此外,确定每个标签是否已分配给选定的动物
我们对动物的 where 条件是一个简单的“检查动物是否是特定动物”,因此只能返回 0 或 1 个条目。但同样适用于任何其他更复杂的条件。
main.js
#!/usr/bin/env node
const assert = require('assert')
const path = require('path')
const DataTypes, Sequelize = require('sequelize')
let sequelize
if (process.argv[2] === 'p')
sequelize = new Sequelize('tmp', undefined, undefined,
dialect: 'postgres',
host: '/var/run/postgresql',
)
else
sequelize = new Sequelize(
dialect: 'sqlite',
storage: 'tmp.sqlite'
)
;(async () =>
const Animal = sequelize.define('Animal',
species: type: DataTypes.STRING ,
)
const Tag = sequelize.define('Tag',
name: type: DataTypes.STRING ,
)
Animal.belongsToMany(Tag, through: 'AnimalTag' )
Tag.belongsToMany(Animal, through: 'AnimalTag' )
await sequelize.sync(force: true)
const animal0 = await Animal.create( species: 'bat' )
const animal1 = await Animal.create( species: 'ant' )
const animal2 = await Animal.create( species: 'dog' )
const tag0 = await Tag.create( name: 'mammal' )
const tag1 = await Tag.create( name: 'flying' )
const tag2 = await Tag.create( name: 'aquatic' )
await animal0.addTag(tag0)
await animal0.addTag(tag1)
await animal2.addTag(tag1)
const animals = [animal0, animal1, animal2]
for (let animal of animals)
let rows
rows = await Tag.findAll(
include: [
model: Animal,
where: id: animal.id ,
// No effect.
//on: id: animal.id ,
through:
// Same as putting the where outside of through.
//where: AnimalId: animal.id ,
,
required: false,
],
order: [['name', 'ASC']],
)
console.error(animal.species);
console.error(rows.map(row => return
name: row.name,
animals: row.Animals.map(animal => animal.species),
))
)().finally(() => return sequelize.close() )
package.json
"name": "tmp",
"private": true,
"version": "1.0.0",
"dependencies":
"pg": "8.5.1",
"pg-hstore": "2.3.3",
"sequelize": "6.5.1",
"sqlite3": "5.0.2"
运行:
npm install
./main.js p
./main.js
PotsgreSQL 结果符合预期:
bat
[
name: 'aquatic', animals: [] ,
name: 'flying', animals: [ 'bat' ] ,
name: 'mammal', animals: [ 'bat' ]
]
ant
[
name: 'aquatic', animals: [] ,
name: 'flying', animals: [] ,
name: 'mammal', animals: []
]
dog
[
name: 'aquatic', animals: [] ,
name: 'flying', animals: [] ,
name: 'mammal', animals: [ 'dog' ]
]
但是 SQLite 的结果完全是错误的:
bat
[
name: 'aquatic', animals: [] ,
name: 'flying', animals: [ 'bat' ] ,
name: 'mammal', animals: [ 'bat', null ]
]
ant
[
name: 'aquatic', animals: [] ,
name: 'flying', animals: [] ,
name: 'mammal', animals: []
]
dog
[
name: 'aquatic', animals: [] ,
name: 'flying', animals: [] ,
name: 'mammal', animals: []
]
我不太介意null
,我可以解决这个问题。
但是dog
不是mammal
,我无法解决。
基础查询
我们可以查看底层查询来猜测发生了什么。
PostgreSQL 有以下类型的查询:
SELECT
"Tag"."name",
"Animals"."species" AS "Animals.species",
FROM
"Tags" AS "Tag"
LEFT OUTER JOIN (
"AnimalTag" AS "Animals->AnimalTag"
INNER JOIN "Animals" AS "Animals" ON "Animals"."id" = "Animals->AnimalTag"."AnimalId"
) ON "Tag"."id" = "Animals->AnimalTag"."TagId"
AND "Animals"."id" = 1
ORDER BY
"Tag"."name" ASC;
SQLite 具有以下类型的查询:
SELECT
`Tag`.`name`,
`Animals`.`species` AS `Animals.species`
FROM `Tags` AS `Tag`
LEFT OUTER JOIN `AnimalTag` AS `Animals->AnimalTag`
ON `Tag`.`id` = `Animals->AnimalTag`.`TagId`
LEFT OUTER JOIN `Animals` AS `Animals`
ON `Animals`.`id` = `Animals->AnimalTag`.`AnimalId`
AND `Animals`.`id`= 3
ORDER BY
`Tag`.`name` ASC;
因此,由于它使用子查询的方式,因此只有 PostgreSQL 查询是所需的查询,因此:
ON "Tag"."id" = "Animals->AnimalTag"."TagId"
AND "Animals"."id" = 1
仅应用于外部LEFT OUTER JOIN
。
SQLite 查询产生了不希望的结果,为我们得到输出的每只动物手动运行它:
bat
aquatic|
flying|bat
mammal|bat
mammal|
ant
aquatic|
flying|
mammal|
mammal|
dot
aquatic|
flying|
mammal|
mammal|dog
所以似乎发生的情况是动物的第一个结果为空,然后 sequelize 在返回时忽略它,这可以解释我们看到的原因:
name: 'mammal', animals: [ 'bat', null ]
对于蝙蝠但是:
name: 'mammal', animals: []
给狗。
所需的语句将在第一个 LEFT OUTER JOIN
上包含 AND
:
SELECT
`Tag`.`name`,
`Animals`.`species` AS `Animals.species`
FROM `Tags` AS `Tag`
LEFT OUTER JOIN `AnimalTag` AS `Animals->AnimalTag`
ON `Tag`.`id` = `Animals->AnimalTag`.`TagId`
AND `Animals->AnimalTag`.`AnimalId`= 1
LEFT OUTER JOIN `Animals` AS `Animals`
ON `Animals`.`id` = `Animals->AnimalTag`.`AnimalId`
ORDER BY
`Tag`.`name` ASC;
解决方法:超级多对多
到目前为止,我能想到的最佳解决方法是使用超级多对多,当问题是我们需要更好地控制 ON
条件时,这通常是一个不错的选择:
#!/usr/bin/env node
const assert = require('assert')
const path = require('path')
const DataTypes, Sequelize = require('sequelize')
let sequelize
let logging = process.argv[3] === '1'
if (process.argv[2] === 'p')
sequelize = new Sequelize('tmp', undefined, undefined,
dialect: 'postgres',
host: '/var/run/postgresql',
logging,
)
else
sequelize = new Sequelize(
dialect: 'sqlite',
storage: 'tmp.sqlite',
logging,
)
;(async () =>
const AnimalTag = sequelize.define('AnimalTag')
const Animal = sequelize.define('Animal',
species: type: DataTypes.STRING ,
)
const Tag = sequelize.define('Tag',
name: type: DataTypes.STRING ,
)
AnimalTag.belongsTo(Animal)
Animal.hasMany(AnimalTag)
AnimalTag.belongsTo(Tag)
Tag.hasMany(AnimalTag)
Animal.belongsToMany(Tag, through: AnimalTag )
Tag.belongsToMany(Animal, through: AnimalTag )
await sequelize.sync(force: true)
const animal0 = await Animal.create( species: 'bat' )
const animal1 = await Animal.create( species: 'ant' )
const animal2 = await Animal.create( species: 'dog' )
const tag0 = await Tag.create( name: 'flying' )
const tag1 = await Tag.create( name: 'mammal' )
const tag2 = await Tag.create( name: 'aquatic' )
await animal0.addTag(tag0)
await animal0.addTag(tag1)
await animal2.addTag(tag1)
const animals = [animal0, animal1, animal2]
for (let animal of animals)
let rows
rows = await Tag.findAll(
include: [
model: AnimalTag,
where: AnimalId: animal.id ,
required: false,
include: [
model: Animal,
]
],
order: [['name', 'ASC']],
)
console.error(rows.map(row => return
name: row.name,
animals: row.AnimalTags.map(animalTag => animalTag.Animal.species)
))
console.error();
)().finally(() => return sequelize.close() )
为 PostgreSQL 和 Sequelize 生成正确的输出。生成的查询正是我们想要的:
SELECT
"Tag"."id",
"Tag"."name",
"Tag"."createdAt",
"Tag"."updatedAt",
"AnimalTags"."createdAt" AS "AnimalTags.createdAt",
"AnimalTags"."updatedAt" AS "AnimalTags.updatedAt",
"AnimalTags"."AnimalId" AS "AnimalTags.AnimalId",
"AnimalTags"."TagId" AS "AnimalTags.TagId",
"AnimalTags->Animal"."id" AS "AnimalTags.Animal.id",
"AnimalTags->Animal"."species" AS "AnimalTags.Animal.species",
"AnimalTags->Animal"."createdAt" AS "AnimalTags.Animal.createdAt",
"AnimalTags->Animal"."updatedAt" AS "AnimalTags.Animal.updatedAt"
FROM
"Tags" AS "Tag"
LEFT OUTER JOIN "AnimalTags" AS "AnimalTags" ON "Tag"."id" = "AnimalTags"."TagId"
AND "AnimalTags"."AnimalId" = 3
LEFT OUTER JOIN "Animals" AS "AnimalTags->Animal" ON "AnimalTags"."AnimalId" = "AnimalTags->Animal"."id"
ORDER BY
"Tag"."name" ASC;
在 Ubuntu 21.10、PostgreSQL 13.5 上测试。
【讨论】:
以上是关于nodejs + sequelize :: 在需要的地方包含 false的主要内容,如果未能解决你的问题,请参考以下文章
我需要了解如何防止 NodeJS 上的 sql 注入 sequelize ORM
nodejs利用sequelize-auto 根据数据库的table 生成model