python - 排序实体和过滤 ListProperty 而不会导致索引 explode

标签 python google-app-engine indexing google-cloud-datastore explode

我正在开发一个简单的博客/书签平台,我正在尝试添加一个tags-explorer/drill-down 功能 là delicious允许用户过滤指定特定标签列表的帖子。

是这样的: enter image description here

帖子在数据存储中用这个简化模型表示:

class Post(db.Model):
    title = db.StringProperty(required = True)
    link = db.LinkProperty(required = True)
    description = db.StringProperty(required = True)
    tags = db.ListProperty(str)
    created = db.DateTimeProperty(required = True, auto_now_add = True)

帖子的标签存储在 ListProperty 中并且,为了检索带有特定标签列表的帖子列表,Post 模型公开了以下静态方法:

@staticmethod
def get_posts(limit, offset, tags_filter = []):
        posts = Post.all()
        for tag in tags_filter:
          if tag:
              posts.filter('tags', tag)
        return posts.fetch(limit = limit, offset = offset)

虽然我没有过分强调它,但效果很好。

当我尝试向 get_posts 方法添加“排序”顺序以保持结果按 "-created" 日期排序时,问题出现了:

@staticmethod
def get_posts(limit, offset, tags_filter = []):
        posts = Post.all()
        for tag in tags_filter:
          if tag:
              posts.filter('tags', tag)
        posts.order("-created")
        return posts.fetch(limit = limit, offset = offset)

排序顺序为每个要过滤的标签添加一个索引,导致可怕的索引 explode 问题。
最后一件让这件事变得更复杂的事情是 get_posts 方法应该提供一些分页机制。

您知道解决此问题的任何策略/想法/解决方法/技巧吗?

最佳答案

Queries involving keys use indexes just like queries involving properties. Queries on keys require custom indexes in the same cases as with properties, with a couple of exceptions: inequality filters or an ascending sort order on key do not require a custom index, but a descending sort order on Entity.KEY_RESERVED_PROPERTY_key_ does.

因此使用可排序的日期字符串作为实体的主键:

class Post(db.Model):
    title = db.StringProperty(required = True)
    link = db.LinkProperty(required = True)
    description = db.StringProperty(required = True)
    tags = db.ListProperty(str)
    created = db.DateTimeProperty(required = True, auto_now_add = True)

    @classmethod
    def create(*args, **kw):
         kw.update(dict(key_name=inverse_millisecond_str() + disambig_chars()))
         return Post(*args, **kw)

...

def inverse_microsecond_str(): #gives string of 8 characters from ascii 23 to 'z' which sorts in reverse temporal order
    t = datetime.datetime.now()
    inv_us = int(1e16 - (time.mktime(t.timetuple()) * 1e6 + t.microsecond)) #no y2k for >100 yrs
    base_100_chars = []
    while inv_us:
        digit, inv_us = inv_us % 100, inv_us / 100
        base_100_str = [chr(23 + digit)] + base_100_chars
    return "".join(base_100_chars)

现在,您甚至不必在查询中包含排序顺序,尽管显式按键排序不会有什么坏处。

要记住的事情:

  • 除非您在此处为所有帖子使用“创建”,否则这将不起作用。
  • 您必须迁移旧数据
  • 不允许有祖先。
  • 键在每个索引中存储一次,因此保持简短是值得的;这就是我在上面进行 base-100 编码的原因。
  • 这不是 100% 可靠,因为可能会发生键冲突。上面的代码,没有 disambig_chars,名义上给出了事务之间微秒数的可靠性,所以如果你在高峰时间每秒有 10 个帖子,它会失败 1/100,000。但是,对于可能的应用程序引擎时钟滴答问题,我会削减几个数量级,所以我实际上只相信它的 1/1000。如果这还不够好,请添加 disambig_chars;如果您需要 100% 的可靠性,那么您可能不应该使用 App Engine,但我想您可以在 save() 上包含处理键冲突的逻辑。

关于python - 排序实体和过滤 ListProperty 而不会导致索引 explode ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6108051/

相关文章:

python - 将两个字符串与一个公共(public)子字符串连接起来?

python - 无法使用 json.loads 将字符串加载到对象中

java - 如何避免Google App Engine不支持Runtime.addShutdownHook

java - com.mysql.jdbc.exceptions.jdbc4.CommunicationsException :

python - 如何识别数据框中具有连续索引的子集

mysql - SQL 边界框优化

python - 在python中按天对时间戳字符串进行分组

python - 缓冲区错误 : Local: Queue full in Python

google-app-engine - App Engine 的过滤器与 gql 方法

mysql - 从非常大的 Mysql 数据库中获取 'text' 列字段的行