Mongoose - 如何按属性值查询子文档

Pat*_*Pat 5 mongoose mongodb node.js express

我正在尝试获取一系列未付款订单。订单子文档有一个属性isPaid,定义订单是否已付款。

在我看来,我只想显示尚未付款的订单。

这是我的架构:

var order = new Schema({
   content: [{
        product: {type: String, required: true},
        quantity: {type: Number},
        vatRate: {type: Number, required: true},
        price: {type: Number}
    }],
    isPaid: {type: Boolean, default: false},
    number: {type: Number}
});

var clientSchema = new Schema({
[...]
information: {
    code: {type: String, required: true},
    name: {type: String, required: true}
},
orders: [order],
[...]
});
Run Code Online (Sandbox Code Playgroud)

我开始了,但没有成功

clientModel.find(
    {
       "information.code": clientCode, 
       "orders.isPaid": false
    }, function (err, client) { ... });
Run Code Online (Sandbox Code Playgroud)

然后,我做了很多尝试,有成功$all$elemMatch也没有成功。大多数时候,它会返回所有已付款或未付款的订单。我不知道为什么。我需要一些帮助,请:)

chr*_*dam 3

您可以采取的一种方法是使用聚合框架来获取所需的数组。考虑以下管道,它首先使用$match运算符来过滤将进入下一阶段(步骤)的文档$project。这只产生所需的订单数组,该数组将使用子文档上的$filter比较运算符进行过滤,并且应用过滤器的条件使用。$eqisPaid

最终管道将如下所示:

const pipeline = [
    { '$match': {
        'information.code': clientCode, 
        'orders.isPaid': false
    } },
    { '$project': {
        'orders': {
            '$filter': {
                'input': '$orders',
                'cond': {
                    '$eq': ['$$this.isPaid', false]
                }
            }
        }
    } }
]
Run Code Online (Sandbox Code Playgroud)

或者如果 MongoDB 服务器版本不支持$filter(较旧的驱动程序),则初始匹配后的下一步将是 $unwind.

此步骤从输入文档解构订单数组字段,以输出每个元素的文档。每个输出文档都是输入文档,其中数组字段的值被元素替换。

下一步使用$match运算符对解构的子文档进行进一步过滤,然后将其$group按标识符_id表达式进行分组(使用 ),并将累加器表达式$push(在订单子文档上)应用于返回所需数组的每个组。

const pipeline = [
    { '$match': {
        'information.code': clientCode, 
        'orders.isPaid': false
    } },
    { '$unwind': '$orders' },
    { '$match': {       
        'orders.isPaid': false
    } },
    { '$group': {
        '_id': '$_id',
        'orders': {
            '$push': '$orders'
        }
    } }
]

clientModel.aggregate(pipeline).exec(function (err, res){
    if (err) return handleError(err);
    console.log(res); // [ { orders: [...] } ]
});
Run Code Online (Sandbox Code Playgroud)

或者使用聚合管道构建器

clientModel.aggregate()
    .match({'information.code': clientCode, 'orders.isPaid': false})
    .project({
        'orders': {
            '$filter': {
                'input': '$orders',
                'cond': {
                    '$eq': ['$$this.isPaid', false]
                }
            }
        }
    })
    .exec(function (err, res) {
        if (err) return handleError(err);
        console.log(res); // [ { orders: [...] } ]
    });
Run Code Online (Sandbox Code Playgroud)

或者对于旧版本

clientModel.aggregate()
    .match({'information.code': clientCode, 'orders.isPaid': false})
    .unwind('orders')
    .match({'orders.isPaid': false })
    .group({'_id': '$_id', 'orders': { '$push': '$orders' } })
    .select('-_id orders')
    .exec(function (err, res) {
        if (err) return handleError(err);
        console.log(res); // [ { orders: [...] } ]
    });
Run Code Online (Sandbox Code Playgroud)