Mongodb Mongo:存储时间序列测量数据

Mongodb Mongo:存储时间序列测量数据,mongodb,Mongodb,我们每隔一段时间存储温度测量数据,我们只想保留90个以前的数据。我们的数据结构如下: { "_id" : ObjectId("xxx"), "device" : "deviceId1", "count": 2, "values" : [ { "ts" : NumberLong("1471077454902"), "measureData" : 37.3 }, { "ts" : NumberLong("1471077454911"), "measureData" : 37.

我们每隔一段时间存储温度测量数据,我们只想保留90个以前的数据。我们的数据结构如下:

{ "_id" : ObjectId("xxx"), "device" : "deviceId1", "count": 2, "values" : 
  [ 
    { "ts" : NumberLong("1471077454902"), "measureData" : 37.3 },
    { "ts" : NumberLong("1471077454911"), "measureData" : 37.4 } 
  ] 
}
count是值的大小,例如,当数组有2个元素时,则大小为2。 我们对Java API的设计如下:

 When the new measurement data comes:
   Get corresponding device id count:
       if count < 90:
             Push the new mesaure data into values and increase count by 1                
       if count >90:
             Pull the first element of the array and push the latest data into array.
  Store the first element pulled into history collection.
当新的测量数据出现时:
获取相应的设备id计数:
如果计数小于90:
将新的mesaure数据推入值并将计数增加1
如果计数>90:
拉取数组的第一个元素并将最新数据推入数组。
存储拉入历史记录集合的第一个元素。
是否有一个查询或一个聚合可以执行这些步骤?或者我们应该用传统的方法,比如查询、求值,然后推或者拉/推

// //***************第2部分传统方法***************//

public class TSDesignMain {

    public static void main(String[] args) {
        MongoClient mClient = new MongoClient();
        MongoDatabase db = mClient.getDatabase(MongoTSConstants.dbname);

        long tWarmingStart = System.nanoTime();
        InsertingWarmingDocument(db);
        long tWarmingEnd = System.nanoTime();
        double tDurationWarming = (double) (tWarmingEnd-tWarmingStart) / 1000/1000;
        System.out.println("warming db, insert 10000 document per event duration is "+tDurationWarming+"ms");


        long tInsert1Dev1DocStart = System.nanoTime();
        for(int j=1;j<10000;j++){
            storeMeasureDataIntoDB(db);
        }    
        long tInsert1Dev1DocEnd = System.nanoTime();
        double tInsert1Dev1DocDuration = (double) (tInsert1Dev1DocEnd-tInsert1Dev1DocStart) / 1000/1000;
        System.out.println("insert 10000 document in 90*24*30 elements array duration is "+tInsert1Dev1DocDuration+"ms");   


        long tQuery1Dev1DocStart = System.nanoTime();
        for(int j=1;j<10000;j++){
            handleQueryDocument(db);
        }    
        long tQuery1Dev1DocEnd = System.nanoTime();
        double tQuery1Dev1DocDuration = (double) (tQuery1Dev1DocEnd-tQuery1Dev1DocStart) / 1000/1000;
        System.out.println("query 10000 times in 90*24*30 elements array duration is "+tQuery1Dev1DocDuration+"ms");   


        mClient.close();               

    }

    private static void InsertingWarmingDocument(MongoDatabase db) {

        long ts = Calendar.getInstance().getTimeInMillis();
        for(long i=1;i<100;i++){
            db.getCollection(MongoTSConstants.tsDataPdCollection).insertOne( 
                new Document(MongoTSConstants.deviceFn,MongoTSConstants.deviceIdPrefix+i+"test")
                     .append(MongoTSConstants.tsFn,ts+i)        
                );
        }    
    }

    private static void handleQueryDocument(MongoDatabase db) {
        Document where = new Document(MongoTSConstants.deviceFn,MongoTSConstants.deviceIdPrefix+1); 
        FindIterable<Document> it = db.getCollection(MongoTSConstants.tsDataInOneCollection).find(where);
        MongoCursor<Document> cur = it.iterator();

        int i=0;

        if(!cur.hasNext()){
             Document doc = cur.next();
             ArrayList<Document> obj = (ArrayList<Document>) doc.get("values");           
        }

    }



    private static void storeMeasureDataIntoDB(MongoDatabase db) {
        Document where = new Document(MongoTSConstants.deviceFn,MongoTSConstants.deviceIdPrefix+1); 
        FindIterable<Document> it = db.getCollection(MongoTSConstants.tsDataInOneCollection).find(where);
        MongoCursor<Document> cur = it.iterator();

        int i=0;


        /**
         * There is no device document in DB, insert new one. and use update to store 1st measure data 
         * 
         */
        if(!cur.hasNext()){
            long tsInsert = System.nanoTime();
            /**
             * insert device id
             */
            db.getCollection(MongoTSConstants.tsDataInOneCollection).insertOne(
                        new Document(MongoTSConstants.deviceFn,MongoTSConstants.deviceIdPrefix+1)
                            .append(MongoTSConstants.countFn,0));
            /**
             * using update to insert values array (store the first measure data)
             * 
             */                 
            db.getCollection(MongoTSConstants.tsDataInOneCollection).updateOne(
                        new Document(MongoTSConstants.deviceFn,MongoTSConstants.deviceIdPrefix+1),
                        new Document("$push", new Document("values",new Document(MongoTSConstants.tsFn,tsInsert).
                                 append(MongoTSConstants.measureDataFn,37.1)))
                        .append("$inc", new Document(MongoTSConstants.countFn,1)));
        }else{      
            while(cur.hasNext()){
                /**
                 * if i > 1, it means there are two doc with same device id, error!!
                 */
                if(i>=1){
                //log error find two docs with same devicedID.
                break;
                }

                Document doc = cur.next();
                Integer count = doc.getInteger("count");    

                /**
                 * measure data has over 3 month. 
                 */
                if( count >= MongoTSConstants.queryDataLength ){

                     /**
                      * get the first one in the array
                      */
                     ArrayList<Document> obj = (ArrayList<Document>) doc.get("values");                  
                     Document doc1Elem = (Document) obj.get(0);

                     /**
                      * pull the first one in the array
                      */                     
                     db.getCollection(MongoTSConstants.tsDataInOneCollection).updateOne(where,
                             new Document("$pop", -1));
                     long ts = Calendar.getInstance().getTimeInMillis();

                     /**
                      * push the new one 
                      */
                     db.getCollection(MongoTSConstants.tsDataInOneCollection).updateOne(where,
                            new Document("$push", new Document("values",new Document(MongoTSConstants.tsFn,ts).
                                                                                 append(MongoTSConstants.measureDataFn,37.8))));
                     //Store doc1Elem in another history collection;                     
                     db.getCollection(MongoTSConstants.tsHistoryCollection).insertOne(doc1Elem);
                }else{
                    /**
                     * Measure data has not reach 3 month data 
                     */
                    long ts = Calendar.getInstance().getTimeInMillis();                     
                    db.getCollection(MongoTSConstants.tsDataInOneCollection).updateOne(where,
                            new Document("$push", new Document("values",new Document(MongoTSConstants.tsFn,ts).
                           append(MongoTSConstants.measureDataFn,37.9))).append("$inc", new Document(MongoTSConstants.countFn,1)));                     
                }          
                i++;
            }
        }       
    }  
}
公共类TSDesignMain{
公共静态void main(字符串[]args){
MongoClient mClient=新的MongoClient();
MongoDatabase db=mClient.getDatabase(MongoTSConstants.dbname);
long-tWarmingStart=System.nanoTime();
插入预热文件(db);
long-tWarmingEnd=System.nanoTime();
双时间升温=(双时间)(tWarmingEnd tWarmingStart)/1000/1000;
System.out.println(“升温数据库,每个事件持续时间插入10000个文档”+tDurationWarming+“ms”);
long tInsert1Dev1DocStart=System.nanoTime();
for(int j=1;j=MongoTSConstants.queryDataLength){
/**
*获取数组中的第一个
*/
ArrayList obj=(ArrayList)doc.get(“值”);
documentdoc1elem=(Document)对象get(0);
/**
*拉数组中的第一个
*/                     
db.getCollection(MongoTSConstants.tsDataInOneCollection).updateOne(其中,
新文件(“$pop”,-1));
long ts=Calendar.getInstance().getTimeInMillis();
/**
*推新的
*/
db.getCollection(MongoTSConstants.tsDataInOneCollection).updateOne(其中,
新文档($push),新文档(“值”,新文档(MongoTSConstants.tsFn,ts)。
追加(MongoTSConstants.measureDataFn,37.8));
//将文档存储在另一个历史记录集合中;
db.getCollection(MongoTSConstants.tsHistoryCollection).insertOne(doc1Elem);
}否则{
/**
*测量数据未达到3个月数据
*/
long ts=Calendar.getInstance().getTimeInMillis();
db.getCollection(MongoTSConstants.tsDataInOneCollection).updateOne(其中,
新文档($push),新文档(“值”,新文档(MongoTSConstants.tsFn,ts)。
追加(MongoTSConstants.measureDataFn,37.9))。追加($inc),新文件(MongoTSConstants.countFn,1));
}          
i++;
}
}       
}  
}

在我看来,目前很难做到这一点(目前mongodb的最新版本是v3.2),我不知道如何实现这一目标

解释 一、概述 此处有5个操作,您希望将它们合并为1个更新操作:

  • 按设备ID查询
  • 对计数的评估(计数<90或计数>=90?)
  • 将新数据推入值的尾部
  • 数组中弹出最早的数据(可选,基于步骤2)
  • 计数+1(可选,基于步骤2)
  • 让我们将它们分为两部分:查询部分更新部分

    二,。查询部分:合并查询(第一)和评估(第二)? 由于我不知道如何将评估放入更新语句的更新操作部分:

    db.test.update(
        {/* Query criteria */},
        {/* Update action */}
    )
    
    // We call this statement as UPDATE_STAT_2
    db.test.update(
        {device: 'deviceId1', count: {$gte: 90}},
        {/* Update action: push new data to values and pop the oldest*/}
    );
    
    db.test.update(
        {device: 'deviceId1', count: {$lt: 90}},
        {
            $push: {values: { "ts" : "1471077454988", "measureData" : 39 }},
            $inc : {count: 1}
        }
    );
    
    我采用了另一种方法-将评估放入查询条件部分,如下所示:

    // We call this statement as UPDATE_STAT_1
    db.test.update(
        {device: 'deviceId1', count: {$lt: 90}},
        {/* Update action: push new data to values and increase count */}
    );
    
    如果计数<90,您的示例文档将被匹配并执行更新操作(此时不关心更新操作是否成功)。 然后mongoDB会将执行结果返回给您:

    // Matched and updated
    WriteResult({ 
        "nMatched" : 1,
        "nUpserted" : 0, 
        "nModified" : 1 
    });
    
    结果告诉您一个文档已匹配并更新(nMatched=1&nModified=1)

    另一方面,如果计数>=90,则不会匹配和更新任何内容,执行结果将为:

    // Not Matched
    WriteResult({ 
        "nMatched" : 0, 
        "nUpserted" : 0, 
        "nModified" : 0 
    })
    
    因此,您将知道计数达到90,您应该执行另一个update语句:

    db.test.update(
        {/* Query criteria */},
        {/* Update action */}
    )
    
    // We call this statement as UPDATE_STAT_2
    db.test.update(
        {device: 'deviceId1', count: {$gte: 90}},
        {/* Update action: push new data to values and pop the oldest*/}
    );
    
    db.test.update(
        {device: 'deviceId1', count: {$lt: 90}},
        {
            $push: {values: { "ts" : "1471077454988", "measureData" : 39 }},
            $inc : {count: 1}
        }
    );
    
    总之,您可以先执行更新统计1,然后检查执行结果
    ,以决定是否需要像这样执行更新统计2(伪代码):

    这是我合并查询(第一)和评估(第二)的方法。我不知道如何将上述所有代码合并到一个语句中

    三、 更新部分:合并推送(第三次)/弹出(第四次)/增加(第五次)? 我已经在本地尝试过,您可以将增加放在一个语句中:

    db.test.update(
        {/* Query criteria */},
        {/* Update action */}
    )
    
    // We call this statement as UPDATE_STAT_2
    db.test.update(
        {device: 'deviceId1', count: {$gte: 90}},
        {/* Update action: push new data to values and pop the oldest*/}
    );
    
    db.test.update(
        {device: 'deviceId1', count: {$lt: 90}},
        {
            $push: {values: { "ts" : "1471077454988", "measureData" : 39 }},
            $inc : {count: 1}
        }
    );
    
    同时推(第三)和增加(第五)是合法的。但如果要推送和弹出阵列:

    db.test.update(
        {device: 'deviceId1', count: {$gte: 90}},
        {
            $pop: {values: -1},
            $push: {values: { "ts" : "1471077454911", "measureData" : 40 }}
        }
    );
    
    您将得到以下错误:

    WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
            "code" : 16837,
            "errmsg" : "Cannot update 'values' and 'values' at the same time"
        }
    })
    
    这意味着您不能同时对值执行push&pop操作。为此,我找到了这样的解释:

    问题是MongoDB不允许在服务器上执行多个操作 同一更新调用中的相同属性。这意味着