在字段上排序时,Elastic的搜索结果不稳定



我们刚刚升级到Elasticsearch 2.3.1(从1.7),我们得到了奇怪的搜索行为,我无法解释。似乎发生的是,包含bool查询和sort子句的搜索请求返回:

  1. 似乎与给定的搜索条件不匹配的文档。
  2. 每次请求对匹配文档total的估计差异很大

请求行为的最小示例:

post pim_search_1/_search
{
   "explain": false,
   "track_scores": false,
   "sort": [
      {
         "product_id": {
            "order": "desc"
         }
      }
   ],
   "query": {
      "bool": {
         "filter": [
            {
               "terms": {
                  "publication": [
                     "public"
                  ]
               }
            },
            {
               "query_string": {
                  "query": "iphone",
                  "default_operator": "and"
               }
            }
         ]
      }
   }
}

在这个例子中,"iphone"的查询字符串根本没有返回iphone。将explain设置为true对于根本没有匹配项的文档产生如下结果:

 "_explanation": {
           "value": 0,
           "description": "Failure to meet condition(s) of required/prohibited clause(s)",
           "details": [
              {
                 "value": 0,
                 "description": "no match on required clause (#ConstantScore(publication:public) #_all:iphone)",

所以文档没有匹配子句,但它仍然返回?

我们找到了两种解决方法:

  1. _score进行排序或完全省略sort子句。在其他任何地方排序,比如上面的字段或_doc,都会产生不稳定的行为。
  2. 包含track_scores : true请求

所以它似乎与得分和相关性有关。但由于我们在自己的领域进行排序,我们对相关性或分数不感兴趣。如果没有变通方法,响应的max_scorenull,每个文档的_score也是如此。

这种行为是否可以用任何方式解释,或者我们是否应该查看集群运行状况/配置/损坏?根据集群,它的运行状况是绿色的,并且该索引的所有分片看起来都是健康的。它目前是一个小索引,在3个节点上有3个分片(每个分片1个副本)。

<标题> 更新

我已经进一步调查了这个问题,它似乎与缓存有关。具体来说,_all字段的字段数据缓存(我不是很熟悉Elasticsearch的内部,所以请纠正我,如果这不是一件事)。

复制

的步骤

我有一个重现问题的数据集,留下评论,我可以发送给你。

使用以下查询:

post pim_search_1/_search
{
   "fields": [
       "_all"
   ],
   "explain": true,
   "size": 100,
   "sort": [
      {
         "product_id": {
            "order": "desc"
         }
      }
   ],
   "query": {
      "bool": {
         "must": [
            {
               "query_string": {
                  "default_field": "_all", 
                  "query": "surface",
                  "default_operator": "and"
               }
            }
         ],
         "filter": [
            {
               "terms": {
                  "publication": [
                     "public"
                  ]
               }
            }
         ]
      }  
   }
}
  1. 执行查询。您在这里的查询字符串中搜索"surface",这应该会导致总共22个命中。这是正确的。执行这个查询多次(这似乎对步骤2很重要)。
  2. 将查询字符串更改为"iphone"。这仍然会导致22次命中,即使数据集只包含一个应该匹配的项。_explanation还提到,找到的文档实际上并不匹配,就像我上面的例子一样。
  3. 执行post pim_search_1/_cache/clear
  4. 再次执行"iphone"的查询。它现在应该只返回1次命中,这是正确的。也要多次执行这个。
  5. 再次执行查询"surface",这将只返回1次命中,并且_explanation再次声明它没有在结果文档上得到匹配。
  6. 从查询中删除sort子句,一切看起来正常。包含"track_scores" : true也是如此。

代替_cache/clear,它也可以重新启动集群。

我说它与_all字段有关,因为将query_stringdefault_field更改为primitive_name字段(分析字段)会导致正确的行为。对于这个例子,我把_all作为一个存储字段(它通常不与我们在一起),它在搜索结果中返回,所以你可以检查它(似乎不包含任何奇怪的东西)。

以上是在Elasticsearch 2.3.5的单节点集群(我的本地PC)上完成的。

这个Github问题似乎与我的问题相同,但当时无法复制并被关闭。

这在Elasticsearch 2.4中已经修复:

https://github.com/elastic/elasticsearch/pull/20196

最新更新