Silentdoer

导航

elasticsearch分词查询总结,批量删除总结

前言:这些总结是根据之前看到的文章和实践得出的,但是也不一定对,后续会根据最新的理解校验更新

核心点之一:term、match、match_phrase对keyword或text是有不同的功效的;并不是说match用于keyword和text的字段的匹配逻辑一致;

注意,中文是单个字就会进行分词(除非特殊配置)

 

text如果在fields配置keyword的话其实就可以理解为在text分词的基础上添加了keyword分词,比如aa bb在普通text里是["aa", "bb"],但是如果在fields里添加了keyword则变成["aa", "bb", "aa bb"]三个元素;

 

针对主类型是keyword的字段,match,term,match_phrase没有区别,es服务会忽略客户端分词而直接进行全词匹配(针对这三个匹配类型而言)

所以keyword不能理解为服务端会分词,变成["aa bb cc"]而已,而是主keyword就是没有分词的概念;

 

注意match和match_phrase和term的分词查询都是equals查询,只是匹配逻辑特殊;它不是模糊匹配整体值;

 

模糊匹配可以用通配符查询:wildcard,类似MySQL的like;通配符查询一般是查keyword(比如name: "aa*"),但是也可以查text,这个时候模糊匹配的是name里所有的分词对aa*进行通配符匹配,匹配到了一个分词就算成功;

 

查询出来越匹配的记录会排在越上面,比如aa bb然后match一个text index:true的字段,两条记录的该字段值分别是aa和bb aa,那显然第二条记录更匹配因为它同时匹配到了aa和bb两个分词所以查询出来是排在第一条记录前面的

 

1.es的分词是分为查询分词(“客户端分词”)和存储分词(“服务端分词”只有text有这个功能);

【term就是全词匹配不会去对服务端分词后的来匹配,而text进行term匹配的话是要求客户端的查询条件等于服务端的某个分词,和match逻辑其实一致,只是term不会客户端分词,即clientList元素个数是1,match匹配则是对客户端的分词分别去匹配服务端的分词列表,即服务端text的分词列表只要存在一个分词等于客户端的分词之一就算匹配上了,伪代码类似:clientList.anyMatch(e -> serverList.anyMatch(ee -> ee.equals(e)));

而keyword则match就可以理解为服务端会对客户端的多个分词分别contains匹配

而不是对该字段的分词进行匹配】

所谓查询分词就比如{"query":{"match":{"desc":"中国加油"}}}去查index user的所有文档里的desc字段;

这里用了match,会进行客户端分词(term则不会客户端分词),那么就会分词为中国,加油两个分词(假设是这个分词规则),

而假设index user的desc是text的,则它在存储的时候也会分词(“服务端分词”),因此假设存在id是1的desc保存了“中国必胜”,会有分词“中国”和“必胜”

,则这次查询是用客户端的分词“中国”,“加油”去匹配服务端的分词“中国”,“必胜”,发现有交集匹配成功【注意,默认情况下客户端和服务端的token匹配是不区分大小写的比如SAid和said两个token是相等的,但是keyword是区分大小写的】(es匹配还有匹配度的说法,像这个只有一个分词匹配成功了所以匹配度是1)

,因此查出的结果会包括id是1的这条文档;

如果是用:{"query":{"term":{"desc":"中国加油"}}}去查询,term查询表示里面的值本身就是一个分词不会再拆分(客户端层面的精确匹配),因此客户端“分词”:“中国加油”不会匹配服务端的分词“中国”和“必胜”,因此查询结果没有这条;

如果用:{"query":{"match_phrase":{"desc":"中国加油"}}}去查询的话,match_phrase和match一样也会分词,它和match不同的是,它的分词和“服务端”文档的分词不再是交集的关系

,而是要求服务端的分词必须包含客户端的分词,且顺序要一样,比如服务端的id是2的desc是"我为中国加油"假设是分词为“我”,“为”,“中国”,“加油”,则服务端的分词列表包含客户端的

分词列表“中国”,“加油”,因此能匹配id是2的文档;id是1的不匹配,因为它的分词只有包含了客户端的分词“中国”;如果id是3的desc数据是“我为加油中国”,则它的分词变成了“我”,“为”,“加油”,“中国”,虽然也是完全包含客户端的所有分词“中国”,“加油”,但是顺序不一样,也是不匹配的;

 

所以一个字段要做到能完全精确匹配,用term查询,且查询模式是keyword

经过测试,当字段是text,且只开启了index: true, doc_values:false的情况下,用term是无法模糊搜索和精确搜索,但是用match则可以分词搜索(注意text的分词匹配和模糊匹配不是一个概念,比如服务端是aa bbb ccc,则服务端分词为aa和bbb和ccc,而客户端它是match: bb,是无法匹配的,因为bb并不等于aa、bbb、ccc的其中之一)

然后text类型的doc_values无法设置为true,即无法对该类型字段进行聚合,排序,script等操作;

 

match_phrase和match的区别是,当客户端分词是多个时(如aa bb会分词为两个),则服务端会要求服务端分词(text)是也同时存在aa和bb的分词,且还要按先后顺序,即服务端的该字段内容必须包括aa bb,而哪怕是bb aa也不匹配;

 

普通text(index为true)无法term查询,除非在fields里加了keyword

 

【重要】所以一个字段的作用是分为两块:1.被搜索;2.用于排序、聚合;index是用于可以被搜索

 

而假设index user的desc是keyword类型,则它在“服务端”是不会分词的(或者说完整的内容就是它的分词)【即只有一个分词“中国必胜”】,这个时候用{"query":{"match":{"desc":"中国加油"}}} 去查则不会查出id是1的那条,因为match分词里的中国和加油两个分词都不匹配服务端的id是1的desc的“分词:中国加油”;

而用:{“query”:{“wildcard”:{“name”:" *国必* "}}} 去查会匹配,因为wildcard不会分词,但是去服务端匹配的时候虽然也是用存在分词交集的匹配逻辑,但是具体分词匹配的时候是用模糊查询的;

 

所以es的匹配分为四个维度(目前的理解),1是客户端匹配服务端文档字段时存在分词情况(根据查询条件来判断要不要分词,比如term则不需要再分词,match需要);2是服务端根据字段type来判断保存时是否要分词【客户端和服务端不管是否需要进一步分词,都至少存在“一个分词,即内容本身”】;3是客户端的分词和服务端分词匹配的时候匹配成功的规则是什么,目前就是两边的分词存在匹配交集就算成功;4是分词进行匹配时的算法是什么,比如match,term都是要分词相等,而wildcard可以分词之间模糊匹配即可;

且分词匹配的越多,查询结果越靠前;

https://blog.csdn.net/qq_35149889/article/details/127894381;这篇文章可以参考

 

服务端的分词是入库的时候就会持久化了,因此是在入库前(比如代码层面或者post的字段JSON数据里可以指定分词器?)要配置分词策略,然后服务端进行分词入库的时候就根据这个分词器来分词(一个字段数据包括其原始数据和分词后的数据),分词策略也许可以在提交数据的时候临时指定(类似针对当前session临时export环境变量,但也有个默认分词【这块不确定是否可以临时,但是在index的mappings里应该可以指定字段默认的分词器的】);

 

每个分词称为一个token

 

要查看某个字符串的分词结果可以用这个:

GET _analyze

{
    "analyzer": "standard",
    "text": "I just said hello world"
}

注意默认就是standard的,可以不指定;第二个key就是要text,这个不是fieldName;

然后standard的是以英文进行分隔来分词,每个分词会将大写转小写英文(估计token匹配也是会先转小写),然后中文的则默认是按单个字符来分词;

然后就是分词后的tokens不会有交集,即不会出现i, just, said, hello, world五个分词后还会自动组合一个hello world的分词得到六个token;

 

服务端针对index的字段定义的时候,还可以让某个字段既可以按text来查,也能按keyword来查:【草,不知道是不是我的es版本问题,keyword建的index,居然默认是text+fields type是keyword的模式,无语了(好吧,是因为自己创建了idx_test的模板,index_patterns是idx_test_*,但是提交数据创建的index却直接叫idx_test导致的,如果是idx_test_aa就不会有这问题)】【.keyword也适用于match和match_phrase,注意用了.keyword后客户端的match等也是不再分词了】【注意如果字段本身是keyword的,然后再加.keyword是会查不出来数据的】

"foo": {
  "type": "text",
  "fields": {
    "keyword": {
      "type": "keyword",
      "ignore_above": 256
    }
  }
}

那么客户端查的时候用"term": {"foo": "aaa ss"},会用分词"aaa ss"这个分词去匹配服务端,服务端则也是用文档foo的tokens来和"aaa ss"进行匹配;

但是客户端如果是这样指定的"term":{"foo.keyword":"aaa ss"},则客户端也会以keyword(term改成match也不会对客户端分词了)"aaa ss"来匹配服务端,服务端也会会直接将文档的foo字段以keyword的模式和"aaa ss"来匹配(equals)

如果是"match":{"foo.keyword":"aaa rr"}是不会匹配服务端的,因为客户端是"aaa rr"去匹配服务端的"aaa ss"

如果是"wildcard":{"foo.keyword":"aaa*"}是可以匹配服务端的,keyword只是指定不分词匹配,但是匹配规则仍然按match(equals),wildcard(模糊匹配)的规则匹配的。

 

bool里有must、should、must_not、filter这几种过滤方式,其中must和should在过滤的同时还会匹配得分(tokens匹配了多少个),而后面两个则不会,所以当不需要匹配得分的时候用filter和must_not来过滤数据效率会更高;【这两种方案分别叫query context查询(must should)和filter context查询,虽然都是在query/bool下】

 

es支持类似MongoDB一样的写json,比如{"user":{"id":9,"name":"aaa"}},写入数据假设index不存在也会自动创建(如果index已经存在且user的数据结构已经定下来不是这个结构则会写入失败)index,然后可以通过"term":{"user.id":9}来查user.id【以term的方式查,注意.id和.keyword在定义上不一样,id是子properties,而keyword是fields里定义的】

es字段类型没有数组,但是可以写值得时候写数组,比如字段age是integer类型,则可以写"age": [1, 3],然后查的时候"term": {"age": 1}和"term":{"age",3}都能查出该文档记录,但是查的时候不能"term":{"age":[1,3]}来查

 

字段的index属性是true,则表示它有类似红黑树索引,可以做range查询;doc_values是true表示它可以做aggs聚合查询;

 

注意,正常的删除无法用模糊匹配index的方式来执行(这点和查询不一样),但是可以用aa_*/_delete_by_query, method是POST,然后条件JSON就是正常的查询条件来批量删除了;

posted on 2022-11-22 16:24  Silentdoer  阅读(3053)  评论(0编辑  收藏  举报