Mongodb 猫鼬在聚集后繁殖
在运行聚合管道之后,我试图获得一个特定的数据模型,然后再进行填充,但我还没有达到这个目标 最终的预期结果如下:Mongodb 猫鼬在聚集后繁殖,mongodb,mongoose,mongodb-query,aggregation-framework,mongoose-populate,Mongodb,Mongoose,Mongodb Query,Aggregation Framework,Mongoose Populate,在运行聚合管道之后,我试图获得一个特定的数据模型,然后再进行填充,但我还没有达到这个目标 最终的预期结果如下: [ { _accountId: "5beee0966d17bc42501f1234", name: "Company Name 1", contactEmail: "email1@email.com", contactName: "contact Name 1" reason: "Warranties", total: 1152,
[
{
_accountId: "5beee0966d17bc42501f1234",
name: "Company Name 1",
contactEmail: "email1@email.com",
contactName: "contact Name 1"
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
},
{
_accountId: "5beee0966d17bc42501f1235",
name: "Company Name 2",
contactEmail: "email2@email.com",
contactName: "contact Name 2"
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
}
]
Warranty.aggregate([
{
$match: {
payStatus: "Invoiced Next Billing Cycle"
}
},
{
$group: {
_id: "$_accountId",
total: {
$sum: "$warrantyFee"
},
lineItems: {
$push: {
_id: "$_id",
jobsiteAddress: {
$concat: [
"$jobsiteAddressStreet",
" ",
"$jobsiteAddressCity",
", ",
"$jobsiteAddressState",
" ",
"$jobsiteAddressZip"
]
},
warrantyFee: "$warrantyFee"
}
}
}
},
{
$project: {
reason: "Warranties",
total: "$total",
lineItems: "$lineItems"
}
}
])
.then(warranties => {
console.log(warranties);
Account.populate(warranties, {
path: "_id",
select: "contactName contactEmail name"
})
.then(warranties => {
res.send(warranties);
})
.catch(err => {
res.status(422).send(err);
throw err;
});
})
.catch(err => {
res.status(422).send(err);
throw err;
});
[
{
_id: {
_id: "5bc39dfa331c0e2cb897b61e",
name: "Company Name 1",
contactEmail: "email1@email.com",
contactName: "Contact Name 1"
},
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
},
{
_id: {
_id: "5bc39dfa331c0e2cb897b61e",
name: "Company Name 2",
contactEmail: "email2@email.com",
contactName: "Contact Name 2"
},
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
}
]
我从以下两个模型中收集这些数据:
保修
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddressStreet: String,
jobsiteAddressCity: String,
jobsiteAddressState" String,
jobsiteAddressZip: Number,
warrantyFee: Number,
_accountId: {
type: Schema.Types.ObjectId,
ref: "accounts"
},
payStatus: String
}
账户
{
_id: "5beee0966d17bc42501f1235",
name: String,
contactName: String,
contactEmail: String
}
我当前的查询如下:
[
{
_accountId: "5beee0966d17bc42501f1234",
name: "Company Name 1",
contactEmail: "email1@email.com",
contactName: "contact Name 1"
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
},
{
_accountId: "5beee0966d17bc42501f1235",
name: "Company Name 2",
contactEmail: "email2@email.com",
contactName: "contact Name 2"
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
}
]
Warranty.aggregate([
{
$match: {
payStatus: "Invoiced Next Billing Cycle"
}
},
{
$group: {
_id: "$_accountId",
total: {
$sum: "$warrantyFee"
},
lineItems: {
$push: {
_id: "$_id",
jobsiteAddress: {
$concat: [
"$jobsiteAddressStreet",
" ",
"$jobsiteAddressCity",
", ",
"$jobsiteAddressState",
" ",
"$jobsiteAddressZip"
]
},
warrantyFee: "$warrantyFee"
}
}
}
},
{
$project: {
reason: "Warranties",
total: "$total",
lineItems: "$lineItems"
}
}
])
.then(warranties => {
console.log(warranties);
Account.populate(warranties, {
path: "_id",
select: "contactName contactEmail name"
})
.then(warranties => {
res.send(warranties);
})
.catch(err => {
res.status(422).send(err);
throw err;
});
})
.catch(err => {
res.status(422).send(err);
throw err;
});
[
{
_id: {
_id: "5bc39dfa331c0e2cb897b61e",
name: "Company Name 1",
contactEmail: "email1@email.com",
contactName: "Contact Name 1"
},
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
},
{
_id: {
_id: "5bc39dfa331c0e2cb897b61e",
name: "Company Name 2",
contactEmail: "email2@email.com",
contactName: "Contact Name 2"
},
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
}
]
其结果如下:
[
{
_accountId: "5beee0966d17bc42501f1234",
name: "Company Name 1",
contactEmail: "email1@email.com",
contactName: "contact Name 1"
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
},
{
_accountId: "5beee0966d17bc42501f1235",
name: "Company Name 2",
contactEmail: "email2@email.com",
contactName: "contact Name 2"
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
}
]
Warranty.aggregate([
{
$match: {
payStatus: "Invoiced Next Billing Cycle"
}
},
{
$group: {
_id: "$_accountId",
total: {
$sum: "$warrantyFee"
},
lineItems: {
$push: {
_id: "$_id",
jobsiteAddress: {
$concat: [
"$jobsiteAddressStreet",
" ",
"$jobsiteAddressCity",
", ",
"$jobsiteAddressState",
" ",
"$jobsiteAddressZip"
]
},
warrantyFee: "$warrantyFee"
}
}
}
},
{
$project: {
reason: "Warranties",
total: "$total",
lineItems: "$lineItems"
}
}
])
.then(warranties => {
console.log(warranties);
Account.populate(warranties, {
path: "_id",
select: "contactName contactEmail name"
})
.then(warranties => {
res.send(warranties);
})
.catch(err => {
res.status(422).send(err);
throw err;
});
})
.catch(err => {
res.status(422).send(err);
throw err;
});
[
{
_id: {
_id: "5bc39dfa331c0e2cb897b61e",
name: "Company Name 1",
contactEmail: "email1@email.com",
contactName: "Contact Name 1"
},
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
},
{
_id: {
_id: "5bc39dfa331c0e2cb897b61e",
name: "Company Name 2",
contactEmail: "email2@email.com",
contactName: "Contact Name 2"
},
reason: "Warranties",
total: 1152,
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
]
}
]
正如你所看到的,这与一些小问题非常接近
[
{
lineItems: [
{
_id: "5be203eb3afd8098d4988152",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
],
reason: "Warranties",
total: 384,
type: "Invoice",
date: "2018-11-21T14:08:15.052Z",
company: "Company Name 1",
contactName: "Contact Name 1",
contactEmail: "email1@email.com",
_accountId: "5be203eb3afd8098d4988152",
referenceNumber: 1542809296615
},
{
lineItems: [
{
_id: "5beee0966d17bc42501f5086",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf43929e7179a56e21382bc",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
},
{
_id: "5bf4392fe7179a56e21382bd",
jobsiteAddress: "1234 Street Southwest Sunnyville, Wyoming 12345",
warrantyFee: 384
}
],
reason: "Warranties",
total: 1152,
type: "Invoice",
date: "2018-11-21T14:08:15.052Z",
company: "Company Name 2",
contactName: "Contact Name 2",
contactEmail: "email2@email.com",
_accountId: "5bc39dfa331c0e2cb897b61e",
referenceNumber: 1542809295680
}
]
- 一种是在它被填充后用javascript处理它。要实现这一点,可以使用函数“map()”
- 另一种解决方案是在聚合管道中使用$lookup来填充同一mongoDB查询中的文档,在$lookup之后,必须再次使用$project来根据需要构建输出文档
因此,当您要求对聚合结果进行“填充”时,实际上您缺少了一些概念。通常,这不是您实际要做的,而是为了解释以下要点:
aggregate()
的输出不同于Model.find()
或类似操作,因为此处的目的是“重塑结果”。这基本上意味着作为聚合源使用的模型不再被视为输出上的模型。即使在输出时仍然保持完全相同的文档结构,这也是正确的,但在您的情况下,输出显然与源文档不同
无论如何,它不再是您所采购的保修
模型的实例,而只是一个普通对象。我们可以在稍后讨论时解决这个问题populate()
无论如何都有点“老掉牙”。这实际上只是在Mongoose实现的早期添加到Mongoose中的一个方便功能。它真正做的只是对单独集合中的相关数据执行“另一个查询”,然后将内存中的结果合并到原始集合输出
由于很多原因,在大多数情况下,这并不是真正有效的,甚至是不可取的。与流行的误解相反,这不是实际上的“加入”
对于真正的“连接”,您实际上使用了聚合管道阶段,MongoDB使用该阶段从另一个集合返回匹配项。与populate()。这避免了网络开销,通常更快,因为“真正的连接”允许您执行populate()
无法执行的操作
中尝试填充()
。然后()
返回结果后,您要做的是将添加到管道中:
{ "$lookup": {
"from": Account.collection.name,
"localField": "_id",
"foreignField": "_id",
"as": "accounts"
}},
{ "$unwind": "$accounts" },
{ "$project": {
"_id": "$accounts",
"total": 1,
"lineItems": 1
}}
注意,这里有一个约束,即数组的输出总是一个数组。只有一个或多个相关项作为输出获取并不重要。管道阶段将从当前文档中查找“localField”
的值,并使用该值匹配指定的“foreignField”
中的值。在这种情况下,它是从聚合目标到外部集合的\u id
的\u id
由于输出始终是前面提到的一个数组,因此在这个实例中使用该方法的最有效方法是直接在后面添加一个stage。所有这些都将为目标数组中返回的每个项返回一个新文档,在本例中,您希望它是一个。如果\u id
在外部集合中不匹配,则将删除不匹配的结果
请注意,这实际上是一个优化模式,如核心文档中所述。这里发生了一件特殊的事情,指令实际上以一种有效的方式合并到操作中。你可以在那里了解更多
使用填充
从以上内容中,您应该能够基本了解为什么populate()
这里是错误的做法。除了输出不再由保修
模型对象组成这一基本事实之外,该模型实际上只知道\u accountId
属性中描述的外来项,而这些外来项在输出中并不存在
现在您可以实际定义一个模型,该模型可用于显式地将输出对象强制转换为已定义的输出类型。一个简短的演示将涉及向您的应用程序中添加以下代码:
// Special models
const outputSchema = new Schema({
_id: { type: Schema.Types.ObjectId, ref: "Account" },
total: Number,
lineItems: [{ address: String }]
});
const Output = mongoose.model('Output', outputSchema, 'dontuseme');
然后可以使用这个新的输出
模型,将生成的普通JavaScript对象“强制转换”到Mongoose文档中,这样就可以实际调用以下方法:
// excerpt
result2 = result2.map(r => new Output(r)); // Cast to Output Mongoose Documents
// Call populate on the list of documents
result2 = await Output.populate(result2, { path: '_id' })
log(result2);
因为Output
定义了一个模式,该模式知道“ref