在Google App Engine上获取唯一用户

4
如何在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编写的应用程序,其中用户生成事件,例如页面浏览。我想知道在给定时间段内有多少独特用户生成了页面浏览事件。我最感兴趣的时间跨度是一周,在一个给定的星期中大约有一百万这样的事件。我想在计划任务中运行此操作。

我的事件实体如下:

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完成,但我正在构建一个应用程序特定度量的仪表板,并打算将其作为许多统计数据的第一个。

能够拥有滚动周是否重要?也就是说,从任何给定日期开始,过去的七天,或者在日历周中计数是否可以呢? - Adam Crossland
你有考虑过使用Analytics Feed API将数据从分析中取出吗? http://code.google.com/apis/analytics/docs/gdata/gdataReferenceDimensionsMetrics.html - Adam Crossland
我猜你想建议添加一个像“calendar_week”这样的字段,这样我就可以做SELECT stuff WHERE calendar_week = some_week?那将是一个解决方案,也许我可以在时间戳之外再加上它,这样以后我仍然可以做任何时间跨度。不过,我很好奇是否还有其他更灵活的解决方案。至于第二点,我并不想从分析中获取我的数据,因为我需要执行Analytics不支持的其他任务,这些任务需要我在数据存储中拥有的信息。例如,需要一组唯一用户来制作行为直方图等。 - Bemmu
对于第一个问题,我认为您可以创建一个分片计数器,记录每周的活动情况。您可以通过为每个用户保留“last_activity”日期字段来决定是否增加计数器。当用户有活动时,如果他们的上次活动时间超过一周,您可以增加当前周的分片计数器。 - Adam Crossland
通过Google Analytics Feed的建议,我认为您可以使用该Feed将数据集成到您的仪表板中,而无需直接访问Analytics,它可以与应用程序本身收集和管理的数据并存。我提出这个建议主要是因为只有AppEngine资源来处理这种任务的选项将非常有限且难以实现。 - Adam Crossland
4个回答

4

1

这里是一个可能可行的解决方案。它在一定程度上依赖于使用memcache,因此您的数据可能会以不可预测的方式被清除。买家自负。

您将拥有一个名为unique_visits_today或类似名称的memcache变量。每当用户在当天首次访问页面时,您将使用.incr()函数来增加该计数器。

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

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

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

您可以轻松快速地获取与任何日期范围匹配的所有UniqueVisitsRecords,并累加它们event_count字段中的数字。

1
这取决于您的值在memcache中保留整整一天。 memcache是缓存,而不是可靠的存储;如果您乐意一直失去计数,那么这只是一个好答案。 - Wooble

1

0

Google App Engine和更具体的GQL不支持DISTINCT函数。

但是,您可以使用Python的set函数,如this博客中所述以及thisSO问题中所述。


谢谢。我知道那个SO问题和博客文章,但由于任务的规模,它们不适用于此情况。 - Bemmu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接