我使用MongoDB数据库来跟踪应用程序的分析。我正在写一个Clojure应用程序(使用clj-time和Monger)从数据库中获取数据。
我有一个包含如下记录的集合
{"_id": ObjectId(...),
timestamp: ISODate("2013-06-01T15:18:37Z"),
device: "04dbf04b6dc0d0a4fd383967b3dc62f50111e07e"}
每个不同的device
代表我的服务的不同用户。我想做的是找出我每天有多少(唯一的)用户,但需要注意的是,我希望"day"特别指的是美国/中部时区,考虑到夏令时。(如果这不是一个要求,我想我可以只是做一些像$group
,然后distinct
。)
这是我一直在做的:
(ns analytics.reporting
(:use [monger.core :only [connect! connect set-db! get-db]]
monger.operators
clj-time.core
clj-time.periodic
clj-time.format)
(:require [monger.collection :as mc]))
(defn to-central
[dt]
(from-time-zone dt (time-zone-for-id "America/Chicago")))
(defn count-distinct
[coll]
(count (distinct coll)))
(defn daily-usage
[ndays]
(let [midnights (map to-central
(reverse (for [offset (map days (range ndays))]
(minus (to-central (today-at 0 0)) offset))))
by-day (for [midnight midnights]
(mc/find-maps "devices" {:timestamp {$gte midnight $lt (plus midnight (days 1))}}))
devices-by-day (map #(map :device %) by-day)
distinct-devices-by-day (map count-distinct devices-by-day)]
distinct-devices-by-day))
如果你不能读Clojure,这基本上是说:获得一个在中央时区最近的n午夜的列表,然后运行Mongo查询来找到每一对连续的午夜之间的所有记录。然后,统计每天不同device
s的个数。
我不喜欢这种方法的地方:
- 每天运行一个单独的查询(我通常一次看30天)感觉不对;这应该在数据库端而不是应用端完成。
- 计算不同的
device
也应该由数据库完成。 - 我的服务器设置为UTC时区,所以如果它在UTC午夜之后,但在中央时间午夜之前,这个列表中的最后一个条目将始终为零。这很容易修补,但我更喜欢一个足够聪明的解决方案,以防止它在第一个地方。
- 整个函数的运行时间约为500ms。这并不可怕——我是唯一运行查询的人,每天只有一两次——但看起来操作不应该花那么长时间。
有一种方法,我可以把更多的这个逻辑到MongoDB查询?
正如@WiredPrairie所建议的那样,在将每条记录添加到数据库时,我最终只在其中包含中央时间日期。然后,我可以使用一个简单的$group
查询来收集每个日期的记录数。