写点什么

H2 的全文检索功能

用户头像
Page
关注
发布于: 2020 年 05 月 22 日
H2 的全文检索功能

在前面的文章中,我们介绍了 H2 的一些特性以及 为什么H2 适合应用在测试环境中。H2 不但可以作为嵌入式数据库、内存数据库使用。在适当的场景下可以选择使用 H2 替换掉 SQLite,还可利用 H2 内存数据库的特点,将它还提供了全文检索的功能。


H2 内置了两个全文检索(FullText Search)的实现:


  1. Native FullText Search。使用 H2 中内置的全文检索,将索引存储在数据库指定的表中。

  2. Apache Lucene FullText Search。 H2 使用 Java 来进行变得,因此可以依赖第三方库来实现功能的扩展,在


1 命令行中使用 Native FullText Search


下面的例子中有主要涉及到两个表:Car(汽车)、Brand(厂商),其中涉及到一些关键词两个表中都涉及到,通过全文检索能够快速定位到数据在这两表中的位置。


1.1 创建表


创建 cars 和 brands 的表结构。


create SCHEMA TEST_SCHEMA;
create table TEST_SCHEMA.cars( id bigint generated always as identity not null, name varchar(20), introduce varchar(200), primary key (id));

create table TEST_SCHEMA.brands( id bigint generated always as identity not null, name varchar(20), primary key (id));
复制代码


创建后如下图:



1.2 创建索引


使用 FT_INIT() 来进行全文检索的初始化,初始化过程指定使用 H2 内置的全文检索功能。


create alias if not exists FT_INIT for "org.h2.fulltext.FullText.init";
CALL FT_INIT();
复制代码


执行完语句之后会创建名字为 FT 的 Schema,在 FT 中会创建几个新的表,其中 INDEXS 中存储的是建立索引的规则。



指定建立索引的表和列。


CALL FT_CREATE_INDEX('TEST_SCHEMA', 'CARS', NULL);CALL FT_CREATE_INDEX('TEST_SCHEMA', 'BRANDS', NULL);
复制代码


FT_CREATE_INDEX 函数的


​ 第一个参数指定的建立索引的 SCHEMA Name;


​ 第二个参数是建立索引的 TABLE Name;


​ 第三个参数是建立索引的列表,当为 NULL 时表示为所有列建立索引。



1.3 插入数据并查询索引


insert into TEST_SCHEMA.cars values (1, 'benz A200', 'Benz A200 L Car'), (2, 'BMW 3', 'BMW 3 2.0L');
insert into TEST_SCHEMA.brands values (1, 'benz'), (2, 'BMW');
复制代码


插入数据后,结构如下:



搜索之前我们先确定要得到的结果,通过上图,我们知道,包含关键字 benz 的关键字记录一共 有两条。


  1. cars 表中的 id 为 1 的记录,出现在 name、introduce 两列中,

  2. brands 表中 id 为 1 的记录,出现在 name 列中。


查询关键字 benz 应该得到 2 条记录;


SELECT * FROM FT_SEARCH_DATA('benz', 0, 0);
复制代码


搜索结果包含 5 个字段:


​ SCHEMA: 搜索到的记录所属的 Schema 名称;


​ TABLE: 搜索到的记录所属的 table 名称


​ COLUMNS: 搜索到的结果定位的 column 名


​ KEYS: 搜索到的结果记录对应的地址


​ SCORE: 搜索到的结果评分,在 H2 的 Native FullText Search 中 score 的值始终为 1.0


查询结果如下:



另外搜索的结果是忽略大小写的,一次搜索 BENZ 会得到的同样的搜索结果。


经过尝试,H2 内置的全文检索是按照英文字符进行分词的,数字和字母分词,如果是中文依然按照英文字符进行分词。


例如:


"马自达,创驰蓝天" 分词后为 "马自达,创驰蓝天"


"创驰蓝天,2.5L" 分词后为"创驰蓝天","2","5","L","创驰蓝天,2",创驰蓝天,2.5","创驰蓝天,2.5L", "2.5", "5L", "2.5"。


了解分词之后规则之后,在一些简单的场景中就可以使用这种简单的全文检索功能。


1.4 删除索引


# 删除指定的库call FT_DROP('TEST_SCHEMA', 'CARS');
# 删除全部索引call FT_DROP_ALL();
复制代码


2 Java 代码中使用 H2 的全文检索功能


Spring Boot 2.x 中使用的数据库连接池为 HikariCP,


application.properties


spring.datasource.schema=schema.sqlspring.datasource.data=data.sqlspring.datasource.type=org.h2.jdbcx.JdbcDataSource
spring.jpa.show-sql=truespring.jpa.hibernate.ddl-auto=update
复制代码


schema.sql


create table cars(    id   bigint generated always as identity not null,    name varchar(20),    introduce varchar(200),    primary key (id));

create table brands( id bigint generated always as identity not null, name varchar(20), primary key (id));

# 使用 H2 Native FullText Search 初始化create alias if not exists FT_INIT for "org.h2.fulltext.FullText.init";CALL FT_INIT();
# 创建索引CALL FT_CREATE_INDEX('PUBLIC', 'CARS', NULL);CALL FT_CREATE_INDEX('PUBLIC', 'BRANDS', NULL);
复制代码


data.sql


insert into cars values (1, 'benz A200', 'Benz A200 L Car'), (2, 'BMW 3', 'BMW 3 2.0L');
insert into brands values (1, 'benz'), (2, 'BMW');
复制代码


测试代码


@SpringBootTestclass FullTextSearchTests {
@Autowired private FullTextService fullTextService;
@Test void should_got_2_record_when_fulltext_search_given_2_cars_records_and_2_brands_records() throws SQLException { List<FullTextSearchResult> results = fullTextService.search("benz");
then(results.size()).isEqualTo(2); }
}
复制代码


其他依赖的类:


Brand.java


@Data@Entity@Table(name = "brands")public class Brand {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id;
private String name;}
复制代码


Car.java


@Data@Entity@Table(name = "cars")public class Car {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    private long id;
private String name;
private String introduce;}
复制代码


BrandRepository.java


@Repositorypublic interface BrandRepository extends JpaRepository<Brand, Long> {}
复制代码


CarRepository.java


@Repositorypublic interface CarRepository extends JpaRepository<Car, Long> {}
复制代码


FullTextSearchResult.java 将全文检索搜索结果封装为该类。


@Builder@Datapublic class FullTextSearchResult {
private String schema;
private String table;
private String columns;
private String keys;
private BigDecimal score;}
复制代码


FullTextService.java


@Servicepublic class FullTextService {
public static final int SEARCH_RESULT_LIMIT = 0; public static final int SEARCH_RESULT_OFFSET = 0;
public static final int SCHEMA_INDEX = 1; public static final int TABLE_INDEX = 2; public static final int COLUMNS_INDEX = 3; public static final int KEYS_INDEX = 4; public static final int SCORE_INDEX = 5;

@Autowired private DataSource dataSource;
public List<FullTextSearchResult> search(String keyword) throws SQLException { List<FullTextSearchResult> results = new ArrayList<>();
ResultSet resultSet = FullText.searchData( dataSource.getConnection(), keyword, SEARCH_RESULT_LIMIT, SEARCH_RESULT_OFFSET); while (resultSet.next()) { String schemaName = resultSet.getString(SCHEMA_INDEX); String tableName = resultSet.getString(TABLE_INDEX); Object[] columns = (Object[]) resultSet.getArray(COLUMNS_INDEX).getArray(); String column = (String) columns[0]; Object[] keys = (Object[]) resultSet.getArray(KEYS_INDEX).getArray(); String key = (String) keys[0]; BigDecimal score = resultSet.getBigDecimal(SCORE_INDEX);
results.add( FullTextSearchResult.builder() .schema(schemaName) .table(tableName) .columns(column) .keys(key) .score(score) .build()); }
return results; }}
复制代码


在提取全文检索的结果时 H2 提供的类并不能方便的使用。因此可以添加 FullText 的代理类,将常用的方法进行封装。


3 使用 Apache Lucene 的全文检索


由于 H2 是使用 Java 编写的,因此只需要引入 Apache Lucene 的类,即可进行数据库的扩展。与 Native FullText Search 不同,使用 Apache Lucene 会讲索引储存在 Lucene 之中,并可以根据 Lucene 提供的特性进行分词和索引的功能扩展。


另外当前最新版本的 H2 数据库支持 Apache Lucene 5.5 以及 8.0.x 版本。


3.1 命令行中使用 Apache Lucene 创建索引


初始化使用:org.h2.fulltext.FullTextLucene.init


create alias if not exists FTL_INIT for "org.h2.fulltext.FullTextLucene.init";CALL FTL_INIT();
复制代码


其他操作均以 FTL_ 开头的函数来进行操作,例如: FTL_SEARCH_DATA()


3.2 H2 提供的了对 Apahce Lucene 操作的封装类


可以使用 fulltext.FullTextLucene.searchData 类进行数据的检索。


更多 API 可参考H2 Database Java doc


发布于: 2020 年 05 月 22 日阅读数: 690
用户头像

Page

关注

如果一项实践有价值,那么就将它做到极致。 2011.04.17 加入

ThoughtWorks

评论 (1 条评论)

发布
用户头像
感谢分享,这篇博客放到InfoQ首页推荐。
2020 年 05 月 22 日 16:18
回复
没有更多了
H2 的全文检索功能