elasticsearch 搭配 canal 实现索引增加字段和数组字段同步及搜索

567 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情

前言

之前开发的服务已经上线半年的时间了,elasticsearch的搜索也在正常使用中。最近甲方这边有增加了很多的功能升级。我一看产品文档,不好,感觉要增加很多字段了。之前增加字段什么的,由于不熟悉,elasticsearch和canal,都是删除索引,然后再通过全量更新的命令把数据从mysql中同步过来。

后面仔细看了elasticsearch的文档发现,如果只是单纯的增加字段的话是完全可以通过api实现的。

已有索引增加字段

首先从文档上了解,elasticsearch对于你直接修改字段类型,或者说直接删除字段是不允许的,除非你重建索引。但是对于你增加索引中的字段是支持的,这也就是文档型数据库的优势,对于字段的增加十分的自由,甚至一个表中,不同记录的字段可以不相同。

elasticsearch允许我们在创建索引的时候声明字段,或者对已有的索引增加字段。例如我们现在已经有个了一个索引并且里面已经有了数据,那么我们可以使用_mapping这个api来实现这个功能

curl --location --request PUT 'http://127.0.0.1:9200/test/_mapping' \
--header 'Authorization: Basic ZWxhc3RpYzplbGFzdGlj' \
--header 'Content-Type: application/json' \
--data-raw '{
    "properties": {
        "visible": {
            "type": "short"
        },
        "post_type": {
            "type": "short"
        },
        "post_status": {
            "type": "short"
        },
        "check_state": {
            "type": "short"
        },
        "gmt_checked": {
            "type": "keyword"
        },
        "official": {
            "type": "boolean"
        },
        "labels": {
            "type": "text"
        }
    }
}'

在properties中声明我们要添加的字段的名称和类型。

elasticsearch 数组字段

在msysql中如果是数组类型我们一般会有两种设计方案,一种是将这个数组字段,用任意的分隔符号来拼接,放在一个字段中存储;另一种是使用一对多的关系,分别创建两张表来实现一张表将数组中的值分散为单个字段,然后用另一个字段做关联,,这样两张表就可以实现一个数组的字段。

但是在elasticsearch中并不是这样,首先文档存储非常自由,一个文档中存储的字段非常的自由。elasticsearch中并没有arrays的字段类型,任意一个字段都可以拥有0个或多个值,换句话说,如果你这个字段拥有了多个值,那么你就是数组类型,这里有一个要求就是这个字段中多个值的类型必须相同不能混合,比如[1,'abc']这种数字和字符串混合的就不行,不被elasticsearch所支持。

elasticsearch对于数组字段的搜索是这样的

PUT my-index-000001/_doc/1
{
  "message": "some arrays in this document...",
  "tags":  [ "elasticsearch", "wow" ], 
  "lists": [ 
    {
      "name": "prog_list",
      "description": "programming list"
    },
    {
      "name": "cool_list",
      "description": "cool stuff list"
    }
  ]
}

GET my-index-000001/_search
{
  "query": {
    "match": {
      "tags": "elasticsearch" 
    }
  }
}

这里可以把tags包含elasticsearch的给筛选出来,应该是数组类型,会按照每个元素来进行分组,所以可以和搜索普通字段一样搜索数组。

canal 同步数组字段

我们当前使用的架构是mysql 通过canal将数据同步到elasticsearch中,鉴于elasticsearch文档中字段类型的灵活和多样性,不可避免的我们会同步到数组字段或者对象字段。那么如何去实现这一个功能呢?假设我们有一个商品的实体,它有个标签的属性,这个属性是允许有多个值的。我们之前了解,canal是通过一个sql查询把需要同步的数据查询出来,投影出自己需要的字段,然后对应同步到elasticsearch中。对于复杂字段,给出了这样的解决方案

image.png

在上面给出了如果你是希望你的sql中有些查询出来的字段要被映射成一个数组字段的话,那首先第一步开启和sql属性同级的objFields。这个字段主要是配置数组和json对象字段的。如果我们需要的是数组那么按照这样的格式配置 字段名: arrays:分隔符号 也就是我们必须在sql中查询返回结果中要带上这个分隔符号。 mysql中提供了一个聚合的拼接函数group_concat。这个函数的作用是将一系列非null的值串联起来成一个字符串。默认是通过,拼接,我们可以显式的指定拼接的符号,比如分号,回到我们的假设中,商品对应多个标签。我们可以构建一个查询 select product_id,group_concat(label order by label_id asec separator ';') as labels from product_labels group by product_id; 这样就可以把product关联的label通过先通过group by聚合起来,然后通过group_concat拼接。这里需要注意这种拼接函数默认字符拼接后最大长度1024,可以通过修改mysql全局参数group_concat_max_len来扩大。 我们修改canal的yml文件,重启canal,然后测试一下我们的同步

image.png

同步后会生成数组类型的字段。 也就是canal是能够支持我们把mysql中的一对多字段以数组的形式同步到elasticsearch中的。