对于刚接触搜索或者Elasticsearch的小白来说对queryString可能接触的不多,但是对于早期从事搜索的人来说queryString并不会陌生,它可以理解成检索表达式,但并不是elasticsearch的queryDSL,他遵从的是Lucene语法。elasticsearch同样有接口应用于queryString。下面上个例子:
{
"from": 0,
"size": 100,
"query": {
"query_string": {
"query": "TITLE:无人机"
}
}
}
“TITLE:无人机”就是一个queryString,是一个字符串,表示在TITLE字段中查询匹配“无人机”。
它还支持一些参数:
{
"from": 0,
"size": 100,
"query": {
"query_string": {
"query": "TITLE:无人机"
"fields": [],
"type": "best_fields",
"default_operator": "or",
"max_determinized_states": 10000,
"enable_position_increments": true,
"fuzzy_transpositions": true,
"boost": 1
}
}
}
具体的关于queryString本篇不再过多介绍,这里不是重点。
参考官网:https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html
从上述例子中我们知道了我们的需求是从TITLE中匹配“无人机”这个词。这个检索式没任何问题,因为一般的分词器都会把无人机这个词分出来,但随着我们检索的深入,我们会遇到一个问题,比如我们要搜索“铝合金轮毂”这个词,一般的分词器都不会分出这个词来,也就是我们所说的专有名词词库。那么会出现什么问题呢?我们试试就知道了
{
"query": {
"query_string": {
"query": "TITLE:铝合金轮毂"
}
}
}
我们会发现匹配结果包含很多铝合金和轮毂的文档,可想而知是因为在检索的时候,是先把“铝合金轮毂‘这个词先分词,再去检索的,类似match检索。那么这样的话就会带来两个问题:
说到这,可能有些人就会说了,你把“铝合金轮毂”这个词加上引号不就行了。我们来具体看看行还是不行:
{
"query": {
"query_string": {
"query": "TITLE:\"铝合金轮毂\""
}
}
}
我们还引号的目的是想做精准匹配,的确,早期的lucene的确是这么做的。从返回结果上看,也的确达到了我们想要的记过——只返回“铝合金轮毂”。但是我想告诉大家的是,这其实是早期Lucene的一个bug,在Lucene4中已经被修复了——将检索词加引号并不是实现精准匹配。
下面我们开始验证。
{
"query": {
"term": {
"TITLE": "铝合金轮毂"
}
}
}
我们使用term检索来验证,前面已经说到词库里是没有“铝合金轮毂”这个词的,也就是说分词器不会将“铝合金轮毂”作为倒排的一个term进行索引。这样来说,这个检索必然是没有数据返回,我们找一句话验证一下。
GET _analyze?pretty
{
"analyzer": "ik_max_word",
"text": "某汽车铝合金轮毂在使用一年后发现裂纹"
}
我们使用ik_max分词,避免有些猩猩不服,下面是分词结果:
{
"tokens": [
{
"token": "某",
"start_offset": 0,
"end_offset": 1,
"type": "CN_CHAR",
"position": 0
},
{
"token": "汽车",
"start_offset": 1,
"end_offset": 3,
"type": "CN_WORD",
"position": 1
},
{
"token": "铝合金",
"start_offset": 3,
"end_offset": 6,
"type": "CN_WORD",
"position": 2
},
{
"token": "合金",
"start_offset": 4,
"end_offset": 6,
"type": "CN_WORD",
"position": 3
},
{
"token": "轮毂",
"start_offset": 6,
"end_offset": 8,
"type": "CN_WORD",
"position": 4
},
{
"token": "在",
"start_offset": 8,
"end_offset": 9,
"type": "CN_CHAR",
"position": 5
},
{
"token": "使用",
"start_offset": 9,
"end_offset": 11,
"type": "CN_WORD",
"position": 6
},
{
"token": "一年",
"start_offset": 11,
"end_offset": 13,
"type": "CN_WORD",
"position": 7
},
{
"token": "一",
"start_offset": 11,
"end_offset": 12,
"type": "TYPE_CNUM",
"position": 8
},
{
"token": "年后",
"start_offset": 12,
"end_offset": 14,
"type": "CN_WORD",
"position": 9
},
{
"token": "年",
"start_offset": 12,
"end_offset": 13,
"type": "COUNT",
"position": 10
},
{
"token": "后",
"start_offset": 13,
"end_offset": 14,
"type": "CN_CHAR",
"position": 11
},
{
"token": "发现",
"start_offset": 14,
"end_offset": 16,
"type": "CN_WORD",
"position": 12
},
{
"token": "裂纹",
"start_offset": 16,
"end_offset": 18,
"type": "CN_WORD",
"position": 13
}
]
}
从结果中可以发现,并没有“铝合金轮毂”这个词,所以这个时候使用term检索是不会检索到这条数据的。那为什么用querySring就可以检索的到呢?
因为我们主观带入的认为加上引号就是做了精准匹配,实际上早期的Lucene的确是这样,但这是Lucene的一个bug,早已经被修复了,修复的结果是queryString中带引号的检索词并不是使用精准匹配,而是使用短语匹配。
我们在使用Elasticsearch的过程中可能使用term和match的时候是最多的,用到短语匹配的可能并不是很多。但在特定的场景中,短语匹配的效果要远好于term和match。
{
"query": {
"match_phrase" : {
"TITLE" : "铝合金轮毂",
"slop": 0
}
}
}
match_phrase为短语匹配,短语匹配同样是先将检索词分词,支持自定义分词“analyzer”:“my_analyzer”,一般建议使用和索引分词同一个分词器的检索效果更好。与match不同的是在于参数slop,官方给的解释是
A phrase query matches terms up to a configurable slop (which defaults to 0) in any order. Transposed terms have a slop of 2.
翻大概意思就是允许分词匹配顺序错误的次数, “slop”: 0表示不允许顺序错误,这样即使将“铝合金”和“轮毂”分开,但是结果中必须是“铝合金”和“轮毂”挨着的才会被检索到,同样可以实现term的检索效果。
以上就是使用queryString检索词加不加引号,以及如何检索精确检索词库中不存在的词的介绍。
match检索一直有一个通病,就是不支持多个检索词(例如terms),会报错。网上找了很多资料,国内的各大网上均没有相关介绍,最终在国外的网站找到了相似的:跳转链接 和 github相关iss
{
"query": {
"match" : {
"TITLE" : ["铝合金","轮毂"]
}
}
}
这种检索式会报错:
{
"error": {
"root_cause": [
{
"type": "illegal_state_exception",
"reason": "Can't get text on a START_ARRAY at 1:28"
}
],
"type": "illegal_state_exception",
"reason": "Can't get text on a START_ARRAY at 1:28"
},
"status": 500
}
所以我们可以这么做:
{
"query": {
"bool": {
"must": [
{
"match": {
"TITLE": "铝合金"
}
},
{
"match": {
"TITLE": "轮毂"
}
}
]
}
}
}
前面提到match_phrase可以通过"slop": 0来实现term的效果,脑洞大开的猩猩们肯定会想到,那我是不是可以通过控制slop参数来实现match检索的功能了呢?答案是不建议。虽然PhraseQuery也会进行打分,但是打分效果远没有BooleanQuery效果好,所以不建议这么使用,无论是terms还是match比这都要好得多。
本片文章介绍了Lucene的queryString检索词加不加引号的区别,以及如何使用term、match来进行检索匹配。如何对词库中没有的词进行精确匹配,以及match不支持数组参数等问题进行探究。
作为整个Elasticsearch干货系列的开篇,内容可能不多,但这的确是困扰了我很久的一个问题,在这里分享给大家,主要还是相关资料太少。我们的老集群已经运行很多年了, 还是0.9版本的集群,那个年代的搜索引擎和现在差别太大了。在这里跟一些初学者说,无论你是学习搜索还是Elasticsearch,一定要学习Lucene,不管Elasticsearch还是以前的solr,他们能够强大,是因为Lucene是全世界最好的搜索引擎,千遍万遍底层技术十年不变。
后续会不定期更新干货系列,非常希望大家能够一起沟通,Elasticsearch越学你会越发现,它非常有趣。