MongoDB按名称和不同时间间隔聚合

时间:2018-01-04 10:39:06

标签: mongodb mongoose aggregation-framework

我一直在寻找解决方案好几天,但我无法得到结果。拜托,帮助我!

我在MongoDB中有一个数据:

{ name: 'apple', date: 2018-01-04T10:00:00.000Z, price: 100 }
{ name: 'apple', date: 2018-01-04T10:01:00.000Z, price: 101 }
{ name: 'apple', date: 2018-01-04T10:02:00.000Z, price: 102 }
{ name: 'apple', date: 2018-01-04T10:03:00.000Z, price: 103 }
{ name: 'apple', date: 2018-01-04T10:04:00.000Z, price: 102 }
{ name: 'apple', date: 2018-01-04T10:05:00.000Z, price: 104 }

{ name: 'cherry', date: 2018-01-04T10:00:00.000Z, price: 53 }
{ name: 'cherry', date: 2018-01-04T10:01:00.000Z, price: 55 }
{ name: 'cherry', date: 2018-01-04T10:02:00.000Z, price: 51 }
{ name: 'cherry', date: 2018-01-04T10:03:00.000Z, price: 51 }
{ name: 'cherry', date: 2018-01-04T10:04:00.000Z, price: 50 }
{ name: 'cherry', date: 2018-01-04T10:05:00.000Z, price: 52 }

{ name: 'melon', date: 2018-01-04T10:00:00.000Z, price: 133 }
{ name: 'melon', date: 2018-01-04T10:01:00.000Z, price: 132 }
{ name: 'melon', date: 2018-01-04T10:02:00.000Z, price: 136 }
{ name: 'melon', date: 2018-01-04T10:03:00.000Z, price: 137 }
{ name: 'melon', date: 2018-01-04T10:04:00.000Z, price: 138 }
{ name: 'melon', date: 2018-01-04T10:05:00.000Z, price: 140 }

我希望通过一个查询得到这个结果。

{name: 'apple', price_last: 104, price_1m_ago: 102, price_5m_ago: 100}
{name: 'cherry', price_last: 52, price_1m_ago: 50, price_5m_ago: 53}
{name: 'melon', price_last: 140, price_1m_ago: 138, price_5m_ago: 133}

我尝试使用聚合但我无法以任何方式构建有效的查询。

food.aggregate([
    {
        $group: {
            _id: "$name",
            price_last: {$last:"$price"},
        }
    }
]}

2 个答案:

答案 0 :(得分:0)

如果您的数据保证每分钟包含一个元素且中间没有丢失的数据,则可以非常轻松地实现这一点:

food.aggregate([{
    $sort: {
        date: 1 // make sure the data is sorted by date
    }
}, {
    $group: {
        _id: "$name",
        prices: { $push: "$price" }, // create an array per "name" with all prices in it
    }
}, {
    $project: {
        name: "$_id",
        price_last: { $arrayElemAt: [ "$prices", -1 ] }, // select the first from the end of the array
        price_1m_ago:  { $arrayElemAt: [ "$prices", -2 ] }, // select the second from the end
        price_5m_ago:  { $arrayElemAt: [ "$prices", -6 ] } // select the 6th from the end
    }
}])

答案 1 :(得分:0)

我用@Avij第一个查询得到了这个结果。这是一些重复。如果我更换$ minute:' $ lastdate'使用新的Date(),查询无效。

{ _id: 'melon',
    lastPrice: 1575,
    five_min_price: 1573 },
  { _id: 'cherry',
    lastPrice: 60165,
    five_min_price: 59098 },
  { _id: 'apple',
    lastPrice: 0153,
    five_min_price: 0155 },
  { _id: 'apple',
    lastPrice: 0153,
    five_min_price: 0154 },
  { _id: 'apple',
    lastPrice: 0153,
    five_min_price: 0155 },
  { _id: 'apricot',
    lastPrice: 6897,
    five_min_price: 6235 },
  { _id: 'apricot',
    lastPrice: 6897,
    five_min_price: 6235 },
  { _id: 'apricot',
    lastPrice: 6897,
    five_min_price: 6822 },
  { _id: 'apricot',
    lastPrice: 6897,
    five_min_price: 683 },
  { _id: 'cucumber',
    lastPrice: 3461,
    five_min_price: 3533 },
  { _id: 'cucumber',
    lastPrice: 3461,
    five_min_price: 3496 },
  { _id: 'orange',
    lastPrice: 5366,
    five_min_price: 5367 }

也许可以使用$ match查询查询,然后在此结果中获得最后一个。如果DB中的日期存储在Unix格式中。这项工作只有一个结果,但我如何得到10s / 30s / 1m / 5m / 10m / 30m / 1h会产生一个查询。

var now = new Date();
var shift_5_min = shift_5_min = new Date( now.valueOf() - ( 5*60*1000 );

"$match": {
    "date": { "$lte": shift_5_min" }
}
$group: {
     id: "$name",
     price: {$last: "$price"}
}