我们在 mongo 中有一些文档,本质上是保存的过滤器,由
name
字符串和 query
JSON 以及一些其他属性组成,例如:
{
name: "myFilter",
query: { prop1: "val1", prop2: "val2" },
groupId: 'xxxyyy'
}
我想要做的是拥有一个应用每个过滤器查询的聚合,这样我们就可以找到哪些过滤器具有匹配项,而无需单独获取和执行每个过滤器。
我得到的最接近的是:
$lookup {
from: "users",
localField: "groupId",
foreignField: "groupId",
as: "users",
let: {
q: "$query",
},
pipeline: [
{ $match: { $expr: "$$q" } },
}
问题是,这每次都会匹配所有用户,我认为是因为
$$q
被解释为 json blob,而 $expr
将其视为真实的,而不将其解析为查询的一部分。如果将 "$$q"
替换为 {foo: "bar"}
、"foo"
或 true
,我会得到相同的结果。
我需要的是一个
exec()
函数的等价物,它将使 mongo 实际上处理这个 JSON,就像它已被输入到管道定义中一样。
认识到这可能存在安全问题,有人知道这是否可能吗?
编辑,添加示例文档和预期输出。
首先,我忽略了在上面的 Filter 示例中包含
groupId
属性,现在我已经更正了。 groupId 只是将用户分成大群组的一种方法,然后我们使用过滤器对其进行细分。
这是一个完整的示例:
过滤器:
[
{
name: "malesOver20",
query: { gender: "M", age: { $gte: 21 } },
groupId: "xxxyyy"
},
{
name: "allUnder21",
query: { age: { $lt: 21 } },
groupId: "xxxyyy"
}
]
用户:
[
{
name: "Joe Schmoe",
gender: "M",
age: 30,
groupId: 'xxxyyy'
},
{
name: "Kerry Berry",
gender: "F",
age: 19,
groupId: 'xxxyyy'
},
{
name: "Sanjay Manjay",
gender: "M",
age: 21,
groupId: 'xxxyyy'
}
{
name: "Jimmy Pimmy",
gender: "M",
age: 8,
groupId: 'aaabbb'
}
]
通过将这样的过滤器应用到过滤器集合...
[
{
$lookup: {
from: "users",
localField: "groupId",
foreignField: "groupId",
as: "users",
let: {
q: "$query",
},
pipeline: [
{ $match: { $expr: "$$q" } },
]
}
}
]
我正在尝试得到这样的结果:
[
{
name: "malesOver20",
query: { gender: "M", age: { $gte: 21 } },
groupId: "xxxyyy",
users:
[
{ name: "Joe Schmoe", gender: "M", age: 30, groupId: "xxxyyy" },
{ name: "Sanjay Manjay", gender: "M", age: 21, groupId: "xxxyyy" },
],
},
{
name: "allUnder21",
query: { age: { $lt: 21 } },
groupId: "xxxyyy",
users: [{ name: "Kerry Berry", gender: "F", age: 19, groupId: "xxxyyy" }],
},
]
通常,该管道中会应用更多过滤器,但这是我们陷入困境的部分。我希望这有助于澄清事情!
您可以通过使用 $facet 在 2 个查询中实现此变体。尝试将这段代码转换为您选择的语言。
const filters = db.filters.find();
let facetStage = { $facet: {} };
for (let i = 0; i < filters.length; i++) {
facetStage['$facet'][filters[i].name] = [{ $match: filters[i].query }, { $match: { groupId: filters[i].groupId } }]; // Add more stages here
}
const pipeline = [facetStage];
const users = db.users.aggregate(pipeline);
在这段代码中,我们首先找到所有的过滤器。然后我们循环它们以创建一个
$facet
阶段。在此阶段,我们存储用户,在不同的键(过滤器名称本身)下匹配不同的过滤器。然后我们在 users
集合上运行此管道。
这将为您提供以下格式的输出:
{
malesOver20: [
{ name: "Joe Schmoe", gender: "M", age: 30, groupId: "xxxyyy" },
{ name: "Sanjay Manjay", gender: "M", age: 21, groupId: "xxxyyy" },
],
allOver21: [{ name: "Kerry Berry", gender: "F", age: 19, groupId: "xxxyyy" }]
}