SELECT COUNT(DISTINCT user) FROM event WHERE event_type = "PAGEVIEW"
AND t >= start_time AND t <= end_time
长篇版本:
我有一个使用Python Google App Engine编写的应用程序,其中用户生成事件,例如页面浏览。我想知道在给定时间段内有多少独特用户生成了页面浏览事件。我最感兴趣的时间跨度是一周,在一个给定的星期中大约有一百万这样的事件。我想在计划任务中运行此操作。
我的事件实体如下:
class Event(db.Model):
t = db.DateTimeProperty(auto_now_add=True)
user = db.StringProperty(required=True)
event_type = db.StringProperty(required=True)
使用SQL数据库,我会这样做:
SELECT COUNT(DISTINCT user) FROM event WHERE event_type = "PAGEVIEW"
AND t >= start_time AND t <= end_time
首先想到的是获取所有PAGEVIEW事件并过滤掉重复的用户。大致如下:
query = Event.all()
query.filter("t >=", start_time)
query.filter("t <=", end_time)
usernames = []
for event in query:
usernames.append(event.user)
answer = len(set(usernames))
但这样做是行不通的,因为它只支持最多1000个事件。接下来我想到的是先获取1000个事件,然后当这些用完时再获取下一个1000个事件,以此类推。但这也不可行,因为查询1000次并检索一百万个实体需要超过30秒,而这已经达到了请求时间限制。
然后我想到按用户排序以更快地跳过重复项。但由于我已经使用了不等式“t >= start_time AND t <= end_time”,所以这是不允许的。
显然,在30秒内无法完成这项任务,因此需要进行分段处理。但似乎找到不同的项目不太容易分成子任务。我能想到的最好的方法是在每次cron job调用中查找1000个页面浏览事件,然后从中获取不同的用户名,并将它们放入像Chard这样的实体中。它可能看起来像:
class Chard(db.Model):
usernames = db.StringListProperty(required=True)
因此,每个字符最多可以有1000个用户名,如果有重复的用户名被删除,则少于1000个。经过约16个小时(这很好),我将获得所有的字符,然后可以执行以下操作:
chards = Chard.all()
all_usernames = set()
for chard in chards:
all_usernames = all_usernames.union(chard.usernames)
answer = len(all_usernames)
似乎这个解决方案可行,但不是一个优美的解决方案。如果唯一用户足够多,这个循环可能会花费太长时间。我希望有人能提出更好的建议来避免测试这个循环是否足够快。
有没有更漂亮的解决方案?
当然,所有这些唯一用户计数都可以轻松地使用Google Analytics完成,但我正在构建一个应用程序特定度量的仪表板,并打算将其作为许多统计数据的第一个。