MongoDB 聚合中的 Mongoose Virtuals
Mongoose Virtuals in MongoDB Aggregate
我的猫鼬模式如下:
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);
然而这个查询 returns VIRTUAL dateformatted 属性为 NULL。关于为什么会发生这种情况有什么想法吗?
the docs 中的几个注释谈到了为什么会这样:
- Arguments are not cast to the model's schema because
$project
operators allow redefining the "shape" of the documents at any stage
of the pipeline, which may leave documents in an incompatible format.
- The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
但它不止于此,因为 aggregate
操作是在服务器端执行的,其中不存在任何客户端 Mongoose 概念,如虚拟。
结果是您需要在 $project
和 $group
阶段中包含 date
字段,并将您自己的 dateformatted
字段添加到结果中基于 date
值的代码。
这是一个老问题,但我想出了一个有用的 hack 来恢复虚拟,并认为它可能对那些搜索这个问题的人有用。
您可以轻松地将对象转换回猫鼬模型:
documents = documents.map(d => {
return new Document(d);
});
var virtual = documents[0].virtualProperty;
<field>: <1 or true>
表单用于包含现有字段,但此处并非如此,因为 dateformatted
字段不存在,您必须使用表达式 [=16] 创建它=] 可以使用:
"$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"} }
}
},
...
我的猫鼬模式如下:
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);
然而这个查询 returns VIRTUAL dateformatted 属性为 NULL。关于为什么会发生这种情况有什么想法吗?
the docs 中的几个注释谈到了为什么会这样:
- Arguments are not cast to the model's schema because
$project
operators allow redefining the "shape" of the documents at any stage of the pipeline, which may leave documents in an incompatible format.- The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
但它不止于此,因为 aggregate
操作是在服务器端执行的,其中不存在任何客户端 Mongoose 概念,如虚拟。
结果是您需要在 $project
和 $group
阶段中包含 date
字段,并将您自己的 dateformatted
字段添加到结果中基于 date
值的代码。
这是一个老问题,但我想出了一个有用的 hack 来恢复虚拟,并认为它可能对那些搜索这个问题的人有用。
您可以轻松地将对象转换回猫鼬模型:
documents = documents.map(d => {
return new Document(d);
});
var virtual = documents[0].virtualProperty;
<field>: <1 or true>
表单用于包含现有字段,但此处并非如此,因为 dateformatted
字段不存在,您必须使用表达式 [=16] 创建它=] 可以使用:
"$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"} }
}
},
...