【问题标题】:Mongodb querie get peaks of timeseriesMongodb查询获取时间序列的峰值
【发布时间】:2020-02-17 18:58:50
【问题描述】:

我有一个 mongodb,里面有一些传感器数据。每次触发传感器时,该值都会增加 1。有时会重置传感器。现在我想将传感器数据的值相加得到一个时间序列的总值。

我的数据如下所示:

{
    value: 10
    timestamp: 1577836800
}, {
    value: 12
    timestamp: 1577836810
}, {
    value: 17 <== PEAK
    timestamp: 1577836820
}, {
    value: 2
    timestamp: 1577836830
}, {
    value: 7
    timestamp: 1577836840
}, {
    value: 10 <== PEAK
    timestamp: 1577836850
},{
    value: 1
    timestamp: 1577836860
}

我的目标是在最小和最大时间戳内获取所有峰值 (17 + 10) 的值,并将它们相加(之后也可以手动完成)。这意味着我必须按时间戳对它们进行排序,并将一个值与前一个值进行比较,以检查哪个值更高。我是 mongo 的新手,不知道从哪里开始。欢迎任何帮助!

【问题讨论】:

  • 您是否在每个期间创建一个新文档?这将使查询变得非常简单,但如果不是这样,那么我们需要创建一个 JavaScript 函数来找出周期(时间序列)是什么。
  • 是的,我每个时期都创建一个新对象。所以上面代码中的每个对象都是一个新的文档。抱歉,我应该说清楚的。
  • 别担心,现在的挑战是起始值和峰值与周期相结合,分散在整个集合中。您是使用边缘设备插入文档还是您的传感器这样做?
  • 数据由nodeJS服务写入

标签: mongodb mongodb-query


【解决方案1】:

我在评论中提到了边缘设备的使用,这使得可以将值分配给适当的时期。使用 nodeJS 服务来确定它必须更新哪个文档,因为它使查询集合变得更加容易和快捷。

您可以在每个文档中使用这种格式:

{
    "period" : {
        "start" : 1577836800,
        "end" : 1577836820
    },
    "values": [
        {
            "value": 10
            "timestamp": 1577836800
        }, 
        {
            "value": 12
            "timestamp": 1577836810
        }, 
        {
            "value": 17
            "timestamp": 1577836820
        }
    ]
}

以传感器为服务提供新值为例。在这种情况下,值为 2,获取最后一个文档并将其与 values 中的最后一个值进行比较。

如果新值小于前一个值,则使用以下信息创建一个新文档,当然不要忘记使用最后一个值的时间戳填写“periode.end”来更新前一个文档。

{
    "period" : {
        "start" : 1577836830,
        "end" : null
    },
    "values": [
        {
            "value": 2
            "timestamp": 1577836830
        }
    ]
}

更新

我编写了这个 JavaScript 脚本来从一个集合迁移,根据需求将数据处理成一种新格式,然后将它们插入到新集合中。

function createSensorObject(start, value){
    var doc = new Object();

    doc.period = new Object();
    doc.period["start"] = start;
    doc.period["end"] = null;

    doc.values = new Array();

    var valueObject = new Object();
    valueObject["value"] = value;
    valueObject["timestamp"] = start;

    doc.values.push(valueObject);
    return doc;
}

function pushNewValueIntoSensorObject(doc, value, timestamp){
    doc["period"]["end"] = timestamp;

    var valueObject = new Object();
    valueObject["value"] = value;
    valueObject["timestamp"] = timestamp;

    doc["values"].push(valueObject);
}

function closePeriodInSensorObject(doc) {
    doc["period"]["end"] = doc["period"]["start"];
}

// New collection cursor to insert new values.
var sensorNewCursor = db.sensor_new; 

// Using the aggregate pipeline to sort and using toArray to iterate through.
var sensorDocuments = db.sensor.aggregate([ { $sort: { "timestamp" : 1}}]).toArray();

// Temp array to store the new sensor objects.
var sensorObjectArray = new Array();

for(var i = 0; i < sensorDocuments.length; i++) {
    // Working with the current document.
    var currentDocument = sensorDocuments[i];
    var value = currentDocument["value"];
    var timestamp = currentDocument["timestamp"];

    // Initially creating a new document.
    if(i === 0) {
        var sensorObject = createSensorObject(timestamp, value);

        // If there is only one document then we need to close the document.
        if(i === (sensorDocuments.Length - 1)) {
            closePeriodInSensorObject(sensorObject);
        }

        // Push into temp array.
        sensorObjectArray.push(sensorObject);
    }
    else{   
        // Get the previous value.
        var previousValue = sensorDocuments[i - 1]["value"];

        // Get the latest sensor object from the temp array.
        var sensorObject = sensorObjectArray[sensorObjectArray.length - 1];

        // When the new value is larger or equal to the previous value.
        if(value >= previousValue) {
            // Push the new value into the latest sensor object.
            pushNewValueIntoSensorObject(sensorObject, value, timestamp);
        }
        else {  
            // Create new sensor object.
            var sensorObject = createSensorObject(timestamp, value);
            sensorObjectArray.push(sensorObject);
        }
    }
}

print(`Inserting ${sensorObjectArray.length} objects into the db.sensor_new collection.`);

// Insert the whole array into the new collection
sensorNewCursor.insert(sensorObjectArray);

您可以使用 load 函数从 mongoshell 调用此 js 文件。 https://docs.mongodb.com/manual/reference/method/load/


看看两张截图。


终于!查询

您必须使用聚合管道来获取每个周期的最大值之和。

首先我们需要展开这些值,然后使用 $max 进行 $group 以找到我们的最大值,然后再次分组但没有 $_id 字段来对这些值进行 $sum。

db.collection.aggregate([
    {
        $unwind: "$values"
    },
    {
        $group: {
            "_id": "$_id",
            "value": {
                $max: "$values.value"
            }
        }
    },
    {
        $group: {
            "_id": null,
            "total": {
                $sum: "$value"
            }
        }
    }
])

这个查询的结果是 28

[
    {
        "_id": null,
        "total": 28
    }
]

MongoPlayground 根据新的文档结构: https://mongoplayground.net/p/WblN5QzPoeB

我真的希望这对您有用,如果没有请发表评论。 :)

【讨论】:

  • 这是个好主意,但目前我的问题是数据已经在数据库中。现在最好是查询。
  • 然后我们将不得不编写一个 javascript 函数来将数据迁移到一个新的集合中,因为这对于查询是不可能的。
猜你喜欢
  • 1970-01-01
  • 2017-03-05
  • 2021-08-29
  • 2019-07-31
  • 2012-08-28
  • 2012-09-10
  • 2014-11-28
  • 1970-01-01
  • 2012-03-01
相关资源
最近更新 更多