官方提供的关系型数据库是否支持fts全文检索?具体应该如何实现。
关系型数据库没有提供直接的接口设置全文检索,需要在建表时执行SQL语句CREATE VIRTUAL TABLE语句建立FTS表,再使用MATCH操作符实现检索。
分词功能 | StoreConfig配置 | 建表语句示例 |
|---|---|---|
不使用icu分词,仅使用英文分词 | tokenizer设为NONE_TOKENIZER或不设置该字段 | CREATE VIRTUAL TABLE example USING fts4(name, content, tokenize=unicode61) |
使用icu分词器 | tokenizer设为ICU_TOKENIZER | CREATE VIRTUAL TABLE example USING fts4(name, content, tokenize=icu zh_CN) |
使用自研分词器 | tokenizer设为CUSTOM_TOKENIZER | CREATE VIRTUAL TABLE example USING fts5(name, content, tokenize=customtokenizer) |
使用自定义分词器 | 不设置tokenizer字段,配置pluginLibs字段 | CREATE VIRTUAL TABLE example USING fts5(name, content, tokenize=自定义分词器名称) |
创建数据库时tokenizer设为NONE_TOKENIZER或不设置该字段,建表时指定英文分词器类型(如:tokenize=unicode61),查询时使用MACTH关键字做全文检索。
dbConfig1: relationalStore.StoreConfig = {
name: 'fts_test1.db',
securityLevel: relationalStore.SecurityLevel.S1
// tokenizer设为NONE_TOKENIZER或不设置该字段,仅可使用默认英文分词器
};
createTableSql1: string =
`CREATE VIRTUAL TABLE IF NOT EXISTS ${this.tableName} USING fts4(title, authors, content, tokenize=unicode61)`; // 查询sql使用MATCH全文检索
querySql: string = `SELECT DISTINCT * FROM ${this.tableName} WHERE content MATCH ?;`; 创建数据库时tokenizer设为ICU_TOKENIZER,建表时指定分词器类型(如:tokenize=icu zh_CN),查询时使用MACTH关键字做全文检索。
dbConfig2: relationalStore.StoreConfig = {
name: 'fts_test2.db',
securityLevel: relationalStore.SecurityLevel.S1,
// 配置tokenizer为ICU_TOKENIZER,启用icu分词器
tokenizer: relationalStore.Tokenizer.ICU_TOKENIZER
};
createTableSql2: string =
`CREATE VIRTUAL TABLE IF NOT EXISTS ${this.tableName} USING fts4(title, authors, content, tokenize=icu zh_CN)`; // 查询sql使用MATCH全文检索
querySql: string = `SELECT DISTINCT * FROM ${this.tableName} WHERE content MATCH ?;`; 创建数据库时tokenizer设为CUSTOM_TOKENIZER,建表时指定分词器类型(如:tokenize=customtokenizer),查询时使用MACTH关键字做全文检索。
dbConfig3: relationalStore.StoreConfig = {
name: 'fts_test3.db',
securityLevel: relationalStore.SecurityLevel.S1,
// 配置tokenizer为CUSTOM_TOKENIZER,启用自研分词器
tokenizer: relationalStore.Tokenizer.CUSTOM_TOKENIZER
};
createTableSql3: string =
`CREATE VIRTUAL TABLE IF NOT EXISTS ${this.tableName} USING fts5(title, authors, content, tokenize=customtokenizer)`; // 查询sql使用MATCH全文检索
querySql: string = `SELECT DISTINCT * FROM ${this.tableName} WHERE content MATCH ?;`; 使用英文分词、icu分词器、自研分词器使用可参考如下示例代码:
import { relationalStore, ValuesBucket } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
// 预置数据
const DATA_LIST: ValuesBucket[] = [
{ 'title': '静夜思', 'authors': '李白', 'content': '床前明月光,疑是地上霜。\n举头望明月,低头思故乡。' },
{ 'title': '竹里馆', 'authors': '王维', 'content': '独坐幽篁里,弹琴复长啸。\n深林人不知,明月来相照。' },
{ 'title': '近代吴歌·秋歌', 'authors': '鲍令晖', 'content': '秋风入窗里,罗帐起飘飏。\n仰头看明月,寄情千里光。' },
{ 'title': '渌水曲', 'authors': '李白', 'content': '渌水明秋月,南湖采白蘋。\n荷花娇欲语,愁杀荡舟人。' },
{
'title': 'The Moon',
'authors': 'R. L. Stevenson',
'content': 'The moon has a face like the clock in the hall;\n' +
'She shines on thieves on the garden wall,\n' +
'On streets and fields and harbour quays,\n' +
'And birdies asleep in the forks of the trees.'
},
{
'title': 'Song to the Moon',
'authors': 'Antonín Leopold Dvořák',
'content': 'O Moon, high in the heavens, silver bright,\n' +
'You wander through the starry night.\n' +
'Tell me, where is my beloved one?\n' +
'Tell me, Moon, where has he gone?'
},
{
'title': 'Had I not seen the Sun',
'authors': 'Emily Dickinson',
'content': 'Had I not seen the Sun,\n' +
'I could have borne the shade.\n' +
'But Light a newer Wilderness,\n' +
'My Wilderness has made.'
},
];
@Entry
@Component
struct RdbFtsDemo {
@State currentIndex: number = 0;
@State selectedIndex: number = 0;
@State results: Array<relationalStore.ValuesBucket> = [];
store: relationalStore.RdbStore | undefined = undefined;
context = this.getUIContext().getHostContext() as common.UIAbilityContext;
promptAction = this.getUIContext().getPromptAction();
tableName: string = 'fts_table';
dbConfig1: relationalStore.StoreConfig = {
name: 'fts_test1.db',
securityLevel: relationalStore.SecurityLevel.S1
// tokenizer设为NONE_TOKENIZER或不设置该字段,仅可使用默认英文分词器
};
createTableSql1: string =
`CREATE VIRTUAL TABLE IF NOT EXISTS ${this.tableName} USING fts4(title, authors, content, tokenize=unicode61)`;
dbConfig2: relationalStore.StoreConfig = {
name: 'fts_test2.db',
securityLevel: relationalStore.SecurityLevel.S1,
// 配置tokenizer为ICU_TOKENIZER,启用icu分词器
tokenizer: relationalStore.Tokenizer.ICU_TOKENIZER
};
createTableSql2: string =
`CREATE VIRTUAL TABLE IF NOT EXISTS ${this.tableName} USING fts4(title, authors, content, tokenize=icu zh_CN)`;
dbConfig3: relationalStore.StoreConfig = {
name: 'fts_test3.db',
securityLevel: relationalStore.SecurityLevel.S1,
// 配置tokenizer为CUSTOM_TOKENIZER,启用自研分词器
tokenizer: relationalStore.Tokenizer.CUSTOM_TOKENIZER
};
createTableSql3: string =
`CREATE VIRTUAL TABLE IF NOT EXISTS ${this.tableName} USING fts5(title, authors, content, tokenize=customtokenizer)`;
// 查询sql使用MATCH全文检索
querySql: string = `SELECT DISTINCT * FROM ${this.tableName} WHERE content MATCH ?;`;
build() {
Column({ space: 20 }) {
Tabs() {
TabContent() {
this.buttons();
}.tabBar(this.tabBuilder(0, '英文分词'));
TabContent() {
this.buttons();
}.tabBar(this.tabBuilder(1, 'icu中文分词'));
TabContent() {
this.buttons();
}.tabBar(this.tabBuilder(2, 'icu自研分词器'));
}
.barMode(BarMode.Fixed)
.onChange((index: number) => {
this.currentIndex = index;
})
.onSelected((index: number) => {
this.selectedIndex = index;
this.results = [];
this.deleteRdb();
});
}
.justifyContent(FlexAlign.Center)
.height('100%')
.width('100%');
}
@Builder
tabBuilder(index: number, name: string) {
Column() {
Text(name)
.fontColor(this.selectedIndex === index ? '#0A59F7' : '#000000')
.fontSize(16)
.fontWeight(this.selectedIndex === index ? 500 : 400)
.lineHeight(22)
.margin({ top: 17, bottom: 7 });
Divider()
.strokeWidth(2)
.color('#0A59F7')
.opacity(this.selectedIndex === index ? 1 : 0);
}.width('100%');
}
@Builder
buttons() {
Column({ space: 20 }) {
ForEach(this.results, (data: relationalStore.ValuesBucket) => {
TextArea({
text: `${data['title'] as string}(${data['authors'] as string})\n\n${data['content'] as string}`
})
.backgroundColor('#D1D1D6')
.fontSize(15)
.margin({ left: 20, right: 20 });
});
Button('初始化fts表数据')
.onClick(() => {
this.initRdb();
});
Button('查询数据')
.onClick(() => {
this.queryData();
});
};
}
initRdb() {
// 1.创建数据库实例
let dbConfig = this.getDbConfig();
relationalStore.getRdbStore(this.context, dbConfig).then((rdbStore: relationalStore.RdbStore) => {
this.store = rdbStore;
try {
if (rdbStore != undefined) {
// 监听sql错误信息
rdbStore.on('sqliteErrorOccurred', exceptionMessage => {
let sqliteCode = exceptionMessage.code;
let sqliteMessage = exceptionMessage.message;
let errSQL = exceptionMessage.sql;
console.error(`error log is ${sqliteCode}, errMessage is ${sqliteMessage}, errSQL is ${errSQL}`);
});
}
} catch (err) {
let code = (err as BusinessError).code;
let message = (err as BusinessError).message;
console.error(`Register observer failed, code is ${code},message is ${message}`);
}
console.info('initRdb successfully.');
// 2.创建fts表
this.createTable().then(() => {
// 3.预置表数据
this.insertData();
this.promptAction.showToast({ message: `初始化成功` });
});
})
.catch((err: BusinessError) => {
console.error(`initRdb failed, code is ${err.code},message is ${err.message}`);
});
}
async createTable() {
try {
if (this.store) {
let createSql = this.createTableSql1;
switch (this.currentIndex) {
case 1:
createSql = this.createTableSql2;
break;
case 2:
createSql = this.createTableSql3;
break;
default:
createSql = this.createTableSql1;
break;
}
await this.store.executeSql(createSql);
console.info(`executeSql successful. 创建fts表成功`);
}
} catch (err) {
let code = (err as BusinessError).code;
let message = (err as BusinessError).message;
console.error(`createTableAndInsertData failed, code is ${code},message is ${message}`);
}
}
insertData() {
try {
if (this.store) {
let insertName = this.store.batchInsertSync(this.tableName, DATA_LIST);
console.info(`batchInsertSync successful. insertName= ${insertName}`);
}
} catch (err) {
let code = (err as BusinessError).code;
let message = (err as BusinessError).message;
console.error(`batchInsertSync failed, code is ${code},message is ${message}`);
}
}
queryData() {
try {
if (this.store) {
let keywords = 'sun';
switch (this.currentIndex) {
case 1:
case 2:
keywords = '明月';
break;
default:
keywords = 'moon';
break;
}
let resultSet = this.store.querySqlSync(this.querySql, [keywords]);
console.info(`ResultSet column names: ${resultSet.columnNames}, row count: ${resultSet.rowCount}`);
let rows: Array<relationalStore.ValuesBucket> = [];
while (resultSet.goToNextRow()) {
let row: relationalStore.ValuesBucket = resultSet.getRow();
rows.push(row);
}
// 释放数据集的内存
resultSet.close();
if (rows && rows.length > 0) {
this.results = rows;
}
console.info(`RelationalStoreDemo remoteQuery rows= ${JSON.stringify(rows)}`);
}
} catch (err) {
let code = (err as BusinessError).code;
let message = (err as BusinessError).message;
console.error(`querySqlSync failed, code is ${code},message is ${message}`);
}
}
getDbConfig() {
let dbConfig = this.dbConfig1;
switch (this.currentIndex) {
case 1:
dbConfig = this.dbConfig2;
break;
case 2:
dbConfig = this.dbConfig3;
break;
default:
dbConfig = this.dbConfig1;
break;
}
return dbConfig;
}
deleteRdb() {
if (this.store) {
let dbConfig = this.getDbConfig();
relationalStore.deleteRdbStore(this.context, dbConfig).then(() => {
this.store = undefined;
console.info('deleteRdbStore successfully.');
})
.catch((err: BusinessError) => {
console.error(`deleteRdbStore failed, code is ${err.code},message is ${err.message}`);
});
}
}
} Q:使用官方提供的关系型数据库创建分词器为icu zh_CN的fts5类型表时报错。
A:关系型数据库(Relational Database,RDB)基于SQLite组件实现,对全文搜索的支持与SQLite保持一致:fts5不支持ICU分词器,若需要使用ICU中文分词器请使用fts3/fts4实现或使用pluginLibs加载开发者自定义分词器实现。
Q:关系型数据库创建fts时使用语句CREATE VIRTUAL TABLE IF NOT EXISTS tablename USING fts4(fts_id UNINDEXED, title, content, tokenize='icu "zh_CN"')报错tokenize='icu "zh_CN"'不支持。
A:关系型数据库对SQL语句校验规则与SQLite保持一致,SQLite并不支持该写法,请更改为tokenize=icu "zh_CN"或tokenize=icu zh_CN。
Q:关系型数据库是否支持中文分词单字检索?
A:关系型数据库当前提供icu分词器和自研分词器都支持中文分词,但都是基于词条实现分词,不支持中文单字检索。若需要使用中文单字检索,可使用pluginLibs加载开发者自定义分词器实现。
Q:关系型数据库是否支持unicode61分词器,是否支持通过categories指定分词器保留的字符类型?
A:支持,使用如下建表语句可实现:CREATE VIRTUAL TABLE example USING fts5(name, content, tokenize="unicode61 categories 'L* N*'")。
合作咨询
我们的专家服务团队将竭诚为您提供专业的合作咨询服务
解决方案
精准高效的一站式服务支持,助力开发者商业成功