使用ES API访问搜索索引 - HBase表

云原生多模数据库 Lindorm搜索引擎(兼容Elasticsearch API版本)支持通过开源ES接口访问搜索索引,便于已采用ES技术栈的业务平滑迁移。本文以宽表引擎的HBase表场景为例,演示通过ES API访问搜索索引的最佳实践。

前提条件

  • 开通搜索索引服务,要求开通Lindorm搜索引擎的ElasticSearch兼容版本。

  • 推荐使用ES 7.10版本的客户端访问;若有其他版本客户端的访问需求,可联系Lindorm技术支持(钉钉号:s0s3eg3)咨询。

数据准备

准备HBase表,并写入样例数据。

宽表引擎创建HBase

HBase表的创建方式可以参考基于HBase API的应用开发相关文档。以下是通过Lindorm Shell访问宽表引擎创建HBase表的简单示例。

create 'testTable', 'f1'

写入样例数据

put 'testTable', 'row1', 'f1:name', 'foo'

创建搜索索引

HBase表有两种方式可以创建搜索索引,以下分别是两种方式的介绍。

通过lindorm-cli创建搜索索引(SQL方式)

使用lindorm-cliHBase表创建搜索索引时,需先为HBase列定义数据类型,后续索引管理与SQL表操作体验完全一致。本文仅提供使用ES API访问的简单示例,详细索引管理方法请参考通过Lindorm-cli管理搜索索引

创建搜索索引

步骤一:HBase表添加列映射
ALTER TABLE testTable MAP DYNAMIC COLUMN `ROW` HSTRING, f1:name HSTRING;
步骤二:创建搜索索引
CREATE INDEX idx USING SEARCH ON testTable (f1:name);
开启索引source

默认情况下,通过SQL表创建的搜索索引不在搜索引擎存储原始数据,以节约存储空间。如需通过ES API直接获取原始数据,请在创建索引时指定SOURCE_SETTINGS属性。

CREATE INDEX idx USING SEARCH ON testTable (f1:name) WITH (
  numShards=4,
  SOURCE_SETTINGS='
    {
      "enabled": true
    }
  '
);

获取ElasticSearch索引名

通过SQL方式创建搜索索引时,ElasticSearch中对应的索引名称为宽表namespace名.宽表Table名.搜索索引名。以上示例对应的ElasticSearch索引名为default.testTable.idx登录UI界面并执行下述查询,可以查到对应的索引信息。

GET default.testTable.idx

宽表与ElasticSearch索引字段的映射关系

HBase表创建的索引,在ElasticSearch中的字段拼接规则是列族_列名。例如,对上述示例,宽表中的f1:name字段,对应ES索引中的f1_name字段。

{
  "mappings": {
    "dynamic": "true",
    "dynamic_templates": [],
    "properties": {
      "_searchindex_id": {
        "type": "keyword",
        "index": false
      },
      "delete_version_l": {
        "type": "long"
      },
      "f1_name": {
        "type": "keyword"
      },
      "update_version_l": {
        "type": "long"
      }
    }
  }
}

宽表与ElasticSearch索引字段的主键关系

HBase rowkeyES索引的_id列通过搜索索引同步建立一一映射:每行HBase数据对应唯一索引文档。以下代码演示如何将ElasticSearch_id列转换为HBaserowkey

package org.example;

import org.apache.commons.codec.binary.Hex;
import org.apache.hadoop.hbase.util.Bytes;

public class SearchDocIdToHBaseRowKey {

    public static void main(String[] args) throws Exception {
        // 转换规则根据HBase主键类型来确定
        String elasticSearchId = "000003e9";
        Object result = transformToRowKey(elasticSearchId, "HSTRING");
        System.out.println("HBase主键类型为HSTRING时,对应HBase主键为: " + result);

        result = transformToRowKey(elasticSearchId, "HINTEGER");
        System.out.println("HBase主键类型为HINTEGER时,对应HBase主键为: " + result);
    }

    private static Object transformToRowKey(String id, String rowKeyType) throws Exception {
        byte[] rowKey;
        switch (rowKeyType) {
            case "HSTRING":
                // 若HBase的主键是HSTRING类型,则HBase主键和ElasticSearch完全一致
                return id;
            // 如果是其他类型,先通过Hex解码拿到原始二进制rowkey,再根据类型转换成对应的值
            case "HBOOLEAN":
                rowKey = Hex.decodeHex(id.toCharArray());
                return Bytes.toBoolean(rowKey);
            case "HSHORT":
                rowKey = Hex.decodeHex(id.toCharArray());
                return Bytes.toShort(rowKey);
            case "HINTEGER":
                rowKey = Hex.decodeHex(id.toCharArray());
                return Bytes.toInt(rowKey);
            case "HLONG":
                rowKey = Hex.decodeHex(id.toCharArray());
                return Bytes.toLong(rowKey);
            case "HFLOAT":
                rowKey = Hex.decodeHex(id.toCharArray());
                return Bytes.toFloat(rowKey);
            case "HDOUBLE":
                rowKey = Hex.decodeHex(id.toCharArray());
                return Bytes.toDouble(rowKey);
            default:
                throw new IllegalArgumentException("Unsupported row key type: " + rowKeyType);
        }
    }
}

通过Lindorm Shell创建搜索索引

通过Lindorm Shell管理搜索索引,需通过JSON文件的形式维护宽表表名、字段和搜索索引名、字段的映射关系,详情可参考通过Lindorm Shell管理搜索索引

创建搜索索引

步骤一:ElasticSearch手动创建索引

登录搜索引擎可视化用户界面,执行以下请求创建索引表democollection

PUT democollection
{
  "settings": {
    "index.number_of_shards": 4
  },
  "mappings": {
    "properties": {
      "name_s": {"type":"keyword"}
    }
  }
}
步骤二:映射宽表列和索引列

创建JSON文件,定义宽表列和搜索索引列的映射关系,确保宽表列的数据能正确同步到搜索索引中。以下示例展示如何将宽表f1:name列映射到索引表的name_s列。

{
  "sourceNamespace": "default",
  "sourceTable": "testTable",
  "targetIndexName": "democollection",
  "indexType": "ES",
  "rowkeyFormatterType": "STRING",
  "fields": [
    {
      "source": "f1:name",
      "targetField": "name_s",
      "type": "STRING"
    }
  ]
}

JSON文件中各字段说明参考:配置列映射

获取ElasticSearch索引名

通过Lindorm Shell方式创建搜索索引时,ElasticSearch中的索引需由用户手动创建。以上示例中,宽表testTable对应的ElasticSearch索引名为democollection

宽表与ElasticSearch索引字段的映射关系

通过Lindorm Shell方式创建搜索索引时,字段的映射关系由上述的JSON配置文件定义。以上示例中,宽表列f1:name对应的索引字段名为name_s

宽表与ElasticSearch索引字段的主键关系

通过Lindorm Shell方式创建搜索索引时,HBase rowkeyElasticSearch _id的映射关系由JSON配置文件中的rowkeyFormatterType属性定义。该属性的取值和行为,可以参考文档:配置列映射中的说明。以下提供简单代码示例,说明如何将ElasticSearch_id列,转换为HBaserowkey。

package org.example;

import org.apache.commons.codec.binary.Hex;
import org.apache.hadoop.hbase.util.Bytes;

public class SearchDocIdToHBaseRowKeyV2 {

    public static void main(String[] args) throws Exception {
        String elasticSearchId = "000003e9";
        // 先转换为原始的byte[]二进制表示
        byte[] rowKey = transformToRowKey(elasticSearchId, "STRING");
        // 将原始的rowKey根据实际类型转换为对应的数据
        Object rowKeyObject = transformRowKeyToObject(rowKey, "STRING");
        System.out.println("rowKeyFormatter为STRING,HBase主键实际类型为STRING,对应HBase主键为: " + rowKeyObject);

        rowKey = transformToRowKey(elasticSearchId, "HEX");
        rowKeyObject = transformRowKeyToObject(rowKey, "INT");
        System.out.println("rowKeyFormatter为HEX,HBase主键实际类型为INT,对应HBase主键为: " + rowKeyObject);
    }

    private static byte[] transformToRowKey(String id, String rowKeyFormatter) throws Exception {
        return "STRING".equals(rowKeyFormatter) ? Bytes.toBytes(id) : Hex.decodeHex(id.toCharArray());
    }

    private static Object transformRowKeyToObject(byte[] rowKey, String realType) throws Exception {
        switch (realType) {
            case "STRING":
                return Bytes.toString(rowKey);
            case "BOOLEAN":
                return Bytes.toBoolean(rowKey);
            case "SHORT":
                return Bytes.toShort(rowKey);
            case "INT":
                return Bytes.toInt(rowKey);
            case "LONG":
                return Bytes.toLong(rowKey);
            case "FLOAT":
                return Bytes.toFloat(rowKey);
            case "DOUBLE":
                return Bytes.toDouble(rowKey);
            default:
                throw new Exception("Unsupported type: " + realType);
        }
    }
}