我正在使用聚合来查询我的架构的日期范围内的计数,我的问题是我没有从服务器得到任何响应(每次都超时),其他mongoose查询工作正常(查找,保存等)和当我调用聚合时,它取决于管道(当我只使用匹配时,我得到一个响应,当我添加放松,我没有得到任何)。
连接代码:
<div class="dots-wrap">
<form action="#">
<div>
<h2 class="strikethrough">
<p>
<a href="#tab-t1">
<input type="radio" id="test1" name="radio-group" checked>
<label for="test1"></label>
</a>
</p>
<p>
<a href="#tab-t2">
<input type="radio" id="test2" name="radio-group">
<label for="test2"></label>
</a>
</p>
<p>
<a href="#tab-t3">
<input type="radio" id="test3" name="radio-group">
<label for="test3"></label>
</a>
</p>
<p>
<a href="#tab-t4">
<input type="radio" id="test3" name="radio-group">
<label for="test4"></label>
</a>
</p>
</h2>
</div>
</form>
<div>
<div>
<label for="test1">Marketing & Lead Generation</label>
</div>
<div>
<label for="test2">Underwriting</label>
</div>
<div>
<label for="test3">Customer Management</label>
</div>
<div>
<label for="test4">Fraud, Collections & Recoveries</label>
</div>
</div>
</div>
架构:
var promise = mongoose.connect('mongodb://<username>:<password>@<db>.mlab.com:<port>/<db-name>', {
useMongoClient: true,
replset: {
ha: true, // Make sure the high availability checks are on
haInterval: 5000 // Run every 5 seconds
}
});
promise.then(function(db){
console.log('DB Connected');
}).catch(function(e){
console.log('DB Not Connected');
console.errors(e.message);
process.exit(1);
});
汇总代码:
var ProspectSchema = new Schema({
contact_name: {
type: String,
required: true
},
company_name: {
type: String,
required: true
},
contact_info: {
type: Array,
required: true
},
description:{
type: String,
required: true
},
product:{
type: Schema.Types.ObjectId, ref: 'Product'
},
progression:{
type: String
},
creator:{
type: String
},
sales: {
type: Schema.Types.ObjectId,
ref: 'User'
},
technical_sales: {
type: Schema.Types.ObjectId,
ref: 'User'
},
actions: [{
type: {type: String},
description: {type: String},
date: {type: Date}
}],
sales_connect_id: {
type: String
},
date_created: {
type: Date,
default: Date.now
}
});
调用聚合:
exports.getActionsIn = function(start_date, end_date) {
var start = new Date(start_date);
var end = new Date(end_date);
return Prospect.aggregate([
{
$match: {
// "actions": {
// $elemMatch: {
// "type": {
// "$exists": true
// }
// }
// }
"actions.date": {
$gte: start,
$lte: end
}
}
}
,{
$project: {
_id: 0,
actions: 1
}
}
,{
$unwind: "actions"
}
,{
$group: {
_id: "actions.date",
count: {
$sum: 1
}
}
}
// ,{
// $project: {
// _id: 0,
// date: {
// $dateToString: {
// format: "%d/%m/%Y",
// date: "actions.date"
// }
// }
// // ,
// // count : "$count"
// }
// }
]).exec();
}
我的主要问题是我根本没有得到任何回复,我可以处理错误信息,问题是我没有得到任何回复,所以我不知道有什么问题。
猫鼬版本:4.11.8
P.S。我尝试了聚合管道的多种变体,所以这不是我的第一次尝试,我有一个聚合工作在主要的前景模式而不是动作子文档
答案 0 :(得分:1)
这里有几个问题,主要是缺少概念。懒惰的读者可以跳到底部以获取完整的管道示例,但这里的主体是解释为什么事情按原样完成。
您正尝试在日期范围内进行选择。检查任何长时间运行操作的第一件事是你有一个有效的索引。你可能有一个,或者你可能没有。但你应该发出:(来自shell)
db.prospects.createIndex({ "actions.date": 1 })
只是为了确定。您可能真的应该将其添加到架构定义中,以便您知道应该部署它。因此,添加到您定义的架构:
ProspectSchema.index({ "actions.date": 1 })
查询&#34;范围&#34;在数组的元素上,你需要了解那些是&#34;多个条件&#34;您期望匹配元素&#34;在&#34;之间。虽然你通常可以逃避查询一个单一的财产&#34;使用&#34; Dot Notation&#34;的数组,你遗漏了[$gte][1]
和$lte
的应用就像明确地用$and
多次指定属性一样。
每当你有这样的&#34;多个条件&#34; 始终意味着使用$elemMatch
。没有它,您只需测试数组中的每个值,看它是否大于或小于(有些可能更大,有些可能更小)。 $elemMatch
运算符确保&#34;两者都是&#34;应用于相同的&#34;元素&#34;,而不仅仅是所有数组值为&#34;点符号&#34;暴露他们:
{ "$match": {
"actions": {
"$elemMatch": { "date": { "$gte": start, "$lte: end } }
}
}}
现在只能匹配&#34;数组元素&#34;落在指定日期之间。没有它,您正在选择和处理与选择无关的更多数据。
数组过滤:标记为粗体,因为它的突出性无法忽略。任何初始$match
都可以像任何&#34;查询&#34;因为它的工作&#34;是选择文件&#34;对表达式有效。但是不会对返回的文档中的数组内容产生任何影响。
每当你有这样的文件选择条件时,你几乎总是打算过滤&#34;过滤&#34;来自阵列本身的这样的内容。这是一个单独的过程,实际上应该在使用内容的任何其他操作之前执行。特别是[$unwind][4]
。
所以你真的应该在$filter
或$addFields
中添加一个$project
,以适合你的意图&#34;立即&#34;在选择任何文件之后:
{ "$project": {
"_id": 0,
"actions": {
"$filter": {
"input": "$actions",
"as": "a",
"in": {
"$and": [
{ "$gte": [ "$$a.date", start ] },
{ "$lte": [ "$$a.date", end ] }
]
}
}
}
}}
现在您已经知道的数组内容&#34;必须&#34;由于初始查询条件,至少包含一个有效项目,是&#34;减少&#34;仅限于那些与您想要的日期范围实际匹配的条目。这消除了以后处理的大量开销。
注意不同的&#34;逻辑变体&#34;在$gte
条件下使用的$lte
和$filter
。这些求值会为需要它们的表达式返回一个布尔值。
分组它可能只是尝试获得结果,但您拥有的代码并没有真正对相关日期做任何事情。由于典型的日期值应该以毫秒精度提供,因此您通常希望减少它们。
评论代码建议在$dateToString
内使用$project
。强烈建议您不要这样做。如果您打算进行此类缩减,请将该表达式直接提供给$group
内的分组键:
{ "$group": {
"_id": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$actions.date"
}
},
"count": { "$sum": 1 }
}}
我个人不喜欢回复&#34;字符串&#34;当一个自然Date
对象已经为我正确序列化时。所以我喜欢使用&#34;数学&#34;接近&#34; round&#34;日期代替:
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": [ "$actions.date", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$actions.date", new Date(0) ] },
1000 * 60 * 60 * 24
]}
],
new Date(0)
]
},
"count": { "$sum": 1 }
}}
返回有效的Date
对象&#34;舍入&#34;到了今天。里程可能因首选方法而异,但它是我喜欢的。并且传输的字节数最少。
Date(0)
的使用代表&#34;纪元日期&#34;。因此当你$subtract
一个BSON日期从另一个BSON日期开始时,你最终将两者之间的毫秒差异作为整数。当$add
为BSON日期的整数值时,您将获得一个新的BSON日期,表示两者之间的毫秒值之和。这是转换为数字,舍入到最近的一天开始,然后将数字转换回日期值的基础。
通过直接在$group
而不是$project
内创建该语句,您基本上可以保存实际被解释为&#34;遍历所有数据并返回此计算值,然后去做...&#34; 。与在一堆物体上工作大致相同,先用笔标记它们,然后将它们作为一个单独的步骤进行计数。
作为单个管道阶段,它会在计算要累积的值的同时进行累积,从而节省大量资源。如果您认为它与提供的类比非常相似,那么它就非常有意义。
作为完整的管道示例,您可以将上述内容放在一起:
Prospect.aggregate([
{ "$match": {
"actions": {
"$elemMatch": { "date": { "$gte": start, "$lte: end } }
}
}},
{ "$project": {
"_id": 0,
"actions": {
"$filter": {
"input": "$actions",
"as": "a",
"in": {
"$and": [
{ "$gte": [ "$$a.date", start ] },
{ "$lte": [ "$$a.date", end ] }
]
}
}
}
}},
{ "$unwind": "$actions" },
{ "$group": {
"_id": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$actions.date"
}
},
"count": { "$sum": 1 }
}}
])
老实说,如果在确定索引到位后,并且在该管道之后仍然存在超时问题,那么请缩短日期选择,直到获得合理的响应时间。
如果它仍然花费太长时间(或日期减少不合理),那么您的硬件根本无法完成任务。如果你真的有很多数据,那么你必须对期望合理。所以扩展或扩展,但这些事情超出了任何问题的范围。
目前,这些改进应该对目前为止所展示的任何尝试产生重大影响。主要是由于一些基本概念被遗漏。