MongoDB Aggregate 中的 Mongoose Virtuals

Posted

技术标签:

【中文标题】MongoDB Aggregate 中的 Mongoose Virtuals【英文标题】:Mongoose Virtuals in MongoDB Aggregate 【发布时间】:2015-07-14 08:29:28 【问题描述】:

我的 Mongoose Schema 如下:

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);

我的架构中的文档如下:


 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,
  ]

我的查询:

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);

然而,此查询将 VIRTUAL 日期格式属性返回为 NULL。关于为什么会发生这种情况的任何想法?

【问题讨论】:

【参考方案1】:

the docs 中的一些注释说明了为什么会这样:

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

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

结果是您需要在$project$group 阶段中​​包含date 字段,并根据date 值将您自己的dateformatted 字段添加到代码中的结果中。

【讨论】:

The documents returned are plain javascript objects 是不是说我不能对聚合框架查询返回的文档使用虚拟字段和猫鼬的方法?【参考方案2】:

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

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

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

var virtual = documents[0].virtualProperty;

【讨论】:

谢谢!我通过调用.toJSON() 稍微修改了它以填充虚拟对象。 documents.map(d => new Document(d).toJSON())【参考方案3】:

<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" 
  
,
...

【讨论】:

以上是关于MongoDB Aggregate 中的 Mongoose Virtuals的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB Aggregate 中的 Mongoose Virtuals

MongoDB中聚合工具Aggregate等的介绍与使用

MongoDB 聚合

mongodb aggregate

MongoDB 聚合

MongoDB 中的关联查询MongoDB : aggregate/lookup 对比 Mongoose : ref / populate