第十章YDB进阶使用详解
一、Lucene原生查询语法的使用
YDB的索引本质上是Lucene索引,如果之前有使用Solr Cloud、ElasticSearch的朋友肯定对Lucene不陌生,对于Lucene有自己的强大的过滤筛选方式,YDB里面依然兼容这种语法方式。
1.使用ydb_raw_query_s like 进行Lucene语法匹配
示例:
ydb_raw_query_s like 'cardnum:9612880118617710'
使用临近搜索的示例
ydb_raw_query_s like 'content:"建设银行"~0'
2.使用ydb_code_query_s like 进行Lucene语法匹配
ydb_raw_query_s与ydb_code_query_s的区别是ydb_code_query_s里的值要进行urlencode编码
如:
ydb_code_query_s like 'content%3A%22%E5%BB%BA%E8%AE%BE%E9%93%B6%E8%A1%8C%22%7E0'
二、自定义拓展分词类型
默认YDB提供了一些分词的数据类型,可以满足大部分的匹配场景,但是如果因为业务的特殊,我们需要自己对Lucene分词进行拓展,YDB也提供了拓展自定义分词的方式。具体使用方法跟Solr的schema.xml非常类似。
1.首先 编辑lib下的fieldType.txt文件,增加新的分词类型
2.然后将相关依赖的jar包放到ydb的lib目录下,并重启YDB
3.建表示例
create table myntest(
c1 ft_yanniantest_y,
c2 ft_yanniantest_yy,
c3 ft_yanniantest_yyy,
c4 ft_yanniantest_yn,
c5 ft_yanniantest_yny,
c6 ft_yanniantest_yyyyyyy,
c7 ft_yanniantest_ynynyny
)
对应关系如下
三、IK词库分词
YDB内置了第三方的IK词库分词,IK分词的介绍如下
1.IKAnalyzer 简介
IKAnalyzer 是一个开源基于 JAVA 语言的轻量级的中文分词第三方工具包,从 2006 年推出已经经历了三个较为完整的版本,目前最新版本为3.2.8,它基于Lucene 为应用主体,但是也支持脱离Lucene,成为一个独立的面向JAVA 的分词工具。
2.IKAnalyzer 特性
a. 算法采用“正向迭代最细粒度切分算法”, 支持细粒度和最大词长两种分词方式,速度最大支持 80W 字/秒(1600KB/秒) 。
b. 支持多子处理器分析模式:中文、数字、字母,并兼容日文、韩文。
c. 较小的内存占用,优化词库占有空间,用户可自定义扩展词库。 采用歧义分析算法优化查询关键字的搜索排列组
d. 扩展Lucene 的扩展实现,
3.YDB内的IK数据类型
textik、ikyy 存储,并且采用IK进行分词
ikyn,只进行IK分词,但不进行存储
4.IK分词的词库,怎样拓展
IK词库文件位于lib目录下的IK_ext.dic
大家修改后,记得重启YDB,以便让词库生效。
四、动态列,动态字段
跟Solr 一样,正常情况下,需要事先把知道的字段定义在创建表中创建好,但是有的时候某个表的字段会不确定,并没同一个表的不同行之间有可能使用的字段也会不同,这时可以使用动态字段。
这是一段动态字段的配置示例:
这样,送过来的索引数据中,如果有以 _s 结尾的字段的值都都会被按照上述的索引方式进行索引,如字段名称为title_s,content_s。
在匹配过程,如果动态字段和静态字段都符合,会优先匹配静态字段。另外动态字段的仅支持 * 这一通配符,这个通配符仅能位于开头或结尾。
动态字段(Dynamic fields)允许 YDB 索引没有在 create table 中明确定义的字段。这个在忘记定义一些字段时很有用。动态字段可以让系统更灵活,通用性更强。
动态字段和成规字段类似,除了它名字中包含一个通配符外,在索引文档时,一个字段如果在常规字段中没有匹配时,将到动态字段中匹配。
假设schema中定义了一个叫*_i的动态动态字段,如果要索引一个叫 cost_i 的字段,但是 schema 中不存在 cost_i 的字段,这样 cost_i 将被索引到 *_i 字段中。
动态字段也是定义在 schema.xml 文件中,和其他字段一样,它也有个名词,字段类型,和属性。
5.动态字段的配置解析
1)name="*_s" 匹配规则
2)type="string" 该动态字段 匹配的Solr的fieldType(记住,并不是YDB的数据类型,后面我们会列出默认YDB内部支持的Solr的fieldType,当然也可以通过《自定义拓展分词类型》对于分词的类型进行自定义拓展)
3)indexed="true" 是否启用索引,如果启用该字段可以用于where中的检索
4)stored="false" 其否启用行存储,一般除了分词的类型使用按行存储外,其他的类型均建议按列存储
5)docValues="true"其否启用列存储,一般除了分词的类型使用按行存储外,其他的类型均建议按列存储
6)omitNorms="true" 是否对字段的长度进行规范化,一般我们用不上,建议配置为true直接忽略
7)omitTermFreqAndPositions="true" 对于保存了位置的分词类型,通过这个参数可以配置该字段是否需要保存词频和位置,一般我们都设置为true,不启用。
6.YDB内部支持的solr的fieldType
1)string类型
2)int类型
3)float类型
4)long类型
5)double类型
6)tint类型
7)tfloat类型
8)tlong类型
9)tdouble类型
10)多值列的string类型
11)多值列的int类型
12)多值列的float类型
13)多值列的long类型
14)多值列的double类型
15)多值列的tint类型
16)多值列的tfloat类型
17)多值列的tlong类型
18)多值列的tdouble类型
19)geopoint用于地理位置存储的类型
20)采用cjk二元分词的分词类型
21)YDB内置的采用按照字符char方式分词的号码类型
22)YDB内置的常规文本分词类型
23) YDB内置的保存了词的存储位置信息的,按照字符char方式分词的号码类型
24) YDB内置的保存了词的存储位置信息的,普通文本分词
25) YDB内置的保存了词的位置的以及分词的时候保存了多远分词的元长度的,按照按照字符char方式分词的号码类型
26) YDB内置的保存了词的位置的以及分词的时候保存了多远分词的元长度的,按照按照字符char方式分词的常规文本类型
27) 第三方的采用字段词库的IK分词
28) 保存了词的位置的 cjk二元分词
29) lucene默认的文本分词
30) 采用lucene默认的文本分词,但保存了词的位置的类型
7.YDB内置的动态字段
1)常规类型的内置定义
2)多值列的内置定义
3)分词类型的内置定义
4)分词类型并且存储了位置的内置定义
8.自定义拓展新的动态列
大家可以编辑lib目录下的dynamicField.txt文件,添加自己的动态列,写法同上。
编辑完毕后,要重启YDB。
五、利用Spoon的Kattle将传统数据库的数据导入到YDB中
1.拷贝JDBC驱动包(oracle):
data-integration\lib
ojdbc6.jar
2.拷贝Hadoop配置文件(ydbmaster)
data-integration\plugins\pentaho-big-data-plugin\hadoop-configurations\hdp24(ydbmaster)
core-site.xml
hdfs-site.xml
yarn-site.xml
mapred-site.xml
3.设置HDP插件版本
4.创建YDB表
5.创建Hive外部表
6. 创建 Spoon 转换项目
7.插入表输入模块
8.插入Hadoop File Output 模块
9.插入阻塞数据模块
10.插入执行SQL脚本模块
11.运行并显示结果
六、如何利用Sqoop将传统数据库的数据导入到YDB中
Sqoop本质上是一个ETL工具,可以将传统数据里面的数据,导出到 HDFS中,然后通过Hive表就可以将数据导入到YDB中
1.Sqoop导出到HDFS基本的Oracle示例如下
sqoop import --connect jdbc:oracle:thin:@xx.xx.xx.xx:1521:xxxdb --username 用户名 --password密码 --query "selectc1,c2,xxxx,xxx from 数据表 where \$CONDITIONS " --target-dir /data/sqoop/oracle表名/$vardate --input-null-string '\\N' --input-null-non-string '\\N' --split-by id --fields-terminated-by '\001' -m 1
2.我给客户写的一个同步的小shell程序
#!/usr/bin/env bash
vardate="ydb_default_partion"
varmonth="ydb_default_partion"
echo $vardate
echo $varmonth
#----step one ---
echo "begin step one"
hadoop fs -mkdir -p /data/sqoop/table_exampel/$vardate
hadoop fs -rm -r /data/sqoop/table_exampel/$vardate
sqoop import --connect jdbc:sap://10.36.11.108:30015 --driver com.sap.db.jdbc.Driver --username oracleUser --password oraclepasswd --query "select * FROM oracle_table_name WHERE \$CONDITIONS" --target-dir /data/sqoop/table_exampel/$vardate --split-by xh --fields-terminated-by '\001' -m 1
#----step two ---
echo "begin step two"
echo "plaase create table_exampel_txt on hive first "
echo "ALTER TABLE table_exampel_txt DROP IF EXISTS PARTITION (event_day='$vardate'); " > /ydb/daoru.sql
echo "ALTER TABLE table_exampel_txt ADD IF NOT EXISTS PARTITION (event_day='$vardate') LOCATION '/data/sqoop/table_exampel/$vardate'; " >> /ydb/daoru.sql
echo "select * from table_exampel_txt where event_day='$vardate'; " >> /ydb/daoru.sql
sh /opt/ydbsoftware/ya100/bin/sql.sh -f /ydb/daoru.sql
#----step three ---
echo "begin step three"
cat << EOF >/ydb/daoru.sql
insert into table ydbpartion
select 'table_exampel', '$varmonth', '',
YROW(
'ID',ID,
'xxx1',xx1,
'xx2',xx2,
'FLAG',FLAG
)
from table_exampel_txt where event_day='$vardate';
/*ydb.pushdown('->')*/
select count(*) from table_exampel where ydbpartion = "$varmonth"
/*('<-')pushdown.ydb*/
EOF
sh /opt/ydbsoftware/ya100/bin/sql.sh -f /ydb/daoru.sql
echo "execute finished"
七、如何将HBase中的数据导入到YDB里
默认Hive提供了与HBase对接的API,但是放在了Spark中兼容性不是特别好。YDB修改了这个地方,以便为了能让其运行,但是实测性能一般。
1.通过Spark可以与HBase进行连接,
这样就可以方便的将数据从HBase中导入到Ydb中了
具体原理参考http://www.tuicool.com/articles/N36Brm
l第1步,复制相关依赖的jar包到ya100的lib目录下(注意,这里极容易产生jar包冲突,比如hive与hbase版本不匹配)
如:
$HIVE_HOME/lib/hive-hbase-handler-0.13.1.jar
$HBASE_HOME/lib/hbase-client-0.96.1.1-cdh5.0.0.jar
$HBASE_HOME/lib/hbase-common-0.96.1.1-cdh5.0.0.jar
$HBASE_HOME/lib/hbase-protocol-0.96.1.1-cdh5.0.0.jar
$HBASE_HOME/lib/hbase-server-0.96.1.1-cdh5.0.0.jar
$HBASE_HOME/lib/htrace-core-2.01.jar
$HBASE_HOME/lib/protobuf-java-2.5.0.jar
$HBASE_HOME/lib/guava-12.0.1.jar
并且 配置环境变量,记得jar包名字别写错了
export SPARK_CLASSPATH=/home/ycloud/ya100/lib/guava-12.0.1.jar:/home/ycloud/ya100/lib/hive-hbase-handler-1.2.1.2.3.4.0-3485.jar:/home/ycloud/ya100/lib/hbase-client.jar:/home/ycloud/ya100/lib/hbase-common.jar:/home/ycloud/ya100/lib/hbase-protocol.jar:/home/ycloud/ya100/lib/hbase-server.jar:/home/ycloud/ya100/lib/htrace-core-3.1.0-incubating.jar:/home/ycloud/ya100/lib/protobuf-java-2.5.0.jar
l第2步,配置HBase的Master地址
将hbase-site.xml 的内容复制到hive-site.xml
l第3步,创建Hive与HBase的连接表(HBase默认的HBaseStorageHandler在Spark下有兼容性的问题,我稍微改了下)
CREATE TABLE hbase_table(
phonenum bigint, usernick string, ydb_sex string, ydb_province string, ydb_grade string, ydb_age string, ydb_blood string, ydb_zhiye string, ydb_earn string, ydb_prefer string, ydb_consume string, ydb_day string, amtdouble double,amtlong int, content string
)
STORED BY 'cn.net.ycloud.ydb.handle.HBaseStorageHandler_YDB'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,cf1:usernick,cf1:ydb_sex,cf1:ydb_province,cf1:ydb_grade,cf1:ydb_age,cf1:ydb_blood,cf1:ydb_zhiye,cf1:ydb_earn,cf1:ydb_prefer,cf1:ydb_consume,cf1:ydb_day,cf1:amtdouble,cf1:amtlong,cf1:content")
TBLPROPERTIES ("hbase.table.name" = "hive_hbase_2016");
2.读取HBase中的数据到文本形式的Hive表中
insert overwrite table ydb_example_txt select *,'hbase2ydb',1 from hbase_table;
ps:本质上可以直接导入到YDB中,但是实测发现,这种方式导入的数据,Map数量并不均衡,有比较严重的数据倾斜,性能不好,所以我们不在给出直接导入到YDB中的例子,而是先导到Hive的文本表,然后通过文本表在导入到YDB中
3.从文本表导入到YDB总
使用方法,请参考前文,这里不做重复介绍。
八、YDB系统表,日志表
1.启用系统表
YDB内置日志表,需要通过Kafka来收集消息,如果要启动内部日志表,需要在ydb_site.yaml里配置如下Kafka参数
ydb.reader.ydb_syslog: 'true'
kafka.topic.ydb_syslog: 'ydb_syslog'
bootstrap.servers.ydb_syslog: 'ydbmaster:6667'
2. --数据预览
/*ydb.pushdown('->')*/
select * from y_system_log order by log_time desc
limit 30
/*('<-')pushdown.ydb*/
;
3.--查看最近执行过的SQL(HIVE)
/*ydb.pushdown('->')*/
select log_time,log_day,reqid_syn,timelen_lyn,log_hour,log_category,log_type,log_pos,log_ip,log_pid,log_execid,log_execindex,log_fields,log_hostname,log_msg from y_system_log where log_category='SQL' and (log_type='getExecuteSql') order by reqid_syn desc,log_time desc
limit 30
/*('<-')pushdown.ydb*/
;
4. --按照ID查看特定某一SQL
/*ydb.pushdown('->')*/
select log_time,log_day,reqid_syn,timelen_lyn,log_hour,log_category,log_type,log_pos,log_ip,log_pid,log_execid,log_execindex,log_fields,log_hostname,log_msg from y_system_log where log_category='SQL' and reqid_syn='161204024000000002' order by log_time desc
limit 30
/*('<-')pushdown.ydb*/
;
5. --查看系统报错
/*ydb.pushdown('->')*/
select log_time,log_day,log_hour,log_category,log_type,log_pos,log_ip,log_pid,log_execid,log_execindex,log_fields,log_hostname,log_msg from y_system_log where log_category='ERROR' order by log_time desc
limit 30
/*('<-')pushdown.ydb*/
;
6. --查看系统负载
/*ydb.pushdown('->')*/
select log_time,log_day,log_hour,log_category,log_type,log_pos,log_ip,log_pid,log_execid,log_execindex,log_hostname,os_cpu_user_lyn,os_cpu_nick_lyn,os_cpu_sys_lyn,os_cpu_idle_lyn,os_iowait_lyn,os_steal_lyn,os_mem_total_lyn,os_mem_free_lyn,os_mem_used_lyn,os_mem_buff_lyn,os_mem_cache_lyn from y_system_log where log_category='SYSTEMINFO' order by log_time desc
limit 30
/*('<-')pushdown.ydb*/
;
7. --线程池
/*ydb.pushdown('->')*/
select log_time,log_day,log_hour,log_category,log_type,log_pos,log_ip,log_pid,log_execid,log_execindex,log_fields,log_hostname,log_msg from y_system_log where log_category='POOL' order by log_time desc
limit 30
/*('<-')pushdown.ydb*/
;
8. --统计
/*ydb.pushdown('->')*/
select log_type,count(*) as cnt from y_system_log group by log_type order by cnt desc
limit 30
/*('<-')pushdown.ydb*/
;
/*ydb.pushdown('->')*/
select log_day,count(*) as cnt from y_system_log group by log_day order by cnt desc
limit 30
/*('<-')pushdown.ydb*/
;
/*ydb.pushdown('->')*/
select log_category,count(*) as cnt from y_system_log group by log_category order by cnt desc
limit 30
/*('<-')pushdown.ydb*/
;
/*ydb.pushdown('->')*/
select log_category,log_type,log_pos,count(*) as cnt from y_system_log group by log_category,log_type,log_pos order by cnt desc
limit 30
/*('<-')pushdown.ydb*/
;
九、10万列规模以上的大宽表如何处理?
Hive表并不支持太多的列,对于Oracle来说最多支持1000个列,如果我们的业务有一个将近一万个列的大宽表,应该怎么办?
1.传统做法存在什么问题?
如果我们采用传统做法,直接写一个10万列的create table语句。
l12W列意味着生成的schema文件就要20多m,由于太大,读取这个表的配置一是耗费很多内存,二是解析也很慢。
lLucene内部的列也很多,在打开索引文件的时候,由于要加载列的信息,索引文件的打开会特别慢。
l12w列在Hive表中(Spark 的Hive SQL模式),也意味着占用太多的元数据库信息,而且建表也容易失败,要知道Hadoop的configuration也是有大小限制的。
2.应该如何做?
l数据类型相近的列,经常一起访问的列,推荐分组并合并,YDB内置有多值列,可以用这一个列存储,这一个组内多个不同的值,这样值的个数没有少,但是列数少了很多。更多的请参考多值列的示例。
l有些列可以采用动态列,减少生成的schema.xml的大小。
lya100_env.sh 如下配置都要改,要至少分配20G以上内存
export YA100_MEMORY=25000m
export YA100_DRIVER_MEMORY=15000m
十、使用帆软FineReport生成基于YDB数据的报表
FineReport是由帆软自主研发的一款纯Java编写的报表软件产品,集数据展示(报表)和数据录入(表单)功能于一身,能够制作复杂的报表,操作简单易用。针对软件开发商和系统集成商,用于快速构建企业信息系统的中国式Web报表软件。由于帆软支持jdbc接入,故YDB也可以与帆软集成。
下面为集成的示例:
1.报表类型
2.常用的图表种类
3.首先将HIVE的JDBC驱动Jar包
hive-jdbc-1.2.1-standalone.jar(该jar包可在ydb的libexec中找到)
复制到${FineReport_Home}\WebReport\WEB-INF\lib中
${FineReport_Home}:FineReport的根目录
4.创建JDBC连接
打开菜单:服务器①->定义数据连接②创建一个HIVE的JDBC连接
5.创建数据库连接
点击加号创建一个数据连接①,驱动器栏输入(org.apache.hive.jdbc.HiveDriver)②,
在URL中输入(jdbc:hive2://ydbmaster:10009/default)其中ydbmaster为所要连接的主机域名③,配置完成后点击测试连接,检查是否配置正确④
6.创建一个模板数据集
7.编辑数据集
选择上一步创建的数据库连接①,在白色输入框中写入sql语句,YDB SQL需要使用特殊的标签包裹②而HIVE SQL则不需要,在WHERE条件的开始处添加一条必定为真的条件,例如性别字段中的值不为0③。
使用函数${if(len(字段名) == 0,"默认值或空","and 字段名 = '" + 变量名 + "'")}来设定如果值为空则不查询或查询默认值④,点击刷新⑤会在下方显示设定的参数,可通过修改参数值来向sql中注入查询参数⑥。
在编辑好sql语句之后点击预览来查看sql语句是否能够成功执行⑦。
在⑧处可选择将所有查询结果都存储到内存中,还是当记录数大于定值的时候将剩余数据缓存到磁盘。
示例sql: 注意,帆软值SQL中支持写if模板变量,注意图中加粗的部分
/*ydb.pushdown('->')*/
select ydb_sex,ydb_grade,ydb_age,ydb_blood,amtlong
from ydb_example_shu
where ydb_sex!=0
${if(len(ydbpartion) == 0,"and ydbpartion=\'ydb_default_partion\'","and ydbpartion = '" +ydbpartion + "'")}
${if(len(ydb_sex) == 0,"","and ydb_sex = '" + ydb_sex + "'")}
${if(len(ydb_grade) == 0,"","and ydb_grade = '" + ydb_grade + "'")}
and (ydb_age='20到30岁' or ydb_blood='O')
and (amtlong like '([3000 TO 4000] )')
${if(len(limit) == 0," limit 100 "," limit "+limit+"")}
/*('<-')pushdown.ydb*/
8.编辑模版数据集
1)在上一步中我们设定了四个查询字段参数:ydb_grade,ydb_sex,limit,ydbpartion,在此处我们需要创建相同名称的模板参数来关联它们,点击模板①->模板参数②进入模板参数编辑菜单,
2)点击①添加一个模板参数,双击②修改参数名称,可在③处设置参数的默认值
9.生成查询表单
点击此处编辑查询表单①。
此处是上一步创建的模板参数①,点击”全部添加”②创建对应参数的查询表单
点击此处选择表单框的类型
表单框的类型有很多,此处我们选择‘文本控件’。
10.添加报表类型聚合块
将‘报表类型聚合块’拖拽至报表的空白区域
在图表的第一行输入列名,将数据模板中的数据列拖拽到对应的列名下
拖动右上和左下的灰色块可以调节表的列数和行数①,右下的灰色块可以移动图表的位置②
11.预览
点击左上的笔记本图标下的小箭头①,选择数据分析②
在弹出页面的对应表单中输入查询参数,点击查询
12.添加饼图
将饼图模块拖拽至报表空白区域,点击饼图区域,在屏幕右上角图表属性表区域中选择‘数据’标签页,定义对应的规则
由于报表类型聚合块大小不定,建议将其放在报表的下方
注:柱形图,条形图,折线图的设置方法与饼图相同。
预览
十一、实时消息接入自定义Reader与Parser
YDB支持自定义任意的数据源接入,以及解析任意格式的数据,只需要您实现Reader与Parser的接口
1.Reader需要实现的普通接口
publicinterface RawDataReader
{
publicvoid init(Stringprefix,KVgetconfig,intreaderIndex,intreaderCount)
throws IOException;
public Listthrows IOException;
publicvoid close()throws IOException;
publicvoid setParser(Parserparser);
}
2.事务性Reader的接口
如果担心进程异常退出,可以进一步实现如下的接口,与binlog结合保证数据不丢失
publicinterface YdbRawDataReaderWithCommitextends YdbRawDataReader
{
public YdbParse Parse(byte[]ahead )throws Throwable;
publiclong walWrite(YdbParsedoclist)throws Throwable;
publicvoid walMayCommit(longmaxcnt,ArrayList
publicvoid process(YdbParsedoclist)throws Throwable;
}
3.Parser需要实现的接口
默认我们只有JSON的Parser,如果我们的数据不是YDB的Json个数,就需要自己实现改接口来拓展
publicinterfaceParser {
public abstractvoid init(Stringprefix,KVgetconfig,
intreaderIndex,intreaderCount)
throws IOException;
publicvoid setReaderMeta(Objectmeta);
public YdbParse parse(Objectraw)throws Throwable;
4.如何使用写好的Reader
1)Reader与Parser实现完成后,将其以jar包的方式放到lib目录下
2)更改ydb_site.xml变更Parser与Reader所指向的类名
3)重启YDB
十二、自定义拓展分词
默认YDB的分词采用Lucene分词,Lucene分词的写法示例如下
集团的内部通讯工具搜同事时,需要根据姓名后缀进行搜索。譬如“徐欢春”,我们要能根据“欢春”搜出这个人;“黄继刚”,要根据“继刚”为关键字搜出“黄继刚”。这是个很人性化的用户体验,当我们有同事的名字是三个字的时候,我们通常会叫他们名字的最后两个字。Lucene本身并没有提供这种分词器,只能自己照着Lucene已有的分词器进行模仿开发。
参照ngram分词器进行开发。
要定制这个后缀分词器,实现一个Tokenizer和一个工厂类就可以了。Tokenizer类是实际分词是要用到的类。为了方便单元测试,额外增加一个Analyzer类。
分词器类
package org.apache.lucene.analysis.suffix;
import java.io.IOException;
import java.io.Reader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
/**
* 用于姓名取后缀的分词器
*
* @author cdlvsheng
* date : 2014年9月23日
* time : 下午1:49:36
* project : im-solr-web
* user : cdlvsheng
*/
public class SuffixTokenizer extends Tokenizer {
private Log LOG = LogFactory.getLog(SuffixTokenizer.class);
private char[] buffer = new char[1024];
private int suffixOffset;
private static int MAX_SIZE;
private int termSize;
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class);
protected SuffixTokenizer(Reader input) {
super(input);
}
protected SuffixTokenizer(AttributeFactory factory, Reader input) {
super(factory, input);
}
@Override
public boolean incrementToken() throws IOException {
try {
clearAttributes();
if (termSize != -1) {
termSize = input.read(buffer);
if (termSize >= 0) {
MAX_SIZE = termSize;
}
}
if (suffixOffset + 1 >= MAX_SIZE) {
return false;
}
termAtt.copyBuffer(buffer, suffixOffset, MAX_SIZE);
termAtt.setLength(MAX_SIZE - suffixOffset);
offsetAtt.setOffset(correctOffset(suffixOffset), correctOffset(MAX_SIZE));
LOG.debug(termAtt.buffer());
suffixOffset++;
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
return true;
}
@Override
public final void end() {
final int finalOffset = correctOffset(suffixOffset);
this.offsetAtt.setOffset(finalOffset, finalOffset);
}
@Override
public void reset() throws IOException {
super.reset();
termSize = 1;
suffixOffset = 1;
MAX_SIZE = 0;
}
}
incrementToken方法是实际分词时用到的方法。一会单元测试时可以看出它的作用。
工厂类
package org.apache.lucene.analysis.suffix;
import java.io.Reader;
import java.util.Map;
import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.util.TokenizerFactory;
import org.apache.lucene.util.AttributeSource.AttributeFactory;
public class SuffixTokenizerFactory extends TokenizerFactory {
public SuffixTokenizerFactory(Map
super(args);
}
@Override
public Tokenizer create(AttributeFactory factory, Reader input) {
return new SuffixTokenizer(factory, input);
}
}
工厂类的作用就是产生分词器类的实例。
analyzer类
package org.apache.lucene.analysis.suffix;
import java.io.Reader;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Tokenizer;
public final class SuffixAnalyzer extends Analyzer {
@Override
protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
final Tokenizer source = new SuffixTokenizer(reader);
return new TokenStreamComponents(source, source);
}
以上3个类合作就构成了一个完整的自定义分词器组件。
单元测试
package test.prefix.tokenizer;
import java.io.IOException;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.suffix.SuffixAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.junit.Test;
/**
* @author cdlvsheng
* date : 2014年9月22日
* time : 下午7:23:01
* project : im-solr-web
* user : cdlvsheng
*/
public class SuffixTest {
@SuppressWarnings("resource")
@Test
public void suffixTest() {
try {
SuffixAnalyzer analyzer = new SuffixAnalyzer();
TokenStream ts = analyzer.tokenStream("text", "黄继刚");
CharTermAttribute term = ts.addAttribute(CharTermAttribute.class);
ts.reset();
while (ts.incrementToken()) {
System.out.println(term.toString());
}
ts.end();
ts.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
十三、为了OEM自定义YDB UI的header与footer样式
配置参数为
ydb.web.info.header
ydb.web.info.fooder