Mongoose Virtuals in MongoDB Aggregate

时间:2015-05-04 19:55:46

标签: node.js mongodb mongoose aggregation-framework

My Mongoose Schema is as follows:

var DSchema = new mongoose.Schema({
  original_y: {type: Number},,
  new_y: {type: Number},,
  date: {type: Date},
  dummy: [dummyEmbeddedDocuments]
  }, toObject: { virtuals: true }, toJSON: { virtuals: true}
});

DSchema.virtual('dateformatted').get(function () {
 return moment(this.date).format('YYYY-MM-DD HH:mm:ss');
});

module.exports = mongoose.model('D', DSchema);

A document in my schema would be the following:

{
 id:1,
 original_y: 200,
 new_y: 140,
 date: 2015-05-03 00:00:00.000-18:30,
 dummy: [
  {id:1, storage:2, cost: 10},
  {id:2, storage:0, cost: 20},
  {id:3, storage:5, cost: 30},
  ]
}

My Query:

Item.aggregate([
{ 
    "$match": {
        "dummy.storage": {"$gt": 0}
    } 
},
{ 
    "$unwind": "$dummy"
},
{
    "$project": {
        "original_y": 1, 
        "new_y": 1,
        "dateformatted": 1,
        "dummy.id": "$dummy.id",
        "dummy.storage": "$dummy.storage",
        "dummy.cost": "$dummy.cost",
        "dummy.tallyAmount": {
            "$divide": [
                { "$add": ["$new_y","$original_y"] },
                "$dummy.cost"
            ]
        }
    }
},
{
    "$group": {
        "_id": "_$id",
        "original_y": { "$first": "$original_y" },
        "dateformatted": { "$first": "$dateformatted" },
        "new_y": { "$first": "$new_y" },
        "dummy": {
            "$addToSet": "$dummy"
        }
    }        
}
]).exec(callback);

This query however returns the VIRTUAL dateformatted attribute as NULL. Any thoughts as to why this is happening?

3 个答案:

答案 0 :(得分:3)

the docs中的几个笔记触及了为什么会这样:

  
      
  • 参数不会转换为模型的模式,因为$project运算符允许在任何阶段重新定义文档的“形状”   管道,可能会以不兼容的格式留下文件。
  •   
  • 返回的文件是普通的javascript对象,而不是猫鼬文件(因为可以返回任何形状的文档)。
  •   

但它超越了这一点,因为aggregate操作是在服务器端执行的,其中任何客户端Mongoose概念(如虚拟)都不存在。

结果是,您需要在date$project阶段加入$group字段,并在代码中将自己的dateformatted字段添加到结果中关于date值。

答案 1 :(得分:1)

这是一个古老的问题,但是我想出了一个有用的技巧来找回虚拟机,并认为这可能对那些搜索此问题的人有用。

您可以轻松地将对象转换回猫鼬模型:

documents = documents.map(d => {
  return new Document(d);
});

var virtual = documents[0].virtualProperty;

答案 2 :(得分:0)

<field>: <1 or true>表单用于包括现有字段,此处不是这种情况,因为dateformatted字段不存在,您必须使用表达式$dateToString来创建它可以使用:

"$project": {
  "original_y": 1, 
  "new_y": 1,
  "dateformatted": { "$dateToString": { "format": "%Y-%m-%d %H:%M:%S", "date": "$date" } },
  ...

另一种选择是将其与$addFields一起使用:

{
  "$project": {
    ...
  }
},
{
  "$addFields": {
    "dateformatted": { "$dateToString": {"format": "%Y-%m-%d %H:%M:%S", "date": "$date"} }
  }
},
...