Как это сделать в 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 секунд, что является пределом времени запроса.
Тогда я подумал, что мне нужно ЗАКАЗАТЬ ПОЛЬЗОВАТЕЛЯ, чтобы быстрее пропустить дубликаты. Но это недопустимо, потому что я уже использую неравенство "t> = start_time AND t <= end_time". </p>
Кажется очевидным, что это не может быть выполнено менее чем за 30 секунд, поэтому его необходимо фрагментировать. Но поиск отдельных предметов, похоже, плохо разбивается на подзадачи. Лучшее, что я могу придумать, - это при каждом вызове cron найти 1000 событий просмотра страниц, а затем получить из них разные имена пользователей и поместить их в сущность, подобную Chard. Это может выглядеть примерно так:
class Chard(db.Model):
usernames = db.StringListProperty(required=True)
Таким образом, в каждом чарде будет до 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, но я создаю информационную панель для метрик, специфичных для приложений, и намереваюсь сделать это первой из многих статистических данных.