2

我想创建一个聚合管道,其中一个匹配项使用来自其他集合(内部查询)的结果。这是一个例子:

db.users.aggregate([
    {"$match":{"id":{"$in":["0","1","2"]},"p": {$in: db.groups.distinct("p", {"enable":true)}}},
    {"$group":{"_id":"$v","number":{"$sum":1}}}
])

我需要从javascript进行查询。实际上,该应用程序是带有 mongoosenodejs

不幸的是,当猫鼬执行查询时,我得到:

MongoError: $in needs an array

这是猫鼬打印的查询:

Mongoose: users.aggregate([
    {"$match":{"id":{"$in":["0","1","2"]},"p": {$in: 'db.groups.distinct("p", {"enable":true)'}},
    {"$group":{"_id":"$v","number":{"$sum":1}}}
])

谁能帮助我如何从 javascript 传递内部查询?

更新: 集合是分片的,所以我不能使用$lookup就是我想将 $in 与 distinct 一起使用的原因

4

5 回答 5

2

TLDR;

mongoose 等价物是首先运行嵌套查询,然后将结果传递给聚合。

groups.distinct("p", {"enable": true}).exec().then(matchingGroups => {
    return users.aggregate([
        {$match: {"id": {$in: ["0", "1", "2"]}, p: {$in: matchingGroups}}},
        {$group:{_id:"$v", number:{$sum:1 }}}
    ]).exec();
}).then(aggregationResult => {
    console.log(aggregationResult);
});

解释

在 mongo shell 中执行以下脚本时,首先提交内部查询(distinct),然后将结果传递给外部查询(聚合),然后提交执行。这可以通过捕获数据包跟踪来确认。在附图中,我们可以看到第一个查询提交(数据包 9)收到的响应(数据包 10 和 11),以及聚合查询提交(数据包 12 和 13)。

于 2020-06-26T23:05:12.970 回答
0

当您使用猫鼬时,您可以从一个查询中获得响应,然后在其中运行另一个查询。

示例代码:

 router.route("/update-payment-amount")
        .get((req, res) => {
            Order.find({
                "$and": [
                    { "order_no": 100413 }
                ]
            })
                .then(orders => {
                    return updateCollectedAmount(orders);
                })
                .then(result => {
                    res.json(result)
                })
        })

    function updateCollectedAmount(orders) {
        let updates = orders.map(order => {
            return new Promise((resolve, reject) => {
                let totalPaid = order.payment_collection.total_paid;

                Order.update({
                    _id: mongoose.Types.ObjectId(order._id)
                }, {
                    $set: {
                        "payment_collection.collection_info.0.collected_amount": totalPaid
                    }
                }, {
                    multi: true
                })
                    .exec()
                    .then(status => {
                        resolve(status)
                    })
                    .catch(err => {
                        //console.log(err)
                        reject(err)
                    })
            })
        })
        return Promise.all(updates)
    }
于 2020-06-27T08:41:05.057 回答
0

使用async/await您的原始 MongoDB shell 代码只需很少的代码更改即可获得相同的结果。

const result = await users.aggregate([
    {"$match":{"id":{"$in":["0","1","2"]},"p": {$in: await groups.distinct("p", {"enable":true)}}},
    {"$group":{"_id":"$v","number":{"$sum":1}}}
])

这相当于先执行“内部”查询并将其传递给“外部”查询

const distinctP = await groups.distinct("p", { "enable": true })
const result = await users.aggregate([
  {
    "$match": {
      "id": { "$in": ["0","1","2"] },
      "p": { $in: distinctP }
    }
  },
  {
    "$group": { "_id": "$v", "number": { "$sum": 1 } }
  }
])
于 2020-06-27T19:18:15.080 回答
0

您可以先执行内部查询并在聚合中使用结果。

也可以在聚合使用$lookup阶段直接执行此操作。这是文档https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#pipe._S_lookup

结果看起来像

Mongoose: users.aggregate([{
$lookup:
     {
       from: "groups",
       localField: "p",
       foreignField: "p",
       as: "p_group"
     }
},
{"$match":{"id":{"$in":["0","1","2"]},"p_group.enable": true},
{"$group":{"_id":"$v","number":{"$sum":1}}}
]);

您可以 $lookup 根据确定的字段将文档附加到每个用户。

// in user collection of database
{ id: '0', p: 'somevalue'} 
// in group collection of data
{ p: 'somevalue', enable: true }
// After a lookup
{ id: '0', p: 'somevalue', asLookupField: { p: 'somevalue', enable: true }} 

然后你匹配和分组。

请注意,也可以在查找之前与部分查询匹配,以减少查找阶段的数量。

于 2020-06-26T20:20:36.093 回答
0

不确定这是否只是一个错字,但您缺少括号{"enable":true)::

更正:

db.users.aggregate([
    {"$match":{"id":{"$in":["0","1","2"]},"p": {$in: db.groups.distinct("p", {"enable":true})}}},
    {"$group":{"_id":"$v","number":{"$sum":1}}}
])

我如何将其转换为猫鼬:

    const docs = await db.collection("users").aggregate([{
            $match: {
                id: {
                    $in: ["0", "1", "2"]
                },
                p: {
                    $in: []
                }
            }
        }, {
            $group: {
                _id: "$v",
                number: {
                    $sum: 1
                }
            }
        }])
        .toArray();

    console.log(docs.map(it => it._id));
于 2020-07-03T04:13:52.660 回答