python - 在 GAE 中组合文本搜索和查询过滤器

标签 python google-app-engine search full-text-search app-engine-ndb

我正在编写一个基于 GAE 的应用程序,该应用程序应该允许用户根据项目的多个属性来过滤项目。项目存储为 NDB 实体。一些 Prop 可以通过标准查询过滤器匹配,但有些 Prop 需要“完整”(子字符串)文本搜索才能使整个事情变得有意义。此外,还需要一些合理的排序。下面这个人为的例子或许可以最好地说明这一点:

class Product(ndb.Model) :
  manufacturer = ndb.StringProperty()
  model = ndb.StringProperty()
  rating = ndb.IntegerProperty(choices = [1, 2, 3, 4])
  features = ndb.StringProperty(repeated = True, choices = ['feature_1', 'feature_2'])
  is_very_expensive = ndb.BooleanProperty()
  categories = ndb.KeyProperty(kind = Category, repeated = True)

产品实体都具有与其“容器”相同的祖先。一个产品可以属于一个或多个类别,后者形成一棵树。

现在,用户应该能够:

  • 通过选择一个类别来缩小产品范围(一个就足够了)
  • 通过指定最低评分和所需功能来过滤它们
  • 只查看非常昂贵或不昂贵的产品(或查看全部)
  • 按型号和/或制造商字段中的一段文本搜索产品
  • 订购最终 list ,例如。按型号名称(不过,能够选择顺序是最理想的)。

所有这一切同时进行,即。提供搜索词时,应无缝应用过滤器和排序。

问题是:如何使用 GAE 以高性能的方式实现这样的功能?

数据库中将有数十万甚至数百万种产品。与 NDB 查询一起使用时,搜索 API 的问题在于过滤搜索结果并可能对它们进行排序。

我一直在想的两个解决方案:

  1. 将重复的 StringProperty 添加到 Product 模型,该模型将包含来自 manufacturer 的所有可搜索子字符串(或至少是前缀) > 和 model 字段。这很简单而且有效,但我非常关心性能。在我的实验中,每个“Product”平均有 40-50 个可搜索词前缀。

  2. 将搜索 API 专门用于任务,利用高级搜索查询。例如。我可以将产品类别(作为 ID 或路径)存储在单独的文档字段中,并使用此字段获取属于给定类别的产品。它可能可以完成,但我关心的是 10,000 个搜索结果的限制和各种使用限制/配额。我也不确定结果的顺序。

还有其他方法吗?

最佳答案

我强烈建议不要为此使用 GAE。我知道这可能不是您想听到的,但它与您的用例不太匹配,也不能提供我认为您希望从产品搜索中获得的灵 active 。听起来您真正想要的是更接近 faceted search 的东西.

以下是 GAE 不适合的原因:

  1. 如果您使用 NDB,您将很快遇到索引爆炸的情况,否则可能会因之字形查询而导致性能严重下降。 This article试图证明你在做什么,但在实践中我们发现它在我的日常工作中并没有扩展到小数据集/少数领域之外。您在其他过滤器中引入的顺序越多,给您带来的问题就越多,更不用说您是否开始需要各种不等式了。

  2. 与其他产品相比,GAE 全文搜索速度慢且功能有限。查询语言本身并不是那么成熟和灵活的 IMO。它对成本/配额也不是很友好。您提到您担心配额,而搜索很容易吞噬它们。

  3. 子字符串方法会增大您保存的每条记录的大小。 Django non-rel 有一个 indexer package正是这样做的,而且它并不漂亮。不确定您是否使用 Django,但无论如何您都可以修改代码,因为它是开源的。增加记录大小是不好的,因为除非您只使用投影查询或键,否则您将通过网络发回大量不必要的数据。

相反,我建议您将数据推送到针对此类查询进行了更优化的数据存储。这是架构的示例大纲:

  • 在 Google Compute Engine 上搜索服务器以减少 App Engine 的延迟。不确定是否有办法让事物位于相同的地理位置,但我怀疑就延迟而言,你最好在这里托管它而不是亚马逊。显然你在这里可能会损失一些速度,但它可能仍然比内置的 GAE 全文搜索更快。

  • 如果您需要一个集群来扩展,您可以使用 ElasticSearch。请注意,如果您这样做,您将需要在谷歌计算引擎上正确设置多播。 ElasticSearch 为此提供了一个插件。

  • 创建一个后台进程,根据您的数据量使用推送或拉取队列来更新您的搜索索引。频率将取决于您需要的数据有多“新鲜”。推还是拉的决定在很大程度上取决于您的数量,但我建议在这里使用拉队列,使用专用服务器推送到您的搜索提供商。无论如何,您都必须使用内置的全文搜索来执行此操作。

  • 创建一个 map reduce 作业,将所有数据推送到搜索索引。这对于播种初始队列和定期“刷新”都很有用。

上述方法的缺点是您将大大增加您执行的 URL 提取调用的次数,并且数据可能并不总是最新的。后者在大多数搜索情况下是正常的,前者可能仍然比内置的全文搜索便宜,具体取决于您的数量。如果数据很少更改,您可以执行转储到 Google Cloud Storage 等操作,然后以更便宜的方式导入。

关于python - 在 GAE 中组合文本搜索和查询过滤器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22426862/

相关文章:

python - 使用 pip 安装一个包的多个版本

java - 关于使用什么平台和技术的建议 : distributed bookkeeping app

java - 将通过 Android Studio 生成的 GAE Endpoints 带到 Eclipse

php - php mysql中的多个关键字搜索

search - 为什么我的同义词什么都不返回?

Python - 使用网格布局来格式化窗口

python - PyPI - 包不包含文件

python - 在 Django 模板中循环字典的字典只需 1 行代码

mysql - Sphnix index_exact_words 不忽略停用词

python - cx_Oracle 数据库清理过度减慢查询