聚合查询调整时区日期

Aggregation Query adjusting dates for timezone

我想运行这个mongodb查询使用java代码。

查询:

db.log.aggregate([
    { $match : { 
        "ts" : { 
            $gte: ISODate("2015-07-31T18:30:00.000Z"),
            $lt: ISODate("2015-08-01T18:30:00.000Z")  
        },
        "dup": {$exists:false} 
    }},
    { $project : { 
        'lts': { 
            '$add': ['$ts',5.5*3600*1000]
        }
    }},
    { $group : { 
        _id : { 
            day: { $dayOfMonth: "$lts" },
            month: { $month: "$lts" },
            year: { $year: "$lts" }
        },
        count: { $sum: 1 }
    }}
])

我已经尝试使用此代码,但它不起作用

                                                                                    DBObject query = QueryBuilder.start().put("ts").greaterThanEquals(startdate).lessThanEquals(enddate).and("dup").exists(false).get();        
     DBObject match = new BasicDBObject("$match", query); 
     DBObject project=new BasicDBObject("$project", new BasicDBObject("ts",new BasicDBObject("$add",5.5*3600*1000)));
    DBObject group = new BasicDBObject("$group", new BasicDBObject("_id",new BasicDBObject("day", new BasicDBObject("$dayOfMonth", "$ts"))
    ).append("count", new Document("$sum", 1)));

    AggregationOutput output = collection.aggregate(match,group);

    for (DBObject result : output.results()) {
         System.out.println(result);
         }

您的初始查询可以写得更好。据推测,您希望按 "day" 对结果进行分组,同时还从 UTC 调整到本地 timzezone。通常最好坚持使用日期数学而不是混入 date aggregation operators in such a case, and also this means that the $project here is not required, and you can just go straight to $group:

db.log.aggregate([
  { "$match": { 
      "ts": { 
        "$gte": ISODate("2015-07-31T18:30:00.000Z"),
        "$lt": ISODate("2015-08-01T18:30:00.000Z")  
      },
      "dup": { "$exists": false } 
  }},
  { "$group": {
    "_id": {
      "$add": [
        { "$subtract": [
          { "$subtract": [
            { "$add": [ "$ts", 5.5*1000*60*60 ] },
            new Date(0)
          ]},
          { "$mod": [
            { "$subtract": [
              { "$add": [ "$ts", 5.5*1000*60*60 ] },
              new Date(0)
            ]},
            1000*60*60*24
          ]}
        ]},
        new Date(0)
      ]
    },
    "count": { "$sum": 1 }
  }}
])

请注意其中的附加 Date(0) 语句。这是 "epoch" 就像你 $add a numeric value to a date you get a Date type in return, when you $subtract 你得到整数值一样。因此,通过添加回 "epoch" 进行更正会使所有内容再次成为 Date 类型。

因此 "date math" 到处都被使用,到 "round" 到 "day":

( 1000 millseconds * 60 seconds * 60 minutes * 24 hours )

然后模 $mod 运算符计算出从当前日期值中减去的余数,得出 "rounded" 日期。

全部在 $group 内完成并且效率很高:

翻译成 Java 只是遵循相同的缩进:

Date startdate = new DateTime(2015, 7, 31, 18, 30, 0, DateTimeZone.UTC).toDate();
Date enddate = new DateTime(2015, 8, 1, 18, 30, 0, DateTimeZone.UTC).toDate();

DBObject query = QueryBuilder.start()
        .put("ts").greaterThanEquals(startdate)
        .lessThan(enddate)
        .and("dup").exists(false).get();

DBObject match = new BasicDBObject("$match",query);


DBObject group = new BasicDBObject("$group",
  new BasicDBObject("_id",
    new BasicDBObject(
      "$add", Arrays.asList(
        new BasicDBObject(
          "$subtract", Arrays.asList(
            new BasicDBObject(
             "$subtract", Arrays.asList(
                new BasicDBObject(
                  "$add",Arrays.asList( "$ts", 5.5*1000*60*60 )
                ),
                new Date(0)
              )
            ),
            new BasicDBObject(
              "$mod", Arrays.asList(
                new BasicDBObject(
                 "$subtract", Arrays.asList(
                    new BasicDBObject(
                      "$add",Arrays.asList( "$ts", 5.5*1000*60*60 )
                    ),
                    new Date(0)
                  )
                ),
                1000*60*60*24
              )
            )
          )
        ),
        new Date(0)
      )
    )
  )
  .append("count", new BasicDBObject("$sum",1))
);


AggregationOutput output = collection.aggregate(Arrays.asList(match, group));

for ( DBObject result : output.results() ) {
    System.out.println(result);
}

另请注意,您的 "timezeone adjustment" 此处假设 5.5 小时 "behind" UTC,我希望这是正确的。如果您的意思是 "after" 或正数,则该操作是 "subtraction" 而不是将 UTC 小时数更正为天数的附加操作。

所以一切都很好,您的分组键也是真实的 Date 对象,而不是日期聚合运算符返回的复合值。

将字段 ts 更改为东 8 时区:

    BasicDBObject addEightHour = new BasicDBObject("$add", Arrays.asList( "$ts", 8*1000*60*60 ));
    final BasicDBObject day = new BasicDBObject("$dateToString", new BasicDBObject("format", "\"%Y%m%d\"").append("date", addEightHour));

    DBObject groupFields = new BasicDBObject("_id", new BasicDBObject(
            "value", day));
    DBObject group = new BasicDBObject("$group", groupFields);