我遇到以下情况:
我们目前通过商业解决方案实现了产品搜索。 我正在玩Elasticsearch来实现我们目前使用Elasticsearch的产品搜索,它基本上非常好用。 但我们有一个专业。我们有大约100万种产品的产品目录,但不是每个客户都可以购买每种产品。 有许多规则定义了客户是否可以购买产品。
这不仅仅是:
客户A不得购买供应商A的产品
或者:
客户B不得购买供应商B的B类产品。
这很容易。
为了获得客户不允许购买的这些产品,我们在几年前实施了微服务/网络服务。此Web服务返回产品黑名单,只是产品编号列表。
问题在于,如果我只是在Elasticsearch中运行查询而忽略这些列入黑名单的产品,我会收回客户不允许购买的产品。如果我查询前10个搜索命中只会发生,我不允许显示这些产品,因为客户不允许购买它们。 此外,如果我使用供应商和类别的聚合,我会收回供应商和/或类别,客户可能不被允许购买。
我在原型中做了什么?
在查询Elasticsearch之前,我请求某个客户的产品黑名单(当然还要缓存它)。在我收到黑名单后,我运行了这样的查询:
{
"query" : {
"bool" : {
"must_not" : [
{
"ids" : {
"values" : [
// Numbers of blacklisted products. Can be thousands!
1234567,
1234568,
1234569,
1234570,
...
]
}
}
],
"should" : [
{
"query" : {
...
}
]
}
}
}
"aggregations" : {
...
}
}
这非常有效,但我们的客户拥有数以千计的黑名单产品。因此,一方面我担心网络流量太高,我发现完整的Elasticsearch请求速度非常慢。但这主要取决于黑名单产品的数量。
我的下一个方法是将我自己的Elasticsearch查询构建器开发为插件,它处理Elasticsearch内部的黑名单内容。 此黑名单查询扩展 AbstractQueryBuilder 并使用 TermInSetQuery 。因此,此查询构建器会请求给定客户的黑名单,缓存它,并使用所有列入黑名单的产品编号构建 TermInSetQuery 。
现在我的查询看起来像这样:
{
"query" : {
"bool" : {
"must_not" : [
{
"blacklist" : { <-- This is my own query builder
"customer" : 1234567
}
}
],
"should" : [
{
"query" : {
...
}
]
}
}
}
"aggregations" : {
...
}
}
这种解决速度更快,并且不必每次都在查询中发送列入黑名单的产品编号列表。所以我没有网络开销。但是查询仍然比没有这个黑名单的东西慢得多。我描述了这个查询并且我没有看到,我的黑名单查询占用了大约80-90%的运行时间。
我认为这个 TermInSetQuery 在我的情况下表现非常糟糕。因为我猜Elasticsearch各自的Lucene匹配过程不仅仅是一个:
if (blacklistSet.contains(id)) {
continue; // ignore the current search hit.
}
你们有人对我有所暗示,如何更有效地实施这样的黑名单机制?
有没有办法拦截Elasticsearch / Lucene查询过程? 也许我可以编写自己的真实Lucene查询,而不是使用 TermInSetQuery 。
提前致谢。
基督教
答案 0 :(得分:2)
这不是解决方案,但也许是另一种方法。
首先,here是您可能感兴趣的旧版SO帖子。据我所知,最新版本的Elasticsearch没有引入/改变更好或更合适的东西。
如果您点击Terms Query Documentation页面答案的链接,您会找到一个非常简单的示例。
现在,您可以创建索引并为每个客户存储黑名单,而不是缓存您的黑名单。然后,您可以使用术语查询,并基本上从其他索引(=您的黑名单缓存)中引用黑名单。
我不知道这些黑名单的更新频率,所以这可能是一个问题。此外,您必须小心不要失去同步。特别值得一提的是,索引插入/更新默认情况下不会立即可见。因此,您可能需要在索引/更新黑名单时强制刷新。
正如我所说,这可能不是解决方案。但如果听起来对你来说可行,那么可能值得尝试与其他解决方案进行比较。
答案 1 :(得分:2)
感谢您的提示。实际上我想避免索引黑名单信息。因此我决定编写自己的Elasticsearch黑名单插件。但我想的越多,我最不喜欢我的想法。如果我可以摆脱我的插件,我就不必维护我的插件,例如移动到云会更容易。所以,我尝试了一些事情。
我创建了一个包含100,000个文档的测试索引,其中包括不允许客户购买哪些产品的信息。 E.g。
{
"id" : "123456"
"description" : "My example products",
...
"blacklist" : [ <lots_of_customer_numbers> ]
}
此外,我创建了一个黑名单索引,其中一个文档带有10,000个项目的黑名单,用于测试术语查找。 (应代表一位客户的黑名单。)
我使用了版本5.1.2的现有Elasticsearch安装。
黑名单被忽略了。只是查询关键字。
"query" : {
"bool" : {
"must" : [
{
"multi_match" : {
"query" : <keyword>,
"fields" : [
"_all"
]
}
}
]
}
}
带有must_not和ids plus关键字的黑名单。 (注意:服务器和客户端在同一主机上。因此我们没有将网络作为瓶颈。)
"query" : {
"bool" : {
"must" : [
{
"multi_match" : {
"query" : <keyword>,
"fields" : [
"_all"
]
}
}
],
"must_not" : [
{
"ids" : {
"values" : [ <10000_ids> ]
}
}
]
}
}
黑名单与术语查找加关键字一起考虑。
"query" : {
"bool" : {
"must" : [
{
"multi_match" : {
"query" : <keyword>,
"fields" : [
"_all"
]
}
}
],
"must_not" : [
{
"terms" : {
"blacklist" : {
"index" : "blacklists",
"type" : "blacklist",
"id" : "1234567",
"path" : "items"
}
}
}
]
}
}
在同一索引和文档以及关键字中使用must_not和term query时考虑黑名单。
"query" : {
"bool" : {
"must" : [
{
"multi_match" : {
"query" : <keyword>,
"fields" : [
"_all"
]
}
}
],
"must_not" : [
{
"term" : {
"blackList" : {
"value" : "1234567"
}
}
}
]
}
}
我为每个测试场景进行了1,000次搜索。这就是结果:
测试1:3,708ms
测试2:104,775ms
测试3:39,586ms
测试4:3,586ms
正如您所见,带有must_not的 test 2 和ids执行速度最慢。使用术语查找的测试3 比测试1 慢大约11倍。 测试4 的性能略优于测试1 。
如果 test 3 场景足以满足我的实际需求,我会尝试,因为实现这一点非常容易。如果不是,我将使用 test 4 场景,这在我的真实场景中会更复杂。
再次感谢。