如果我正确理解了您的问题,您有一个输入对象,其中可能包含 一些 主文档的 params 对象中的字段,按 any 顺序排列,用于示例:
{
device: "windows",
gender: "m"
}
{
gender: "m",
device: "windows"
}
{
device: "windows",
age: 28
}
并且您只想匹配所有输入对象中的字段存在于主文档中:
{
device: "linux", // NO MATCH
gender: "m"
}
{
gender: "m", // MATCH
device: "windows"
}
{
device: "windows", // NO MATCH
age: 29
}
正确吗?
选项 1
您的 param 对象是否始终只包含这三个字段(device、gender 和 age)?
如果是这样,您可以在根级别手动投影它们中的每一个,进行匹配,然后再次“取消投影”它们:
db.my_collection.aggregate([
{
$project: {
name: 1,
params: 1,
device: "$params.device", // add these three for your match stage
gender: "$params.gender",
age: "$params.age"
}
},
{
$match: input_params
},
{
$project: {name: 1, params: 1} // back to original
}
]);
但我假设您想对 params 对象内的任意数量的字段执行此操作。
选项 2
你有办法操纵输入对象吗?如果是这样,您可以在所有字段前加上“params.”:
let input_params =
{
device: "windows",
gender: "m"
};
let new_input_params =
{
"params.device": "windows",
"params.gender": "m"
};
那么你的查询就是:
db.my_collection.find(new_input_params);
选项 3
如果您无法修改输入,您可以使用 $replaceRoot 聚合运算符(从 3.4 版开始,归功于 this answer)将您的 params 展平为根文档。由于这会将根文档替换为嵌入的文档,因此您需要先提取您感兴趣的字段,至少是 _id 字段:
db.my_collection.aggregate([
{
$addFields: {"params.id": "$_id"} // save _id inside the params doc,
// as you will lose the original one
},
{
$replaceRoot: {newRoot: "$params"}
},
{
$match: input_params
},
...
]);
这将匹配文档并保留_id 字段,您可以使用该字段再次获取文档的其余部分,例如通过$lookup:
...
{
$lookup: {
from: "my_collection",
localField: "id",
foreignField: "_id",
as: "doc"
}
},
...
这将使您的文档采用以下格式:
{
"device" : "windows",
"gender" : "m",
"age" : 28,
"id" : ObjectId("XXX"),
"doc" : [
{
"_id" : ObjectId("XXX"),
"name" : "Test",
"params" : {
"device" : "windows",
"gender" : "m",
"age" : 28
}
}
]
}
要完整循环并恢复原始文档格式,您可以:
...
{
$unwind: "$doc" // get rid of the [] around the "doc" object
// ($lookup always results in array)
},
{
$replaceRoot: {newRoot: "$doc"} // get your "doc" back to the root
}
...
在我写这篇文章时,我不敢相信单独使用 MongoDB 没有更清洁的方法,但我想不出任何方法。
我希望这会有所帮助!