云原生多模数据库 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-cli为HBase表创建搜索索引时,需先为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 rowkey与ES索引的_id列通过搜索索引同步建立一一映射:每行HBase数据对应唯一索引文档。以下代码演示如何将ElasticSearch的_id列转换为HBase的rowkey。
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 rowkey和ElasticSearch _id的映射关系由JSON配置文件中的rowkeyFormatterType属性定义。该属性的取值和行为,可以参考文档:配置列映射中的说明。以下提供简单代码示例,说明如何将ElasticSearch的_id列,转换为HBase的rowkey。
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);
}
}
}