9

我喜欢数据存储的简单性、可扩展性和易用性;并且在新的ndb库中发现的增强功能非常棒。

据我了解数据存储最佳实践,当匹配查询的项目数量很大时,不应编写代码来提供匹配查询结果的项目和/或页数;因为这样做的唯一方法是检索资源密集型的所有结果。

但是,在包括我们在内的许多应用程序中,通常希望查看匹配项的计数并为用户提供导航到这些结果的特定页面的能力。数据存储分页问题因需要解决fetch(limit, offset=X)的限制而变得更加复杂,如通过大数据集分页一文中所述. 为了支持推荐的方法,数据必须包含一个唯一值列,该列可以按照结果显示的方式进行排序。此列将为每页结果定义一个起始值;保存它,我们可以有效地获取相应的页面,允许根据请求导航到特定页面或下一页。因此,如果要显示以多种方式排序的结果,可能需要维护几个这样的列。

需要注意的是,从 SDK v1.3.1 开始,查询游标是推荐的数据存储分页方式。它们有一些限制,包括不支持 IN 和 != 过滤器运算符。目前我们的一些重要查询使用IN,但我们将尝试使用OR编写它们以用于查询游标。

遵循建议的指南,可以为用户提供(下一个)(上一个)导航按钮,以及导航进行时的特定页面按钮。例如,如果用户按(Next) 3 次,应用程序可以显示以下按钮,记住每个按钮的唯一起始记录或光标以保持导航效率:(Prev) (Page-1) (Page-2) (Page -3)(第 4 页)(下一个)

一些人建议单独跟踪计数,但是当允许用户查询一组丰富的字段以改变返回的结果时,这种方法是不切实际的。

我正在寻找有关这些问题的一般见解,特别是以下问题:

  1. 您在数据存储应用程序中提供哪些查询结果导航选项来解决这些限制?

  2. 如果为用户提供有效的结果计数和整个查询结果集的页面导航是一个优先事项,那么应该放弃使用数据存储,转而支持现在提供的GAE MySql 解决方案。

  3. 大表架构或数据存储实现中是否有任何即将发生的变化,这些变化将为有效计算查询结果提供额外的功能?

非常感谢您的帮助。

4

4 回答 4

2

这完全取决于您通常会得到多少结果。例如,通过传递 .count() 一个合适的限制,如果#items 为例如 <= 100,则您可以提供准确的计数,如果有更多,则可以提供“许多”。听起来您无法预先计算所有可能的计数,但至少您可以缓存它们,从而节省许多数据存储操作。

使用 NDB,最有效的方法可能是使用 fetch_page() 请求实体的第一页,然后使用生成的游标作为 count() 调用的起点;或者,您最好使用其异步工具同时运行第一页的 fetch() 和 count()。如果您的查询不支持游标,则第二个选项可能是您唯一的选择。大多数 IN / OR 查询当前不支持游标,但如果您按__key__.

在 UI 选项方面,我认为提供下一页和上一页选项就足够了;可以跳过几页的“Gooooooogle”用户界面很可爱,但我自己几乎从不使用它。(要实现“上一页”,请颠倒查询的顺序并使用与当前页相同的光标。我很确定这可以保证工作。)

于 2012-02-23T05:06:22.630 回答
1

也许只是针对这种分页风格:

(第一页)(上一页)(第一)(第二页)(第三页)....(最后一页)(下一页)

这样就不需要总数 - 您只需要您的代码就知道有足够的结果可用于另外 3 个以上的页面。页面大小为每页 10 个项目,您只需要知道有 30 多个项目。

如果您已经在第 4 页上时有 60 个项目(足够 6 页),您的代码会向前看并意识到只有另外 20 条记录要走,所以您可以显示最后一个页码:

(第一页)(上一页)(第四页)(第五页)(第六页)(下一页)(最后一页)

基本上对于当前页面的每次获取,只需为另外 3 页数据获取足够的记录,计算它们以查看您实际拥有的页面数量,然后相应地显示您的寻呼机。

此外,如果您只是获取密钥,它将比获取额外的项目更有效。希望这是有道理的!???:)

于 2012-02-28T14:47:43.917 回答
0
  1. 我注意到 gmail 已经准备好进行一些计数 - 它可以告诉您您收到的电子邮件总数,以及您的收件箱中有多少封邮件等 - 但在其他方面,例如它说您正在查看的全文搜索“1-20 of many”或“1-20 of about 130”。您真的需要显示每个查询的计数,还是可以只预先计算重要的?
于 2012-02-22T17:32:18.103 回答
0

由于问题是“寻找提供页面的想法/替代方案”,因此可能非常简单的替代方案是获取 10 个页面的 key_only 项目,然后在该集合中处理导航是值得考虑的。

我在回答类似问题时对此进行了详细说明,您会在那里找到示例代码:

带有光标的向后分页正在工作但缺少一个项目

示例代码更适合这个问题。这是其中的一部分:

def session_list():
    page = request.args.get('page', 0, type=int)

    sessions_keys = Session.query().order(-Session.time_opened).fetch(100, keys_only=True)
    sessions_keys, paging = generic_list_paging(sessions_keys, page)
    # generic_list_paging will select the proper sublist.
    sessions = [ sk.get() for sk in sessions_keys ]

    return render_template('generic_list.html', objects=sessions, paging=paging)

有关更多代码,请参阅引用的问题。

当然,如果结果集可能很大,仍然必须对获取进行一些限制,我认为硬限制是 1000 项。显然,如果结果超过 10 页,则会要求用户通过添加标准来进行细化。

在数百个 keys_only 项目中处理分页确实要简单得多,这绝对值得考虑。正如问题中提到的那样,它可以很容易地提供直接页面导航。实际的实体项仅针对实际的当前页面获取,其余的只是键,因此成本不高。并且您可以考虑将 keys_only 结果集保留在 memcache 中几分钟,以便快速浏览页面的用户不需要再次执行相同的查询。

于 2015-04-10T15:04:42.713 回答