Elasticsearch - 使用id

时间:2017-07-28 11:16:24

标签: elasticsearch elasticsearch-plugin blacklist

我遇到以下情况:

我们目前通过商业解决方案实现了产品搜索。 我正在玩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

提前致谢。

基督教

2 个答案:

答案 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安装。

测试1:

黑名单被忽略了。只是查询关键字。

  "query" : {
    "bool" : {
      "must" : [
    {
      "multi_match" : {
        "query" : <keyword>,
        "fields" : [
          "_all"
        ]
      }
    }
      ]
    }
  }

测试2:

带有must_not和ids plus关键字的黑名单。 (注意:服务器和客户端在同一主机上。因此我们没有将网络作为瓶颈。)

  "query" : {
    "bool" : {
      "must" : [
    {
      "multi_match" : {
        "query" : <keyword>,
        "fields" : [
          "_all"
        ]
      }
    }
      ],
      "must_not" : [
    {
      "ids" : {
        "values" : [ <10000_ids> ]
      }
    }
      ]
    }
  }

测试3:

黑名单与术语查找加关键字一起考虑。

  "query" : {
    "bool" : {
      "must" : [
    {
      "multi_match" : {
        "query" : <keyword>,
        "fields" : [
          "_all"
        ]
      }
    }
      ],
      "must_not" : [
    {
      "terms" : {
        "blacklist" : {
          "index" : "blacklists",
          "type" : "blacklist",
          "id" : "1234567",
          "path" : "items"
        }
      }
    }
      ]
    }
  }

测试4:

在同一索引和文档以及关键字中使用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 场景,这在我的真实场景中会更复杂。

再次感谢。