简体   繁体   English

MongoDB和Mongoose-仅查找某些子文档

[英]MongoDB with Mongoose - Find only certain child documents

MongoDB 3.0.7 and Mongoose 4.3.4. MongoDB 3.0.7和Mongoose 4.3.4。

Schema: 架构:

var schema = new mongoose.Schema({
    confirmed: { type: Boolean, default: false },
    moves: [new mongoose.Schema({
        name: { type: String, default: '' },
        live: { type: Boolean, default: true }
    })]
});
mongoose.model('Batches', schema);

Query: 查询:

var Batch = mongoose.model('Batches');
var query = {
    confirmed: true,
    moves: {
        $elemMatch: {
            live: true
        }
    }
};
Batch.find(query).exec(function(err, batches){
    console.log('batches: ', batches);
});

I need to return all batches that are confirmed , and all moves within the returned batches that are live . 我需要返回所有已confirmed批次,以及返回的live批次中的所有移动。

At the moment, the above is returning only the confirmed batches (which is what I want), but all the moves in each returned batch (which is not what I want). 目前,以上仅返回已confirmed批次(这是我想要的),但是每个返回的批次中的所有 moves (这都不是我想要的)。 So the limiting of moves by the live flag is not working. 因此,通过live标志限制移动不起作用。

How do I limit the sub-documents that are returned..? 如何限制返回的子文档?

Ideally, I would like to keep everything that controls the data returned within query passed to find , and not have to call more methods on Batch . 理想情况下,我想保留所有控制传递给find query返回的数据的控件,而不必在Batch上调用更多方法。

For Mongoose versions >=4.3.0 which support MongoDB Server 3.2.x , you can use the $filter operator with the aggregation framework to limit/select the subset of the moves array to return based on the specified condition. 对于支持MongoDB Server 3.2.x Mongoose版本>=4.3.0 ,可以将$filter运算符与聚合框架一起使用,以根据指定条件限制/选择要返回的moves数组的子集。 This returns an array with only those elements that match the condition, so you will use it in the $project stage to modify the moves array based on the filter above. 这将返回仅包含与条件匹配的那些元素的数组,因此您将在$project阶段中使用它来基于上面的过滤器修改moves数组。

The following example shows how you can go about this: 以下示例显示了如何执行此操作:

var Batch = mongoose.model('Batches'),
    pipeline = [
        {
            "$match": { "confirmed": true, "moves.live": true }
        },
        { 
            "$project": {
                "confirmed": 1,
                "moves": {
                    "$filter": {
                         "input": "$moves",
                         "as": "el",
                         "cond": { "$eq": [ "$$el.live", true ] }
                     }
                }
            }
        }
    ];

Batch.aggregate(pipeline).exec(function(err, batches){
    console.log('batches: ', batches);
});

or with the fluent aggregate() API pipeline builder: 或使用流畅的aggregate() API管道构建器:

Batch.aggregate()
     .match({
         "$match": { "confirmed": true, "moves.live": true }
     })
     .project({
         "confirmed": 1,
         "moves": {
             "$filter": {
                  "input": "$moves",
                  "as": "el",
                  "cond": { "$eq": [ "$$el.live", true ] }
             }
         }
     })
     .exec(function(err, batches){
        console.log('batches: ', batches);
     });

For Mongoose versions ~3.8.8, ~3.8.22, 4.x which support MongoDB Server >=2.6.x , you could filter out the false values using a combination of the $map and $setDifference operators: 对于支持MongoDB Server >=2.6.x Mongoose版本~3.8.8, ~3.8.22, 4.x您可以使用$map$setDifference运算符的组合来滤除错误值:

var Batch = mongoose.model('Batches'),
    pipeline = [
        {
            "$match": { "confirmed": true, "moves.live": true }
        },
        { 
            "$project": {
                "confirmed": 1,
                "moves": {
                    "$setDifference": [
                         {
                             "$map": {
                                 "input": "$moves",
                                 "as": "el",
                                 "in": {
                                     "$cond": [
                                         { "$eq": [ "$$el.live", true ] },
                                         "$$el",
                                         false
                                     ]
                                 }
                             }
                         },
                         [false]
                     ]
                 }
            }
        }
    ];

Batch.aggregate(pipeline).exec(function(err, batches){
    console.log('batches: ', batches);
});

You can use the aggregation framework's $unwind method to split them into separate documents, here are sample codes. 您可以使用聚合框架的$unwind方法将它们拆分为单独的文档,这里是示例代码。

Batches.aggregate(
    { $match: {'confirmed': true, 'moves.live': true}}, 
    {$unwind: '$moves'},
    {$project: {
            confirmed: 1
            name: '$moves.name',
            live:'$moves.live'
        }
    }, function(err, ret){
})

The query does not limit moves by the live flag. 该查询不通过实时标志来限制移动。 The query reads: find all confirmed batches with at least one live move . 该查询显示: 查找所有已确认批次且至少进行一次实时移动

There are 2 options to retrieve live moves only: retrieve all moves, and filter the array clientside; 有2个选项只能检索实时移动:检索所有移动,并过滤客户端数组; or map-reduce it serverside - unwind all moves, filter live ones, and group by document id. 或在服务器端缩小地图大小 -展开所有移动,过滤实时移动并按文档ID分组。

The former is simpler to implement, but will result with more data transfer, cpu and memory consumption on the client side. 前者易于实现,但会导致客户端的数据传输,CPU和内存消耗更多。 The later is more efficient, but a bit more complex to implement - if you expect more than 16Mb in the response, you will need to use a temporary collection. 后者效率更高,但实现起来却有些复杂-如果您希望响应中的数据超过16Mb,则需要使用一个临时集合。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM