我正在编写一个基于GAE的应用程序,该应用程序应允许用户按其若干属性过滤项目。项目存储为NDB实体。一些道具可以通过标准查询过滤器进行匹配,但有些道具需要对整个事物进行“完整”(子串)文本搜索才有意义。另外,需要一些合理的排序。最好用以下设计的例子来说明:
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)
产品实体与其“容器”具有相同的祖先。产品可以属于一个或多个类别,后者构成一个树。
现在,用户应该能够:
所有这些同时,即。在提供搜索词时,应无缝应用过滤器和排序。
问题是:如何使用GAE以performat方式实现此类功能?
数据库中将有数十万甚至数百万的产品。与NDB查询一起使用时,Search API的问题是过滤搜索结果并可能对它们进行排序。
我一直在想的两个解决方案:
向StringProperty
模型添加重复的Product
,其中包含manufacturer
和model
字段中所有字词的所有可搜索子字符串(或至少前缀)。它很容易,但它很有效,但我非常关心性能。在我的实验中,我为每个“Product
”平均获得了40-50个可搜索的单词前缀。
使用高级搜索查询专门为该任务使用Search API。例如。我可以将产品的类别(作为ID或路径)存储在单独的文档字段中,并使用此字段来获取属于给定类别的产品。它可能已经完成,但我在这里关注的是10,000个搜索结果和各种使用限制/配额的限制。我也不确定结果的排序。
还有其他方法吗?
答案 0 :(得分:3)
我强烈建议不要使用GAE。我知道它可能不是您想要听到的,但它与您的用例不相符,并提供了我认为您希望从产品搜索中获得的灵活性。这听起来像你真正想要的更接近faceted search。
以下是GAE不匹配的原因:
如果您使用NDB,您将很快遇到索引爆炸的情况,否则可能会因Zig-zag查询而导致性能严重下降。 This article试图为您正在做的事情提供一个案例,但在实践中,我们发现它在我的日常工作中没有超出小数据集/少数字段。你引入其他过滤器的顺序越多,这会给你带来的问题就越多,更不用说你是否开始需要多种不等式。
与其他产品相比,GAE全文搜索速度缓慢且有限。查询语言本身并不是那么成熟和灵活的IMO。它的成本/配额也不是很友好。你提到你关心配额,搜索很容易通过它们吃。
子字符串方法会使您保存的每条记录的大小膨胀。 Django non-rel有一个indexer package就是这样,而且它并不漂亮。不确定你是否使用Django,但无论如何你可以调整代码作为它的开源。增加记录大小是不好的,因为除非您仅使用投影查询或密钥,否则您将通过网络发回大量不必要的数据。
相反,我建议您将数据推送到针对这类查询进行了更优化的数据存储。以下是架构的示例大纲:
在Google Compute Engine上搜索服务器以减少App Engine的延迟。不确定是否有办法让事情在同一个地理位置,但我怀疑你在这里托管它比在亚马逊的延迟方面更好。显然你可能会失去一些速度,但它可能仍然比内置的GAE全文搜索更快。
如果需要扩展群集,可以使用ElasticSearch。请注意,如果您执行此操作,则需要在Google计算引擎上正确设置多播。 ElasticSearch为此提供了一个插件。
根据数据量创建使用推送或拉取队列的后台进程,以更新搜索索引。频率将取决于"新鲜"你需要你的数据。推送与拉动的决定在很大程度上取决于您的音量,但我建议在此处使用拉取队列,并将专用服务器推送给您的搜索提供商。无论如何,您必须使用内置的全文搜索来执行此操作。
创建一个地图缩减作业,将所有数据推送到搜索索引。这对于播种初始队列和定期刷新都很有用。"
上面的缺点是,您将大幅增加您执行的URL提取调用的次数,并且数据可能并不总是新鲜的。后者在大多数搜索情况下都是正常的,前者可能比内置全文搜索更便宜,具体取决于您的音量。如果数据很少发生变化,您可以执行诸如转储到Google云端存储之类的操作,然后以相反的方式导入。