使用 ndb 聚合数据

52 阅读2分钟

我们需要聚合 NDB 数据存储中的数据。具体来说,我们需要创建一个 AggregateStatisticStore 模型来存储每天的 StatisticStore 聚合。StatisticStore 模型存储用户访问的链接的信息,而 AggregateStatisticStore 模型存储每天访问链接的总数和前 N 个最常访问的链接。

我们希望 AggregateStatisticStore 模型能够提供以下功能:

  • 能够按日期查询聚合数据。
  • 能够按用户查询聚合数据。
  • 能够轻松地更新聚合数据。

我们考虑了使用 Task Queue API 来生成 AggregateStatisticStore 模型。但是,我们担心这可能会遇到内存问题,因为 StatisticStore 模型可能会有很多记录。

2、解决方案

为了解决这个问题,我们可以使用以下方法:

  1. 使用 MapReduce 来生成 AggregateStatisticStore 模型。MapReduce 是一个分布式计算框架,可以将一个大任务分解成多个小任务,然后在多个机器上并行执行这些小任务。这可以有效地减少内存使用量,并提高生成 AggregateStatisticStore 模型的速度。

  2. 使用 Backend Instances 来生成 AggregateStatisticStore 模型。Backend Instances 是独立于应用程序实例的计算实例。它们可以用于执行长时间运行的任务,而不影响应用程序的性能。

  3. 将聚合过程移到写入时间。这意味着每当创建一个 StatisticStore 记录时,我们也会更新相应的 AggregateStatisticStore 记录。这可以减少在生成 AggregateStatisticStore 模型时需要处理的数据量。

此外,我们建议使用 IntegerProperty 来存储日期,而不是 DateProperty。IntegerProperty 可以存储整数,而 DateProperty 只能存储日期。这可以简化查询并提高性能。

代码例子:

from google.appengine.ext import ndb
from google.appengine.ext importmapreduce
from google.appengine.api import taskqueue


class StatisticStore(ndb.Model):
  user = ndb.KeyProperty(kind='User')
  created = ndb.DateTimeProperty(auto_now_add=True)
  kind = ndb.StringProperty()
  properties = ndb.PickleProperty()


class AggregateStatisticStore(ndb.Model):
  user = ndb.KeyProperty(kind='User')
  date = ndb.IntegerProperty()
  kinds_count = ndb.PickleProperty()
  top_links = ndb.PickleProperty()


def generate_aggregate_statistic_store(start_date, end_date):
  """
  Generates an AggregateStatisticStore for the given date range.

  Args:
    start_date: The start date of the date range.
    end_date: The end date of the date range.
  """
  stats = StatisticStore.query(
    StatisticStore.created >= start_date,
    StatisticStore.created <= end_date
  )

  links_dict = {}
  # generate links_dict from stats
  # keys are from the 'properties' property

  aggregated_stat = AggregateStatisticStore(
    user=stats[0].user,
    date=start_date.date(),
    kinds_count=kinds_count,
    top_links=links_dict
  )

  aggregated_stat.put()


def update_aggregate_statistic_store(statistic_store):
  """
  Updates the AggregateStatisticStore for the given StatisticStore.

  Args:
    statistic_store: The StatisticStore to update the aggregate for.
  """
  date = statistic_store.created.date()

  aggregate_stat = AggregateStatisticStore.query(
    AggregateStatisticStore.user == statistic_store.user,
    AggregateStatisticStore.date == date
  ).get()

  if aggregate_stat is None:
    generate_aggregate_statistic_store(date, date)
    return

  # update aggregate_stat with statistic_store

  aggregate_stat.put()


@ndb.transactional
def record_statistic(user, kind, properties):
  """
  Records a statistic for the given user.

  Args:
    user: The user to record the statistic for.
    kind: The kind of statistic to record.
    properties: The properties of the statistic.
  """
  statistic_store = StatisticStore(
    user=user.key,
    kind=kind,
    properties=properties
  )

  statistic_store.put()

  update_aggregate_statistic_store(statistic_store)


def cleanup_old_statistic_stores():
  """
  Deletes StatisticStore records that are older than 30 days.
  """
  date = datetime.datetime.now() - timedelta(days=30)

  StatisticStore.query(
    StatisticStore.created < date
  ).delete_multi()


taskqueue.add(
  url='/worker',
  params={
    'start_date': '2013-08-22',
    'end_date': '2013-08-22'
  }
)