[英]MongoDB Aggregation - match documents with array of objects, by another array of objects filter
我有由對象數組組成的文檔,並且該數組中的每個對象都由另一個對象數組組成。
為簡單起見,省略了文檔中不相關的字段。
它看起來像這樣(2 個文檔):
{
title: 'abc',
parts: [
{
part: "verse",
progressions: [
{
progression: "62a4a87da7fdbdabf787e47f",
key: "Ab",
_id: "62b5aaa0c9e9fe8a7d7240d3"
},
{
progression: "62adf477ed11cbbe156d5769",
key: "C",
_id: "62b5aaa0c9e9fe8a7d7240d3"
},
],
_id: "62b5aaa0c9e9fe8a7d7240d2"
},
{
part: "chorus",
progressions: [
{
progression: "62a4a51b4693c43dce9be09c",
key: "E",
_id: "62b5aaa0c9e9fe8a7d7240d9"
}
],
_id: "62b5aaa0c9e9fe8a7d7240d8"
}
],
}
{
title: 'def',
parts: [
{
part: "verse",
progressions: [
{
progression: "33a4a87da7fopvvbf787erwe",
key: "E",
_id: "62b5aaa0c9e9fe8a7d7240d3"
},
{
progression: "98opf477ewfscbbe156d5442",
key: "Bb",
_id: "62b5aaa0c9e9fe8a7d7240d3"
},
],
_id: "12r3aaa0c4r5me8a7d72oi8u"
},
{
part: "bridge",
progressions: [
{
progression: "62a4a51b4693c43dce9be09c",
key: "C#",
_id: "62b5aaa0c9e9fe8a7d7240d9"
}
],
_id: "62b5aaa0rwfvse8a7d7240d8"
}
],
}
客戶端隨請求發送的參數是一個對象數組:
[
{ part: 'verse', progressions: ['62a4a87da7fdbdabf787e47f', '62a4a51b4693c43dce9be09c'] },
{ part: 'chorus', progressions: ['62adf477ed11cbbe156d5769'] }
]
我想通過 mongodb 聚合檢索上面輸入數組中至少一個對象與它們匹配的文檔:
在此示例中,文檔在其parts 數組字段中具有在part 屬性中具有值“verse”的對象,並且在其中一個對象的progress 屬性中具有進程id 的['62a4a87da7fdbdabf787e47f', '62a4a51b4693c43dce9be09c'] 之一在progresss 屬性中,或者在其parts 數組字段中具有值的文檔中,在part 屬性中具有值“chorus”的對象和progress 中的一個對象的progress 屬性中的progresss id 之一['62adf477ed11cbbe156d5769']級數屬性。
在這個例子中,匹配的文檔是第一個(標題為'abc'),但在實際使用中,匹配的文檔可能很多。
我嘗試自己創建一個聚合管道(使用 mongoose 'aggregate' 方法):
// parsedProgressions = [
// { part: 'verse', progressions: ['62a4a87da7fdbdabf787e47f', '62a4a51b4693c43dce9be09c'] },
// { part: 'chorus', progressions: ['62adf477ed11cbbe156d5769'] }
// ]
songs.aggregate([
{
$addFields: {
"tempMapResults": {
$map: {
input: parsedProgressions,
as: "parsedProgression",
in: {
$cond: {
if: { parts: { $elemMatch: { part: "$$parsedProgression.part", "progressions.progression": mongoose.Types.ObjectId("$$parsedProgression.progression") } } },
then: true, else: false
}
}
}
}
}
},
{
$addFields: {
"isMatched": { $anyElementTrue: ["$tempMapResults"] }
}
},
{ $match: { isMatched: true } },
{ $project: { title: 1, "parts.part": 1, "parts.progressions.progression": 1 } }
]);
但它不起作用 - 據我了解,因為 $elemMatch 只能在 $match 階段使用。
無論如何,我想我把聚合管道過度復雜化了,所以如果你能修復我的聚合管道/提供一個更好的工作管道,我會很高興。
這不是一個簡單的情況,因為它們都是嵌套數組,我們需要匹配part
和progressions
,它們不在同一級別
一個選項看起來有點復雜,但可以讓你的數據變小:
$set
一個名為matchCond
的新數組字段,其中包括一個名為progs
的數組,其中包含parts.progressions
。 向其中的每個子對象插入匹配的progressions
輸入數組。 我們確實需要在這里小心並處理沒有匹配的progressions
輸入數組progressions
輸入數組的情況,因為這是第二個文檔中的“橋”部分的情況。progs
項目中的任何一個, progression
字段是否與input
數組中的一個選項匹配。 這是使用$filter
和$redice
來完成結果的數量。db.collection.aggregate([
{
$set: {
matchCond: {
$map: {
input: "$parts",
as: "parts",
in: {progs: {
$map: {
input: "$$parts.progressions",
in: {$mergeObjects: [
"$$this",
{input: {progressions: []}},
{input: {$first: {
$filter: {
input: inputData,
as: "inputPart",
cond: {$eq: ["$$inputPart.part", "$$parts.part"]}
}
}}}
]}
}
}}
}
}
}
},
{$set: {
matchCond: {
$reduce: {
input: "$matchCond",
initialValue: 0,
in: {$add: [
"$$value",
{$size: {
$filter: {
input: "$$this.progs",
as: "part",
cond: {$in: ["$$part.progression", "$$part.input.progressions"]}
}
}
}
]
}
}
}
}
},
{$match: {matchCond: {$gt: 0}}},
{$project: {title: 1, parts: 1}}
])
看看它在操場上的例子是如何工作的
另一種選擇是使用$unwind
,它看起來很簡單,但會復制您的數據,因此可能會更慢:
db.collection.aggregate([
{$addFields: {inputData: inputData, cond: "$parts"}},
{$unwind: "$cond"},
{$unwind: "$cond.progressions"},
{$unwind: "$inputData"},
{$match: {
$expr: {
$and: [
{$eq: ["$cond.part", "$inputData.part"]},
{$in: ["$cond.progressions.progression", "$inputData.progressions"]}
]
}
}
},
{$project: {title: 1, parts: 1}}
])
這兩者之間有幾種選擇...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.