寻找提供与GAE数据存储查询匹配的项目的页面/项目计数/导航的想法/替代方案

时间:2012-02-22 07:31:03

标签: google-app-engine app-engine-ndb google-cloud-datastore

我喜欢数据存储区的简单性,可扩展性和易用性;新ndb库中的增强功能非常棒。

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

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

应该注意的是,从SDK v1.3.1开始,Query Cursors是推荐的数据存储分页方式。它们有一些限制,包括缺乏对IN的支持和!=过滤运营商。目前,我们的一些重要查询使用 IN ,但我们会尝试使用 OR 编写它们,以便与查询游标一起使用。

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

有些人建议分别跟踪计数,但是当允许用户查询会改变返回结果的丰富字段时,这种方法是不实际的。

我正在寻找有关这些问题的见解以及以下问题:

  1. 您在数据存储区应用中提供了哪些搜索结果的导航选项来解决这些限制?

  2. 如果为用户提供有效的结果计数和页面导航 整个查询结果集的优先级,应该使用数据存储区 被抛弃,赞成现在提供的GAE MySql solution

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

  4. 非常感谢您的帮助。

4 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:1)

也许只是针对这种分页方式:

(第一)(PREV)(第1页)(第二页)(第3页)....(最后)(下)

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

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

(第一)(PREV)(第4页)(第5页)(第6页)(下)(最后)

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

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

答案 2 :(得分:0)

  1. 我注意到gmail已经准备好了一些 - 它可以告诉你收到的电子邮件总数,收件箱中有多少,等等 - 但在其他方面,比如全文搜索它会说你'重新审视“1-20 of many”或“1-20 of about 130”。您真的需要为每个查询显示计数,还是只能预先计算重要的查询?

答案 3 :(得分:0)

由于问题是"寻找提供页面的想法/替代方案",也许是获取10页值key_only项目的非常简单的替代方案,然后在此集合中处理导航是值得考虑的。 / p>

我在回答类似的问题时已经详细说明了,你会在那里找到示例代码:

Backward pagination with cursor is working but missing an item

示例代码更适合此问题。这是它的一部分:

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中几分钟,以便用户快速浏览页面不需要再次执行相同的查询。