首页 > 解决方案 > ElasticSearch QueryBuilder must_not 子句的奇怪行为

问题描述

根据文档

must_not子句(查询)不得出现在匹配的文档中。

我有这样的查询:

// searching for URI which contains smart and doesn't contain vip.vs.csin.cz
BoolQueryBuilder builder = QueryBuilders.boolQuery();
builder.must(QueryBuilders.termQuery(URI, "smart")));
builder.mustNot(QueryBuilders.termQuery(URI, "vip.vs.csin.cz")));

我的弹性搜索存储库中有两个 URI

1)

/smart-int-vip.vs.csin.cz:5080/smart/api/runtime/case/SC0000000000558648/record/generate/4327/by/SMOBVA002/as/true?espisRecordForm=ANALOG&accountNumber=2318031033/0800

2)

/smart/api/runtime/case/SC0000000000558648/record/generate/4327/by/SMOBVA002/as/true?espisRecordForm=ANALOG&accountNumber=2318031033/0800

当我通过ElasticSearchTemplate执行查询时

elasticsearchTemplate.getClient().search(searchRequest);

我得到0 条记录。当我在没有mustNot子句的情况下执行相同的查询时,我得到了2 条记录。
在 kibana 我可以写:

uri: "smart" NOT uri: "vip.vs.csin.cz"

并按预期获得1 条记录。

我期待来自 Java ElasticSearchClient 的相同行为。如何从 Java 中过滤包含“vip.vs.csin.cz”的记录,以及为什么它过滤了第二条记录,即使它不包含我指定的 mustNot 子句中的任何内容?

编辑这里是我的映射

@Document(indexName = "audit-2018", type = "audit")
public class Trace {

    @Id
    private String id;
    @Field(type = FieldType.Text)
    private String uri;

    // more columns, getter & setters
}

标签: javaelasticsearch

解决方案


您提供的 Java 代码显示了使用mustandmust_not子句的 bool 查询,其中您正在执行术语查询。关于术语查询的事情是它们受制于您在字段中拥有的分析器,标准分析器text(这是您的uri字段的数据类型,请在此处阅读更多内容)字段将删除所有标点符号(换句话说,您的word) 并把你的话分开。vip.vs.csin.cz变成vip vs csin cz. text字段类型应仅保留用于全文搜索,在您的情况下,我会选择字段keyword类型(在此处阅读更多内容)您的 Kibana 查询按预期工作的原因是因为该查询实际上并没有进行术语查询,而是请求参数包含 lucene 查询的查询:uri: "smart" NOT uri: "vip.vs.csin.cz"

所以你有几个选择来解决你的问题。您可以将您的术语查询更改为match_phrase查询,这将允许您保留标记化术语的顺序并可能获得正确的结果。另一种方法是query_string在您的 Java 代码中执行查询而不是术语查询,因为您已经确定这确实会为您提供正确的结果。

但是,我建议的解决方案是使用urifield type重新索引keyword,因为此字段类型不会导致将您的字段值不必要地标记为多个术语。您可以在此处阅读有关keyword字段类型的默认分析器和标记器的更多信息。这将使您在将来避免头疼,因为您知道您的查询与您的字段值完全“原样”匹配。


推荐阅读