使用Lucene模糊运算符时,Elasticsearch查询和过滤器会给出不同的文档计数

4
使用ElasticSearch v1.7.2和一个相当大的索引,我在以下两个搜索中得到了不同的文档计数,这两个搜索在query_string中使用模糊搜索。
查询:
{
  "query": {
     "query_string": {
        "query": "rapt~4"
     }
  }
}

筛选器:

{
 "filter": {
    "query": {
       "query_string": {
          "query": "rapt~4"
       }
    }
 }
}

过滤器比查询结果多约5%。为什么文档计数不同?我可以指定选项使它们保持一致吗?
请注意,只有在使用中等大小的数据集时才会出现此不一致性。我尝试将仅匹配过滤器但不匹配查询的少量(<10)文档插入到干净的群集中,在此之后,我的查询和过滤器都成功地匹配了所有文档。但是,在一个单索引、单类型和几百个文档的群集中,我开始看到差异。
使用explain=true选项,似乎使用 Practical Scoring Function计算查询分数。说明提供有关增强、queryNorm、idf和术语权重的信息。相比之下,filter解释仅报告实际评分函数的增强和queryNorm组件,而不是idf或术语权重。
以下是带解释的响应示例。请注意,我从示例中删除了许多字段并简化了内容,因此解释中的术语频率与实际内容不匹配,除了匹配的单词(在本例中为“fact”)。这些响应是针对相同事件的。我的问题是,过滤器响应中包含了未包含在查询响应中的其他命中。它们的解释看起来完全相同。
查询:
curl -XPOST "http://localhost:9200/index-name/example-type/_search" -H "Content-Type: application/json" -d'{"query":{"query_string":{"query":"rapt~"}},"explain":true}'

并查询响应:

{
"_source": {
  "type": "example",
  "content": "to the fact that"
},
"_explanation": {
  "value": 0.10740301,
  "description": "sum of:",
  "details": [
    {
      "value": 0.10740301,
      "description": "weight(_all:fact^0.5 in 465) [PerFieldSimilarity], result of:",
      "details": [
        {
          "value": 0.10740301,
          "description": "score(doc=465,freq=2.0), product of:",
          "details": [
            {
              "value": 0.11091774,
              "description": "queryWeight, product of:",
              "details": [
                {
                  "value": 0.5,
                  "description": "boost"
                },
                {
                  "value": 7.303468,
                  "description": "idf(docFreq=68, maxDocs=37706)"
                },
                {
                  "value": 0.03037399,
                  "description": "queryNorm"
                }
              ]
            },
            {
              "value": 0.96831226,
              "description": "fieldWeight in 465, product of:",
              "details": [
                {
                  "value": 1.4142135,
                  "description": "tf(freq=2.0), with freq of:",
                  "details": [
                    {
                      "value": 2,
                      "description": "termFreq=2.0"
                    }
                  ]
                },
                {
                  "value": 7.303468,
                  "description": "idf(docFreq=68, maxDocs=37706)"
                },
                {
                  "value": 0.09375,
                  "description": "fieldNorm(doc=465)"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
}

筛选器:

curl -XPOST "http://localhost:9200/index-name/example-type/_search" -H "Content-Type: application/json" -d'{"query":{"filtered":{"filter":{"fquery":{"query":{"query_string":{"query":"rapt~"}}}}}},"explain":true}'

并过滤响应:

{
"_source": {
  "type": "example",
  "content": "to the fact that"
},
"_explanation": {
  "value": 1,
  "description": "ConstantScore(cache(+_type:example-type +org.elasticsearch.index.search.nested.NonNestedDocsFilter@737a6633)), product of:",
  "details": [
    {
      "value": 1,
      "description": "boost"
    },
    {
      "value": 1,
      "description": "queryNorm"
    }
  ]
}
}

当我将过滤器包装在常量评分查询中时,我得到的结果与过滤器完全相同(再次比查询多),但解释看起来更加简洁:
常量评分查询包装过滤器:
curl -XPOST "http://localhost:9200/index-name/example-type/_search" -H "Content-Type: application/json" -d'{"query":{"constant_score":{"filter":{"query":{"query_string":{"query":"rapt~"}}}}},"explain":true}'

常量评分查询包装过滤器响应:

{
"_source": {
  "type": "example",
  "content": "to the fact that"
},
"_explanation": {
  "value": 1,
  "description": "ConstantScore(QueryWrapperFilter(_all:rapt~2)), product of:",
  "details": [
    {
      "value": 1,
      "description": "boost"
    },
    {
      "value": 1,
      "description": "queryNorm"
    }
  ]
}
}

由于过滤器返回的结果比查询结果更多,我的猜测是实用评分函数最终会对与查询匹配的文档进行0分评分。然而,对于“匹配”查询的文档,评分函数的各个组件都不应为零。
编辑:我已在238个文档的小集群上重新创建了此问题(请注意,文档内容是从在维基百科文本上训练的ngram语言模型生成的)。我已在dropbox上发布了clusterjson events。为了在这些数据上看到问题,请运行以下查询,该查询将返回id = 138的事件:
{
 "explain": true,
 "query": {
    "bool": {
       "must_not": [
          {
             "query_string": {
                "query": "rap~",
                "fields": [
                   "body"
                ]
             }
          }
       ],
       "must": [
          {
             "constant_score": {
                "filter": {
                   "query": {
                      "query_string": {
                         "query": "rap~",
                         "fields": [
                            "body"
                         ]
                      }
                   }
                }
             }
          }
       ]
    }
 }
}

你为什么把它作为constant_score中的过滤器?你尝试过直接在查询中使用它吗? - femtoRgon
好的,使用没有过滤器的constant_score查询会得到与简单查询相同的结果。过滤器是有区别的。以下内容可以得到与上述constant_score查询等效的结果:{ "filter": { "query": { "query_string": { "query": "rapt~" } } } }我已经更新了我的问题。谢谢! - Ann Irvine
您正在查询分析过的_all字段。在使用查询时,您的查询将通过分析器进行处理。而在使用过滤器时,则可能不会或以稍微不同的方式进行处理。建议尝试添加explain:true或使用Elasticsearch中的其他调试功能之一。 - Jilles van Gurp
您能否添加请求,包括cURL命令?请随意隐藏索引/类型名称。 - pickypg
是的,说得好。虽然当我将过滤器包装在constant_score查询中时,我看到了相同的结果。 - Ann Irvine
显示剩余3条评论
1个回答

0
在 Elasticsearch 5.x 之前的版本中,顶层的 filter 表示一个 post_filter。当使用聚合时,后置过滤器通常只与聚合相关。
从 Elasticsearch 5.0 开始(以及之后的版本),您必须明确指定 post_filter,以避免混淆。
因此,不同之处在于您的顶级查询字面上将结果限制为一组匹配文档。后置过滤器有效地匹配所有内容,然后仅从 hits 中删除结果,而不影响计数。

...看起来查询分数是通过...

查询总是计算分数,并且旨在根据其相关性(分数)正确排序项目。过滤器永远不会计算分数;过滤器旨在进行纯布尔逻辑,不会影响“相关性”除了包含/排除。
公平地说,在 Elasticsearch 1.x 中,您可以以多种方式将任何查询转换为过滤器(在 2.x 中,所有查询在正确的上下文中也都是过滤器!),但我倾向于使用 fquery。如果这样做,那么您应该获得相同的结果:
作为一个查询:
{
  "query": {
     "query_string": {
        "query": "rapt~"
     }
  }
}

作为过滤器:

{
  "query": {
    "filtered": {
      "filter": {
        "fquery": {
          "query": {
            "query_string": {
              "query": "rapt~"
            }
          }
        }
      }
    }
  }
}

在ES 2.x中,过滤器也得到了简化(查询保持不变):

{
  "query": {
    "bool": {
      "filter": {
        "query_string": {
          "query": "rapt~"
        }
      }
    }
  }
}

不幸的是,那并不能完全实现。过滤器确实会影响计数。使用以下过滤器:{ "query": { "filtered": { "filter": { "fquery": { "query": { "query_string": { "query": "rapt~" } } } } } } }我得到的结果与我的过滤器完全相同: { "filter": { "query": { "query_string": { "query": "rapt~" } } } }同样,这比查询结果高约5%(因为我的数据集更大)。 - Ann Irvine
这就是整个请求被发送了吗?还是还有其他的东西?这是否涉及任何别名或仅涉及普通索引? - pickypg
是的,这就是整个请求。我正在访问的索引确实有一个别名,但当我直接针对单个索引时,差异仍然会发生。 - Ann Irvine
你能否添加一个关于你问题的两个不同响应的例子? - pickypg
刚刚添加了一些内容,希望足够详细并且有用! - Ann Irvine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接