es6.x logstash同步父子文档

es版本6.2.4
父子文档,可以理解为关系型数据库中的一对多的关系。使用logstash同步MySQL数据,有时候需要同步父子文档。
父子文档相比嵌套文档较灵活,但只适用于“一对大量”且这个“一”不是海量的应用场景,该方式比较耗内存和CPU,这种方式查询比嵌套方式慢5~10倍,且需要使用特定的has_parent和has_child过滤器查询语法,查询结果不能同时返回父子文档(一次join查询只能返回一种类型的文档)。而受限于父子文档必须在同一分片上,ES父子文档在滚动索引、多索引场景下对父子关系存储和联合查询支持得不好,而且子文档type删除比较麻烦(子文档删除必须提供父文档ID)。

存宽表是个不错的方案,纠结该多宽也十分讲究!
建议:一个宽表维护业务主表的基本信息及其强依赖的扩展信息。

https://blog.csdn.net/alex_xfboy/article/details/97900734
https://blog.csdn.net/alex_xfboy/article/details/89841553

(1)创建含 join 类型数据的索引(使用mapping)

PUT datacatalog
 {
   "mappings": {
     "_doc": {
       "properties": {
         "join_field": { 
           "type": "join",
           "relations": {
             "datacatalog":"datacatalog_theme_trade"
          }
        }
      }
    }
  }
}

索引:datacatalog,type:_doc,parent-child 关系:“datacatalog”: “datacatalog_theme_trade”
(2)logstash 配置:

input {
  jdbc {
      # mysql 数据库链接,shop为数据库名
      jdbc_connection_string => "jdbc:mysql://ip:3306/kf_data_open?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"
      # 用户名和密码
      jdbc_user => "root"
      jdbc_password => "12345678"
      # 驱动
      jdbc_driver_library => "/usr/local/logstash-6.2.4/config/mysql-connector-java-5.1.42.jar"
      # 驱动类名
      jdbc_driver_class => "com.mysql.jdbc.Driver"
      #是否分页
      jdbc_paging_enabled => "true"
      jdbc_page_size => "50000"
      #直接执行sql语句,通过id > :sql_last_value去判断上次执行到了哪里
      statement =>"select catalog_id,catalog_name,catalog_status, page_views, data_provide,create_time, update_time
      from   kf_data_catalog where update_time > :sql_last_value"
      #使用列值进行查询记录追踪
      use_column_value => true
      #设置跟踪记录的列为:id,要先设置use_column_value=true
      tracking_column => "update_time"
      #默认为number,如果为日期必须声明为timestamp
      tracking_column_type => "timestamp"
      # 执行的sql 文件路径+名称
      #statement_filepath => "filepath"
      #设置监听间隔  各字段含义(由左至右)分、时、天、月、年,全部为*默认含义为每分钟都更新
      schedule => "* * * * *"
      # 索引类型
      type => "datacatalog"
      #插件会在last_run_metadata_path参数所指示的元数据文件中,持久化sql_last_value参数
      last_run_metadata_path=>"/usr/local/logstash-6.2.4/last_run_metadata_path_File/datacatalog_last_run_metadata"
    }
jdbc {
      # mysql 数据库链接,shop为数据库名
      jdbc_connection_string => "jdbc:mysql://ip:3306/kf_data_open?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC"
      # 用户名和密码
      jdbc_user => "root"
      jdbc_password => "12345678"
      # 驱动
      jdbc_driver_library => "/usr/local/logstash-6.2.4/config/mysql-connector-java-5.1.42.jar"
      # 驱动类名
      jdbc_driver_class => "com.mysql.jdbc.Driver"
      #是否分页
      jdbc_paging_enabled => "true"
      jdbc_page_size => "50000"
      # 执行的sql 文件路径+名称
      statement =>"select DISTINCT b.property_id, b.catalog_id,a.dic_label,b.dic_val from kf_dictionaries a INNER JOIN kf_catalog_tag b on a.dic_val=b.dic_val  where a.p_id in (1 ,2)
          and b.property_id > :sql_last_value"
      #使用列值进行查询记录追踪
      use_column_value => true
      #设置跟踪记录的列为:updated
      tracking_column => "property_id"
      # 默认为number,如果为日期必须声明为timestamp
      #tracking_column_type => "timestamp"
      #statement_filepath => "/hw/elasticsearch/logstash-6.2.4/bin/test.sql"
      #设置监听间隔  各字段含义(由左至右)分、时、天、月、年,全部为*默认含义为每分钟都更新
      schedule => "* * * * *"
      # 索引类型
      type => "datacatalog_theme_trade"
      #插件会在last_run_metadata_path参数所指示的元数据文件中,持久化sql_last_value参数
      last_run_metadata_path=>"/usr/local/logstash-6.2.4/last_run_metadata_path_File/datacatalog_theme_trade_last_run_metadata"
    }
}

filter {
  if [type]=="datacatalog" {
  mutate {
        add_field => { "join_field" => "datacatalog" }
  }
}
if [type]=="datacatalog_theme_trade" {
  mutate {
        add_field => {"[join_field][name]" => "datacatalog_theme_trade"}
        #catalog_id 子表的父id
        add_field => {"[join_field][parent]" => "%{catalog_id}"}  
  }
}
}

output {
  #将数据库的数据输出在控制台
  stdout {
      codec => json_lines
  }
  #将数据库数据存储到es
if [type]=="datacatalog" {
  elasticsearch {
  #ESIP地址与端口
  hosts => "ip:9200"
  #ES索引名称(自己定义的)
  index => "datacatalog"
  document_type => "_doc"
  #自增ID编号
  document_id => "%{catalog_id}"
  }
}

if [type]=="datacatalog_theme_trade" {
  elasticsearch {
  #ESIP地址与端口
  hosts => "ip:9200"
  #ES索引名称(自己定义的)
  index => "datacatalog"
  document_type => "_doc"
  #自增ID编号  这里使用系统默认设置的id,是因为子文档和父文档都存一个索引里,怕主键相同出现覆盖
  #document_id => "%{property_id}"
  routing => "%{catalog_id}"
  }
}
}

java api

springboot整合的es,好像不能用Join查询。
使用原生的:

public class DataCatalogESUtil {
    private static String clusterName="test";
    private static String ip="ip";
    private static String port="9300";
    private static TransportClient client;

    static {
        try {
            // 指定集群名,默认为elasticsearch,如果改了集群名,这里一定要加
            Settings settings = Settings.builder()
                    .put("cluster.name", clusterName)
                    .build();
            client = new PreBuiltTransportClient(settings);
            client.addTransportAddress(new TransportAddress(InetAddress.getByName(ip), Integer.valueOf(port)));
        } catch (Exception e) {
            throw new RuntimeException(String.format("连接ES失败:%s", e.getMessage()));
        }
    }

    public static void main(String[] args) {
        //根据子查父  查询一个主题或行业下的所有目录
        //childQuery();
        //获取所有目录(父文档)
        //getAllDataCatalog();
        //父查子  获取一个目录有那些主题或者行业
        parentQuery();
    }

    //父子关系  其实就是多对多的关系
    // 数据目录定义为父,主题/行业定义为子,这样就可以实现查询数据目录下有哪些主题/行业(父查子)
    //或者查询主题下有那些目录(子查父)

    //根据子查父  查询一个主题或行业下的所有目录
    //https://blog.csdn.net/lsr40/article/details/102462989
    public static void childQuery() {
        //设置查询条件 查询主题theme_nyaq下的目录
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("dic_val", "theme_nyaq");

        //datacatalog_theme_trade  主题/行业的文档(子)
        HasChildQueryBuilder hasParentQueryBuilder =
                new HasChildQueryBuilder("datacatalog_theme_trade",matchQueryBuilder , ScoreMode.Max);

        //执行查询  datacatalog表示索引  _doc表示类型
        SearchResponse searchResponse = client.prepareSearch("datacatalog")
                .setTypes("_doc")
                .setQuery(hasParentQueryBuilder)
                .execute()
                .actionGet();

        //遍历查询结果
        SearchHits hits = searchResponse.getHits();
        List<Object> result = Lists.newArrayList();
        for (SearchHit hit : hits) {
            Map<String, Object> source = hit.getSourceAsMap();
            result.add(JSON.toJSON(source));
        }
        result.forEach(a->{
            System.out.println(a);
        });
    }

    //根据子查父 获取所有目录(可设置分页,排序)
    public static void getAllDataCatalog(){
        //设置分页参数
//        int pageSize=5;
//        int currentPage=1;

        //设置查询条件 全查
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();

        //datacatalog_theme_trade  主题/行业的文档(子)
        HasChildQueryBuilder hasChildQueryBuilder =
                new HasChildQueryBuilder("datacatalog_theme_trade",matchAllQueryBuilder, ScoreMode.Max);

        //执行查询  datacatalog表示索引  _doc表示类型
        SearchResponse searchResponse = client.prepareSearch("datacatalog")
                .setTypes("_doc")
//                .addSort("catalog_id", SortOrder.ASC) //排序 ASC DESC
                .setQuery(hasChildQueryBuilder)
//                .setFrom((currentPage-1) * pageSize)
//                .setSize(pageSize)      // 设置分页
                .execute()
                .actionGet();

        //遍历查询结果
        SearchHits hits = searchResponse.getHits();
        List<Object> result = Lists.newArrayList();
        for (SearchHit hit : hits) {
            Map<String, Object> source = hit.getSourceAsMap();
            result.add(JSON.toJSON(source));
        }
        result.forEach(a->{
            System.out.println(a);
        });
    }

    //父查子  获取一个目录有那些主题或者行业
    public static void parentQuery(){

        //设置查询条件  查询catalog_id为5的目录
        MatchQueryBuilder catalog_id = QueryBuilders.matchQuery("catalog_id", "5");

        //datacatalog 表示数据目录文档(父)  true暂时不知作用
        HasParentQueryBuilder hasParentQueryBuilder =
                new HasParentQueryBuilder("datacatalog",catalog_id , true);

        //执行查询  datacatalog表示索引  _doc表示类型
        SearchResponse searchResponse = client.prepareSearch("datacatalog")
                .setTypes("_doc")
//                .addSort("catalog_id", SortOrder.ASC) //排序 ASC DESC
                .setQuery(hasParentQueryBuilder)
//                .setFrom((currentPage-1) * pageSize)
//                .setSize(pageSize)      // 设置分页
                .execute()
                .actionGet();
        //遍历结果
        SearchHits hits = searchResponse.getHits();
        List<Object> result = Lists.newArrayList();
        for (SearchHit hit : hits) {
            Map<String, Object> source = hit.getSourceAsMap();
            result.add(JSON.toJSON(source));
        }
        result.forEach(a->{
            System.out.println(a);
        });
    }
}

java api 参考来源:

https://blog.csdn.net/lsr40/article/details/102462989

多表查询我最终还是没选择父子文档这种方式,就因为查询结果不能同时返回父子文档,编码时很不方便
另一种多表查询解决方式:

https://blog.csdn.net/weixin_42412601/article/details/103756695

你可能感兴趣的:(elasticsearch)