背景:
随着es 版本的不断向前演进,在es 6.x 版本中,已经开始有意识地,主动的去弱化 type 这个概念。原因简单来说就是,es 底层存储时,相同 type的数据会存储在一起,但类比到数据库中的表,我们知道,不同数据库的同样名称的表的数据是不存储在一起的;所以type 对于 es 删除来说有害无利。为解决此问题,es 决定在 6.x 版本中 弱化之,在 7.x 版本中 会彻底删除 type。
join 类型 是 es 6.x 版本新产生的一个类型,其产生是为了弥补 type 被弱化,被删除的影响。因为 在 es 5.x 甚至以前,parent-child 数据在 es 中存储 关键就在于 建立 一个索引,不同的 type 去区分父子;当es 6.x 决定放弃 type 时,join 类型 应运而生,它为解决 parent-child 而来。
本文章主要记录一下 个人使用 join 类型处理问题时遇到的不会之处,欢迎大家帮忙更新!
一、使用 logstash 全量更新 mysql 数据 到 es 6.x 的配置文件编写(含parent、child)
参考:https://discuss.elastic.co/t/how-to-create-a-parent-using-the-new-join-type-with-logstash/119523/2
(1)创建含 join 类型数据的索引(使用mapping)
PUT logstash-index-name
{
"mappings": {
"doc": {
"properties": {
"join_field": {
"type": "join",
"relations": {
"customer": "actions"
}
}
}
}
}
}
索引:logstash-index-name,type:doc,parent-child 关系:“customer”: “actions”
(2)父表对应的 logstash 配置增加如下部分:
mutate {
add_field => { "join_field" => "customer" }
}
(3)子表对应的 logstash 配置 增加如下部分:
mutate {
add_field => {"[join_field][name]" => "actions"}
add_field => {"[join_field][parent]" => "%{parent_id}"}
}
同时,由于 你需要指定子文档的routing ,所以还需要加如下配置:
elasticsearch {
hosts => [ "localhost:9200" ]
index => "logstash-index-name"
routing => "%{parent_id}"
}
至此结束!
二、elasticsearch 集群内部索引间数据复制(将数据从a索引拷贝到b索引)
首先,新建一个一摸一样的索引,然后:
POST _reindex
{
"source": {
"index": "a" // a,指代旧索引
},
"dest": {
"index": "b" // b, 指代新索引
}
}
三、elasticsearch 查询结果震荡问题—同样的查询条件,查询结果不完全相同
elasticsearch可以使用preference参数来指定分片查询的优先级,即我们可以通过该参数来控制搜索时的索引数据分片。
如不设置该参数:在所有有效的主分片以及副本间轮询。
搜索同一条件,结果ES返回的顺序却不尽相同,这就是请求轮询到不同分片,而未设置排序条件,相同相关性评分情况下,是按照所在segment中lucene id来排序的,相同数据的不同备份之间该id是能保证一致的,故造成结果震荡问题。
可以通过设置 preference 参数来解决问题。
Java api 可以通过 setPreference()方法来设置。
参考:
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-preference.html
https://elasticsearch.cn/article/334
四、inner_hits问题
背景:使用join 数据类型,查询时使用 inner_hits 希望查出其 父代或子代数据;但当 一父多子,即一个 父代数据 下面影射了 n个子代数据(1:n),此时使用 inner_hits 只能查出部分数据(3条)。
分析:inner_hits 使用 需要注意几个参数,例如 from,size;见名知意,from 标注起始位置;size 结果集大小(此处默认值是3,也是上面问题出现的根本原因)。所以可通过设置size,使其输出足够多的数据。
参考:
https://www.elastic.co/guide/en/elasticsearch/reference/6.5/search-request-inner-hits.html
五、父子文档查询问题
(1)根据子数据查询父数据(子数据一定存在):
GET mytest/_search
{
"query": {
"has_child": {
"type": "answer",
"query": {
"match_all": {}
},"inner_hits": { // inner_hits 用于携带展示直接关系数据
}
}
}
}
(2)根据父数据查询子数据(子数据一定存在):
GET mytest/_search
{
"query": {
"has_parent": {
"parent_type": "question",
"query": {
"match_all": {}
},"inner_hits": {}
}
}
}
(3)当父或子不存在时,无法展示,可使用如下模板查询:
// 利用bool 的should 功能巧妙查出来
GET mytest/_search
{
"query": {
"bool": {
"should": [
{
Q
},
{
"bool": {
"must": [
{
Q
},
{
"has_child": {
"type": "answer",
"query": {
"match_all": {}
},"inner_hits": {}
}
}
]
}
}
]
}
}
}
(4)查询没有子文档的父文档
场景:
父文档:teacher,子文档:student,假设索引中存在某些teacher文档未被任何student指定为父文档,那么怎样把这部分teacher查出来?
分析:
文档分几种情况,1:有父亲,没儿子(要查询的);2:有父亲,有儿子(要must_not 排除的);3:无父亲,无儿子(没有数据);4:无父亲,有儿子(不可能)
参考查询:
GET applys/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"talent_join": {
"value": "teacher"
}
}
}
],
"must_not": [
{
"has_parent": {
"parent_type": "teacher",
"query": {
"match_all": {}
}
}
},
{
"has_child": {
"type": "student",
"query": {
"match_all": {}
}
}
}
]
}
}
}
六、elasticsearch-sql
elasticsearch-sql 查询语句注意
(1)不存在 != ,使用 not in (xxx,yyy,zzz) 代替 或者使用 <> 代替
(2)使用 substring 函数时,切记 字段类型 必须是 keyword 类型,text 类型不行,会分词,无法使用 截取函数
(3)elasticsearch-sql 的 查询最大值限制,例如只能查到10000,可以通过修改参数去解决,如下:
PUT cmb_hr_index_9_5/_settings
{
"max_result_window": "20000000"
}
(4)logstash 中 截取字符串
event.get(‘birthdate’)[0…3]
(5)logstash 字段去掉空格
方法一:清洗数据,改变数据为想要的格式
mutate{
gsub => ["state","\s",""]
}
方法二:对结果进行清洗,对数据无影响
使用 trim() 函数,前提是该字段类型是 keyword 类型
select state,trim(state) from cmb_hr_index_9_5
(6)注意:es不支持聚合后的结果进行分页操作!!!
七、关于 路由的体会
路由有自己的算法,比如hash(_routing) % num_primary_shards;所以你的routing值即使不一样,在经过算法处理后相似的可能性十分大,本身路由也不是无限多的意思,它只是帮你管理数据,使用户对数据把握更加具体,常利于我们的搜索,因为你知道在哪个分片了,就不用去轮询,自然优化了;
其次,已存在的文档数据路由无法修改,如果你必须修改,只能重建索引,重新导入数据;道理很简单,举例:你在索引中存了一个id=4的数据,他被索引到了a分片;此时你完全可创建id为4的数据,并将其放到b索引上,
但是显而易见,这样搜索会冲突,所以id+routing需要保持唯一性。