在Google App Engine上获取DISTINCT用户

时间:2010-01-29 14:09:43

标签: python google-app-engine scaling

如何在Google App Engine(Python)上执行此操作:

SELECT COUNT(DISTINCT user) FROM event WHERE event_type = "PAGEVIEW" 
AND t >= start_time AND t <= end_time

长版:

我有一个Python Google App Engine应用程序,用户可以生成事件,例如综合浏览量。我想知道在给定的时间跨度内有多少独特用户生成了一个综合浏览事件。我最感兴趣的时间是一周,在一周内有大约一百万个这样的事件。我想在一个cron工作中运行它。

我的活动实体如下所示:

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个事件,然后当这些事件耗尽时获得下一千个事件,依此类推。但这也不会起作用,因为通过一千个查询并检索一百万个实体将花费超过30秒,这是请求时间限制。

然后我认为我应该ORDER BY用户更快地跳过重复项。但这是不允许的,因为我已经在使用不等式“t&gt; = start_time AND t&lt; = end_time”。

很明显,这不可能在30秒内完成,因此需要进行分段。但是找到不同的项目似乎并没有很好地分解为子任务。我能想到的最好是在每个cron jobcall上找到1000个pageview事件,然后从这些事件中获取不同的用户名,并将它们放在像Chard这样的实体中。它可能看起来像

class Chard(db.Model):
    usernames = db.StringListProperty(required=True)

因此,每个chard中最多可包含1000个用户名,如果有重复项被删除则少。大约16个小时后(这很好)我会得到所有的chards并且可以做类似的事情:

chards = Chard.all()
all_usernames = set()
for chard in chards:
    all_usernames = all_usernames.union(chard.usernames)
answer = len(all_usernames)

它似乎可能有效,但几乎不是一个美丽的解决方案。有足够的独特用户,这个循环可能需要很长时间。我没有测试过,希望有人能提出更好的建议,所以如果这个循环变得足够快就不行了。

我的问题是否有更漂亮的解决方案?

当然,所有这些独特的用户计数都可以通过Google Analytics轻松完成,但我正在构建一个应用程序特定指标的信息中心,并打算将其作为众多统计数据中的第一个。

4 个答案:

答案 0 :(得分:4)

从SDK v1.7.4开始,现在有对DISTINCT函数的实验性支持。

请参阅:https://developers.google.com/appengine/docs/python/datastore/gqlreference

答案 1 :(得分:1)

Google App Engine及更具体的GQL不支持DISTINCT功能。

但您可以使用this博客和this SO问题中所述的Python set函数。

答案 2 :(得分:1)

这是一个可行的解决方案。它在某种程度上依赖于使用内存缓存,因此您的数据总是有可能以不可预测的方式被逐出。 注意事项。

你会有一个名为 unique_visits_today 的memcache变量或类似的东西。每当用户获得当天的第一次网页浏览时,您都会使用.incr()函数来递增该计数器。

通过查看附加到用户的 last_activity_day 字段来确定这是用户的第一次访问。当用户访问时,您查看该字段,如果是昨天,则将其更新为今天并增加您的内存缓存计数器。

每天午夜,cron作业将获取memcache计数器中的当前值并将其写入数据存储区,同时将计数器设置为零。你会有这样的模型:

class UniqueVisitsRecord(db.Model):
    # be careful setting date correctly if processing at midnight
    activity_date = db.DateProperty()
    event_count = IntegerProperty()

然后,您可以简单,轻松,快速地获取与任何日期范围匹配的所有UnqiueVisitsRecords,并在 event_count 字段中添加数字。

答案 3 :(得分:1)

NDB仍然不支持DISTINCT。我编写了一个小实用工具方法,可以使用与GAE的distinct。

见这里。 http://verysimplescripts.blogspot.jp/2013/01/getting-distinct-properties-with-ndb.html