【问题标题】:ElasticSearch query optimization - Java APIElasticSearch 查询优化 - Java API
【发布时间】:2017-08-03 07:36:34
【问题描述】:

我是 ES 的新手,正在搜索 100k 数据的记录集。 这是我用来索引数据的映射和设置 JSON:

settings.json

{
    "index": {
        "analysis": {
            "tokenizer": {
                "ngram_tokenizer": {
                    "type": "ngram",
                    "min_gram": 3,
                    "max_gram": 10
                }
            },
            "analyzer": {
                "ngram_tokenizer_analyzer": {
                    "type": "custom",
                    "tokenizer": "ngram_tokenizer"
                }
            }
        }
    }
}

mappings.json

{
    "product": {
        "properties": {
            "name": {
                "type": "string",
                "analyzer": "ngram_tokenizer_analyzer",
                "store": true
            },
            "description": {
                "type": "string",
                "analyzer": "ngram_tokenizer_analyzer",
                "store": true
            },
            "vendorModelNumber": {
                "type": "string",
                "analyzer": "ngram_tokenizer_analyzer",
                "store": true
            },
            "brand": {
                "type": "string",
                "analyzer": "ngram_tokenizer_analyzer",
                "store": true
            },
            "specifications": {
                "type": "string",
                "analyzer": "ngram_tokenizer_analyzer",
                "store": true
            },
            "upc": {
                "type": "string",
                "analyzer": "ngram_tokenizer_analyzer",
                "store": true
            },
            "storeSkuId": {
                "type": "string",
                "analyzer": "ngram_tokenizer_analyzer",
                "store": true
            },
            "modelNumber": {
                "type": "string",
                "analyzer": "ngram_tokenizer_analyzer",
                "store": true
            }
        }
    }
}

我需要根据某些优先级根据提到的所有字段查询文档。这是我搜索所有记录的查询。

BoolQueryBuilder query = QueryBuilders.boolQuery();
int boost = 7;

for (String str : dataSplit) {
    query.should(QueryBuilders.wildcardQuery("name", "*" + str.toLowerCase() + "*").boost(boost));
}
boost--;
for (String str : dataSplit) {
    query.should(QueryBuilders.wildcardQuery("description", "*" + str.toLowerCase() + "*").boost(boost));
}
boost--;
for (String str : dataSplit) {
    query.should(QueryBuilders.wildcardQuery("modelNumber", "*" + str.toLowerCase() + "*").boost(boost));
}
boost--;
for (String str : dataSplit) {
    query.should(QueryBuilders.wildcardQuery("vendorModelNumber", "*" + str.toLowerCase() + "*").boost(boost));
}
boost--;
for (String str : dataSplit) {
    query.should(QueryBuilders.wildcardQuery("storeSkuId", "*" + str.toLowerCase() + "*").boost(boost));
}
boost--;
for (String str : dataSplit) {
    query.should(QueryBuilders.wildcardQuery("upc", "*" + str.toLowerCase() + "*").boost(boost));
}
boost--;
for (String str : dataSplit) {
    query.should(QueryBuilders.wildcardQuery("brand", "*" + str.toLowerCase() + "*").boost(boost));
}
client.prepareSearch(index).setQuery(query).setSize(200).setExplain(true).execute().actionGet();

该查询确实帮助我搜索数据并且工作正常,但我的问题是我使用通配符查询需要很多时间。 有人可以帮助优化此查询或指导我找到最适合我的搜索的查询吗? TIA。

【问题讨论】:

  • 为什么首先使用通配符查询?具有 3+ 的 ngram 标记器,正常的匹配查询应该适用于长度超过 2 个字符的输入。或者 ngram 标记器的原因是什么?旁注;使用此分析器(如定义),您的查询将区分大小写。可能是有意的,但非常不寻常。
  • 谢谢@Slomo,你是对的。我不应该在 ngram 中使用通配符。我可以让它不区分大小写吗?并且使用 ngram 我应该使用 term query 或 match 进行查询,这是更优化的方式?抱歉,如果这不是一个明智的问题:)

标签: java elasticsearch query-optimization wildcard


【解决方案1】:

首先,让我先回答一个简单的问题:区分大小写。如果您定义了一个自定义分析器,您可以添加不同的过滤器,这些过滤器将应用于每个标记输入已被标记器处理之后。

{
"index": {
    "analysis": {
        "tokenizer": {
            "ngram_tokenizer": {
                "type": "ngram",
                "min_gram": 3,
                "max_gram": 10
            }
        },
        "analyzer": {
            "ngram_tokenizer_analyzer": {
                "type": "custom",
                "tokenizer": "ngram_tokenizer",
                "filter": [
                    "lowercase",
                    ...
                ]
            }
        }
    }
}

如您所见,有一个现有的 lowercase 过滤器,它将简单地将所有标记转换为小写。我强烈建议参考documentation。这些令牌过滤器有很多 个。


现在是更复杂的部分:NGram 标记器。同样,为了更深入地了解,您可能需要阅读docs。但是提到你的问题,你的分词器基本上会创建长度为 3 到 10 的术语。这意味着文本

I am an example TEXT.

基本上会产生很多代币。只是展示一些:

  • 尺寸 3:“I a”、“am”、“am”、...、“TEX”、“EXT”
  • 尺寸 4:“I am”、“am”、“am a”、...、“TEX”、“TEXT”。
  • 10 号:“我是前任”,...

你明白了。 (lowercase 标记过滤器现在会将这些标记小写)

Match 和 Term Query 之间的区别:对 Match 查询进行分析,而对 term 查询不进行分析。事实上,这意味着您的匹配查询可以匹配多个字词。例子:你匹配exam"

实际上这将匹配 3 个术语:exaxamexam

这会影响比赛的得分。比赛越多,得分越高。在某些情况下是需要的,在其他情况下则不是。

不分析术语查询,这意味着exam 将匹配,但只有一个术语(当然是exam)。但是,由于它没有被分析,它也没有小写,这意味着你必须自己在代码中这样做。 Exam 永远不会匹配,因为如果您使用小写标记过滤器,您的索引中没有大写字母的术语。

不确定您的用例。但我有一种感觉,你可以(甚至想要)确实使用术语查询。 请注意,您的索引中没有项大小大于 10。因为这就是您的 ngram-tokenizer 所做的。

/编辑:

关于匹配查询值得指出的一点,以及您可能要使用术语的原因:一些匹配查询,如 Simple 也将匹配来自 examplemple

【讨论】:

  • tnx 很多 @Slomo 的详细解释。将完善我的代码,并且还将通过文档。 :)
  • 所以假设我需要在多个字段上搜索多个值,带有匹配查询的布尔值将是一个不错的选择,对吧?
  • @DivyaMenon 你可以。或者,也许您也可以使用multiMatch,您还应该能够对字段进行加权。也许具有预期结果的具体查询示例将有助于回答您的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-04
  • 2017-08-18
  • 2021-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多