elasticsearch 6.x 关于 join 类型的 一些问题记录(长期更新)

  • Post author:
  • Post category:其他


背景:

随着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需要保持唯一性。



版权声明:本文为qq_37891300原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。