需要将已有索引中的已有字段作为新的查询条件,需求中需要对该字段进行范围查询,当前字段为keyword类型的数字,此时发现查询所得结果与预期不符合。
2.1发现问题:
索引字段数据类型不合理
2.2问题本质:
需要将keyword类型的转换成数值类型,以便执行range query范围查询操作。因为keywork类型本质是字符串类型的一种,如果以keyword类型做字符串处理比的是首字符的ASCII码。
2.3方案讨论:
如何进行字段类型转换?
对于三种方案来说,方案一贴合业务,方案二网络上也存在很多操作文档,方案三的文档较少在此给出操作步骤。
方案一:重新建立索引,更正数据类型,重新导入数据
特点:可以从根本上解决问题,但是不够灵活,所有依赖当前索引的服务需要重新上线将查询/写入指向新的索引。
方案二:reindex+alias别名零停机方案
特点:重新建立索引并且更正数据类型,reindex重新导入数据,使用alias别名的方式让用户无感知,依赖当前索引的服务也无需做任何操作。
方案三:convert ingest预处理 + reindex结合
特点:无需重新创建索引,哪个字段不满足就更改哪个字段
3.1创建索引
- PUT student
- {
- "settings": {
- "number_of_shards": 1,
- "number_of_replicas": 0
- },
-
- "mappings": {
- "student":{
- "properties":{
- "obj":{
- "type":"object",
- "properties":{
- "id":{
- "type":"integer"
- },
- "name":{
- "type":"keyword"
- },
- "age":{
- "type":"keyword"
- }
- }
- }
- }
- }
- }
- }
3.2写入数据
- POST student/student/1
- {
- "obj": [
- {
- "id": 1,
- "age": "21",
- "name": "项目1"
- },
- {
- "id": 2,
- "age": "51",
- "name": "项目2"
- }
- ]
- }
-
- POST student/student/2
- {
- "obj": [
- {
- "id": 1,
- "age": "21",
- "name": "项目1"
- },
- {
- "id": 2,
- "age": "51",
- "name": "项目2"
- }
- ]
- }
-
- POST student/student/3
- {
- "obj": [
- {
- "id": 1,
- "age": "21",
- "name": "项目1"
- },
- {
- "id": 2,
- "age": "51",
- "name": "项目2"
- }
- ]
- }
3.3数据预处理convert
- PUT _ingest/pipeline/test_pipeline
- {
- "processors": [
- {
- "foreach": {
- "field": "obj",
- "processor": {
- "convert": {
- "field": "_ingest._value.age",
- "type": "integer",
- "ignore_failure": true
- }
- }
- }
- }
- ]
- }
3.4数据reindex迁移
- POST _reindex
- {
- "source": {"index": "student"},
- "dest": {"index": "student1", "pipeline": "test_pipeline"}
- }
3.5观察结果并分析
查询student1的结果为,可以观察到age的数据类型已经转换为integer,反观student的查询结果,age的数据类型依旧为keyword,写入新的数据后观察查询结果依旧如此:
- {
- "took" : 0,
- "timed_out" : false,
- "_shards" : {
- "total" : 5,
- "successful" : 5,
- "skipped" : 0,
- "failed" : 0
- },
- "hits" : {
- "total" : 3,
- "max_score" : 1.0,
- "hits" : [
- {
- "_index" : "student1",
- "_type" : "student",
- "_id" : "2",
- "_score" : 1.0,
- "_source" : {
- "obj" : [
- {
- "name" : "项目1",
- "id" : 1,
- "age" : 21
- },
- {
- "name" : "项目2",
- "id" : 2,
- "age" : 51
- }
- ]
- }
- },
- {
- "_index" : "student1",
- "_type" : "student",
- "_id" : "1",
- "_score" : 1.0,
- "_source" : {
- "obj" : [
- {
- "name" : "项目1",
- "id" : 1,
- "age" : 21
- },
- {
- "name" : "项目2",
- "id" : 2,
- "age" : 51
- },
- {
- "name" : "项目3",
- "id" : 11,
- "age" : 11
- },
- {
- "name" : "项目4",
- "id" : 22,
- "age" : 31
- }
- ]
- }
- },
- {
- "_index" : "student1",
- "_type" : "student",
- "_id" : "3",
- "_score" : 1.0,
- "_source" : {
- "obj" : [
- {
- "name" : "项目1",
- "id" : 1,
- "age" : 21
- },
- {
- "name" : "项目2",
- "id" : 2,
- "age" : 51
- }
- ]
- }
- }
- ]
- }
- }
对于上述三种方案的取舍要取决于具体业务场景以及服务之间依赖关系是否紧密,如果调用方可以接受重新切换索引,可以酌情考虑方案一和方案三,方案一虽然操作起来较为繁琐,但原有的索引可以删除,节省内存空间。方案三虽然操作简单但是原有索引不可删除,若不计成本的情况下可以方案三更优。若调用方较多链路复杂,调用方不可接受上线切换索引则可以考虑方案二。