提问人:xavier.seignard 提问时间:3/26/2020 最后编辑:xavier.seignard 更新时间:2/21/2021 访问量:2805
在 include 的 include 上使用 where 子句对查询进行续集
Sequelize query with a where clause on an include of an include
问:
我正在努力使用 sequelize 创建查询。
一些背景
我有以下型号:
- A 可以有 [0..n]
Manifestation
Event
- an 属于 1(an 不能没有
Event
Manifestation
Event
Manifestation
) - A 可以有 [0..n]
Place
Event
- an 属于 1(an 不能没有
Event
Place
Event
Place
) - A 可以有 [1..n]
Manifestation
Place
- A 可以有 [0..n]
Place
Manifestation
我按如下方式对关系进行建模:
Manifestation.hasMany(Event, { onDelete: 'CASCADE', hooks: true })
Event.belongsTo(Manifestation)
Place.hasMany(Event, { onDelete: 'CASCADE', hooks: true })
Event.belongsTo(Place)
Manifestation.belongsToMany(Place, { through: 'manifestation_place' })
Place.belongsToMany(Manifestation, { through: 'manifestation_place' })
对我来说,这似乎是正确的,但如果您有评论,请不要犹豫。
问题
我正在尝试查询以获取所有并在给定中发生的情况。但是对于那些,我想将它们包含在他们的中,即使给定的.Place
Manifestation
Event
Place
Event
Manifestation
Manifestation
Place
下面是我尝试实现的“JSON”结构:
{
id: 1,
name: "Place Name",
address: "Place address",
latitude: 47.00000,
longitude: -1.540000,
manifestations: [
{
id: 10,
title: "Manifestation one",
placeId: 1,
events: []
},
{
id: 11,
title: "Manifestation two",
placeId: 3,
events: [
id: 5,
title: "3333",
manifestationId: 11,
placeId: 1
]
}
]
}
所以我想包括 with : 11,因为它之一出现在给定的 (with : 1) 中Manifestation
id
Event
Place
id
更新(04/06/20):现在我依靠javascript来获得预期的结果
我发现如果我在询问之前发布我当前的解决方案会很好。
router.get('/test', async (req, res) => {
try {
const placesPromise = place.findAll()
const manifestationsPromise = manifestation.findAll({
include: [
{ model: event },
{
model: place,
attributes: ['id'],
},
],
})
const [places, untransformedManifestations] = await Promise.all([
placesPromise,
manifestationsPromise,
])
const manifestations = untransformedManifestations.map(m => {
const values = m.toJSON()
const places = values.places.map(p => p.id)
return { ...values, places }
})
const result = places
.map(p => {
const values = p.toJSON()
const relatedManifestations = manifestations
.filter(m => {
const eventsPlaceId = m.events.map(e => e.placeId)
return (
m.places.includes(values.id) ||
eventsPlaceId.includes(values.id)
)
})
.map(m => {
const filteredEvents = m.events.filter(
e => e.placeId === values.id
)
return { ...m, events: filteredEvents }
})
return { ...values, manifestations: relatedManifestations }
})
.filter(p => p.manifestations.length)
return res.status(200).json(result)
} catch (err) {
console.log(err)
return res.status(500).send()
}
})
但我很确定我可以直接用续集来做到这一点。有什么想法或建议吗?
谢谢
答:
这不是最佳选择。但你可以尝试一下:
const findPlace = (id) => {
return new Promise(resolve => {
db.Place.findOne({
where: {
id: id
}
}).then(place => {
db.Manefestation.findAll({
include: [{
model: db.Event,
where: {
placeId: id
}
}]
}).then(manifestations => {
const out = Object.assign({}, {
id: place.id,
name: place.name,
address: place.address,
latitude: place.latitude,
longitude: place.longitude,
manifestations: manifestations.reduce((res, manifestation) => {
if (manifestation.placeId === place.id || manifestation.Event.length > 0) {
res.push({
id: manifestation.id,
title: manifestation.id,
placeId: manifestation.placeId,
events: manifestation.Event
})
}
return res;
}, [])
})
})
resolve(out);
})
})
}
从中,您可以获得分配给放置的所有表现形式,或者具有分配的任何事件。Manefestations 中包含的所有事件都分配给该地点。
编辑:您也可以使用以下方法:
const findPlace = (id) => {
return new Promise(resolve => {
db.Place.findOne({
include: [{
model: db.Manefestation,
include: [{
model: db.Event,
where: {
placeId: id
}
}]
}],
where: {
id: id
}
}).then(place => {
db.Manefestation.findAll({
include: [{
model: db.Event,
where: {
placeId: id
}
}],
where: {
placeId: {
$not: id
}
}
}).then(manifestations => {
place.Manefestation = place.Manefestation.concat(manifestations.filter(m=>m.Event.length>0))
resolve(place);// or you can rename, reassign keys here
})
})
})
}
在这里,我只在第一个查询中直接表现。然后,不包括和串联的表现形式。
评论
我不知道你现在是否想通了。但解决方案如下。
使用 Sequelize 进行搜索可能会:)有趣。您必须包含在另一个包含中。如果查询速度变慢,请使用 .separate:true
Place.findAll({
include: [
{
model: Manifestation,
attributes: ['id'],
include: [{
model: Event ,
attributes: ['id']
}]
},
],
})
我试图在单个查询中完成它,但您仍然需要 JavaScript 才能获得所需的输出类型。
(注意:💡您需要与地点无关的表现形式,但如果该地点存在事件,则应包括该表现形式。唯一的 SQL 方法首先在所有表之间执行 CROSS JOIN,然后过滤掉结果,这将是一个非常繁重的查询)
我想出了这个代码(尝试并执行),它不需要您执行 2 findAll 来获取您当前使用的所有数据。相反,它只获取 1 个查询中最终输出所需的数据。
const places = await Place.findAll({
include: [{
model: Manifestation,
// attributes: ['id']
through: {
attributes: [], // this helps not get keys/data of join table
},
}, {
model: Event,
include: [{
model: Manifestation,
// attributes: ['id']
}],
}
],
});
console.log('original output places:', JSON.stringify(places, null, 2));
const result = places.map(p => {
// destructuring to separate out place, manifestation, event object keys
const {
manifestations,
events,
...placeData
} = p.toJSON();
// building modified manifestation with events array
const _manifestations = manifestations.map(m => {
return ({ ...m, events: [] })
});
// going through places->events to push them to respective manifestation events array
// + add manifestation which is not directly associated to place but event is of that manifestation
events.map(e => {
const {
manifestation: e_manifestation, // renaming variable
...eventData
} = e;
const mIndex = _manifestations.findIndex(m1 => m1.id === e.manifestationId)
if (mIndex === -1) { // if manifestation not found add it with the events array
_manifestations.push({ ...e_manifestation, events: [eventData] });
} else { // if found push it into events array
_manifestations[mIndex].events.push(eventData);
}
});
// returning a place object with manifestations array that contains events array
return ({ ...placeData, manifestations: _manifestations });
})
// filter `.filter(p => p.manifestations.length)` as used in your question
console.log('modified places', JSON.stringify(result, null, 2));
上一个:插入行(如果不存在而没有死锁)
评论