我已经解决了这个问题但是在mongodb服务器而不是客户端上寻找更好的方法。
我有一个订单集合,其中包含展示日期时间(iso日期)和产品。
{ _id:1, datetime:“T1”, product:”Apple”}
{ _id:2, datetime:“T2”, product:”Orange”}
{ _id:3, datetime:“T3”, product:”Pear”}
{ _id:4, datetime:“T4”, product:”Pear”}
{ _id:5, datetime:“T5”, product:”Apple”}
目标:在给定时间(或一组时间)之前显示我的产品组中每个产品的最后一个订单。产品是有限的,已知。
例如。查询时间T6将返回:
{ _id:2, datetime:“T2”, product:”Orange”}
{ _id:4, datetime:“T4”, product:”Pear”}
{ _id:5, datetime:“T5”, product:”Apple”}
T4将返回:
{ _id:1, datetime:“T1”, product:”Apple”}
{ _id:2, datetime:“T2”, product:”Orange”}
{ _id:4, datetime:“T4”, product:”Pear”}
我通过在订单上创建综合索引来实现这一点[datetime:descending,product:ascending] 然后在java客户端上:
findLastOrdersForTimes(times) {
for (time: times) {
for (product: products) {
db.orders.findOne(product:product, datetime: { $lt: time}}
}
}
}
现在它非常快,因为它命中索引并且只获取我需要的数据。但是我需要查询许多时间点(100000+),这将是网络上的大量呼叫。我的订单表也会很大。那么如何在服务器上一次点击这样做,即返回一组时间>阵列产品?如果它是oracle,则id创建一个存储过程,其中的游标会及时循环并收集每个时间点的结果,并在最后一个时间点之后到达最后一个产品时中断。我已经看过聚合框架和mapreduce,但看不出如何实现这种循环。有什么指针吗?
答案 0 :(得分:0)
如果您真的想要每个产品的最后一个订单,那么t he aggregation framework会出现:
db.times.aggregate([
{ "$match": {
"product": { "$in": products },
}},
{ "$group": {
"_id": "$product",
"datetime": { "$max": "$datetime" }
}}
])
数组为products
的示例:
var products = ['Apple', 'Orange', 'Pear'];
{ "_id" : "Pear", "datetime" : "T4" }
{ "_id" : "Orange", "datetime" : "T2" }
{ "_id" : "Apple", "datetime" : "T5" }
或者,如果原始文档中的_id
对您很重要,请使用$sort
代替$last
:
db.times.aggregate([
{ "$match": {
"product": { "$in": products },
}},
{ "$sort": { "datetime": 1 } },
{ "$group": {
"_id": "$product",
"id": { "$last": "$_id" },
"datetime": { "$last": "$datetime" }
}}
])
这就是你最不希望在最后两种情况下做的事情。但你真正想要的指数是"产品":
db.times.ensureIndex({ "product": 1 })
因此,即使您需要使用 $match
条件对 $lt
特定时间点进行迭代,那么这样做会更好或者更好你可以修改"分组"包括" datetime"以及在 $match
中保留一组。
无论如何它似乎更好,所以这或许至少可以改变你的想法。
如果我正确地读出您的笔记,您似乎只是想找到它的头部并找到每个时间点的最后一个产品。所以声明没有太大区别:
db.times.aggregate([
{ "$match": {
"datetime": { "$in": ["T4","T5"] },
}},
{ "$sort": { "product": 1, "datetime": 1 } },
{ "$group": {
"_id": "$datetime",
"id": { "$last": "$_id" },
"product": { "$last": "$product" }
}}
])
理论上,这就像基于你如何呈现问题一样。我有这样的感觉,虽然你正在抽象这个" datetime"可能是实际时间戳作为日期对象类型。
所以你可能不知道你可以应用date aggregation operators,例如获得每小时的边界:
db.times.aggregate([
{ "$group": {
"_id": {
"year": { "$year": "$datetime" },
"dayOfYear": { "$dayOfYear": "$datetime" },
"hour": { "$hour": "$datetime" }
},
"id": { "$last": "$_id" },
"datetime": { "$last": "$datetime" },
"product": { "$last": "$product" }
}}
])
如果是基于纪元的时间戳
,甚至可以使用日期数学而不是运算符db.times.aggregate([
{ "$group": {
"_id": {
"$subtract": [
{ "$subtract": [ "$datetime", new Date("1970-01-01") ] },
{ "$mod": [
{ "$subtract": [ "$datetime", new Date("1970-01-01") ] },
1000*60*60
]}
]
},
"id": { "$last": "$_id" },
"datetime": { "$last": "$datetime" },
"product": { "$last": "$product" }
}}
])
当然,您可以使用 $match
和 $gt
< $lt
为日期添加范围查询/ strong>运算符将数据保持在您特别关注的范围内。
您的整体解决方案可能是各种想法的组合,但正如我所说,您的问题似乎是在某些时间边界上匹配最后的条目,因此最后的示例可能与过滤某些产品相结合是您需要的而不是循环。findOne()
请求。