mysql 中文搜索插件_zg手册 之 Mysql 开发(1)-- 中文全文检索插件开发

目前的中文检索查询方案

基于数据库的模糊匹配(运行时字符串查找,查询速度比较慢)

专有的全文检索引擎(sphinx, lucene等)

我曾经遇到一个项目,数据量在百万级别,不需要高级的全文检索方式(没有复杂的匹配需求,没有复杂的过滤条件),只是需要根据关键词检索数据,当时采用的 mysql 全文检索插件的方式来满足的项目需求。

Mysql 的中文全文检索插件开发

Mysql 的 MyISAM 引擎支持第三方的全文检索插件,可以用第三方插件替换默认的全文检索插件。

在全文检索插件中提供中文分词算法,告诉MyISAM如何分词,并创建索引。

查询的时候通过插件分词,查询索引快速定位数据记录。

插件开发的具体方法

主要通过代码注释描述插件的开发方法,创建文件 tft.c,代码如下

#include 

#include 

// mysql 插件必须包含的头文件

#include 

// 这是我自己写的一个分词库,没有什么优化,可以替换为其他开源的实现。

#include 

#include 

#if !defined(__attribute__) && (defined(__cplusplus) \

|| !defined(__GNUC__)  || __GNUC__ == 2 && __GNUC_MINOR__ 

#define __attribute__(A)

#endif

// 进行插件内部状态统计的变量

static long number_of_calls= 0; /* 统计调用的次数 */

/*  tft 接口:  插件的初始化,卸载函数:

- tft_plugin_init()

- tft_plugin_deinit()

解析接口:

- tft_parse()

- tft_init()

- tft_deinit()

*/

// 我开发的分词库依赖的词库

static char* g_s_dictFile="/home/dev/work/ppr/tft/dict_chs.dic";

// 分词库的 handler

static st_darts* g_s_pDarts = NULL;

/*  插件被加载时的初始化函数

返回值

0     成功

1     失败

*/

static int tft_plugin_init(void *arg __attribute__((unused))){

// 分词器的初始化代码

g_s_pDarts = stDartsLoad(g_s_dictFile);

stLog("load tft plugin succ.");

return(0);

}

/*  插件被卸载时的资源释放函数

返回值

0     成功

1     失败

*/

static int tft_plugin_deinit(void *arg __attribute__((unused))){

// 卸载分词器

stDartsFree(g_s_pDarts);

stLog("free tft plugin succ.");

return(0);

}

/*  查询开始时运行的代码,目前不需要特殊处理 */

static int tft_init(MYSQL_FTPARSER_PARAM *param __attribute__((unused))){

return(0);

}

/*  查询结束时运行的代码,目前不需要特殊处理 */

static int tft_deinit(MYSQL_FTPARSER_PARAM *param __attribute__((unused))){

return(0);

}

/*  返回一个提取的词给 server

参数:

param              插件的解析环境

word               词

len                词长度

描述:

如果传递了 boolean 模式,则为这个词填充 boolean 元数据。

创建索引的时候调用这个函数添加索引词,查询的时候调用这个函数添加查询词

*/

static void add_word(MYSQL_FTPARSER_PARAM *param, char *word, size_t len){

// boolean 查询模式

MYSQL_FTPARSER_BOOLEAN_INFO bool_info=

{ FT_TOKEN_WORD, 0, 0, 0, 0, ' ', 0 };

if (param->mode == MYSQL_FTPARSER_FULL_BOOLEAN_INFO){

bool_info.yesno = 1;

}

// 传递词给 mysql,用来创建索引,或者查询。

param->mysql_add_word(param, word, len, &bool_info);

}

/*  英文分词简单处理,用空格分隔

param              插件环境

描述:    解析英文的文档或者查询词,传递给 mysql 的索引引擎,用来创建索引,或者进行查询。

*/

static int tft_parse_en(MYSQL_FTPARSER_PARAM *param){

char *end, *start, *docend= param->doc + param->length;

number_of_calls++;

for (end= start= param->doc;; end++)

{

if (end == docend)

{

if (end > start)

add_word(param, start, end - start);

break;

}

else if (isspace(*end))

{

if (end > start)

add_word(param, start, end - start);

start= end + 1;

}

}

return 0;

}

/*  分词函数,对文档或者查询词进行分词。如果是全英文文档,则调用英文分词。*/

#define c_uWordsCount 1024

static int tft_parse(MYSQL_FTPARSER_PARAM *param){

if (NULL == param->doc || 0 == param->length){

return 0;

}

// 统计调用次数

number_of_calls++;

st_timer stTimerType = ST_TIMER_MICRO_SEC;

char* start = param->doc;

char* docend = param->doc + param->length;

// 初始化分词 handler

struct st_wordInfo wordInfo[c_uWordsCount] = { { 0, 0, 0 } };

st_darts_state dState;

stDartsStateInit(g_s_pDarts, &dState, start, docend);

uint32_t uWordsCount = 0;

long long queryBeginTime = stTimer(stTimerType);

// 循环获取中文分词

while(uWordsCount 

&& stDartsNextWord(g_s_pDarts, &dState, &wordInfo[uWordsCount])){

++uWordsCount;

}

long long queryEndTime = stTimer(stTimerType);

stLog("result=%u, cost time=%lldus", uWordsCount, queryEndTime - queryBeginTime);

// 检测是否是英文

if(uWordsCount == 0){

tft_parse_en(param);

}

// 传递分词给 mysql ,用来创建索引,或者执行查询

for (int i = 0; i 

add_word(param, wordInfo[i].pWord, wordInfo[i].wordLen);

}

return(0);

}

/*  插件的接口声明*/

static struct st_mysql_ftparser tft_descriptor={

MYSQL_FTPARSER_INTERFACE_VERSION, /* interface version      */

tft_parse,              /* 解析函数 */

tft_init,               /* 初始函数 */

tft_deinit              /* 清理函数 */

};

/*  插件的状态变量声明*/

static struct st_mysql_show_var tft_status[]={

{"static",     (char *)"just a static text",     SHOW_CHAR},

{"called",     (char *)&number_of_calls, SHOW_LONG},

{0,0,0}

};

/*  插件定义*/

mysql_declare_plugin(tft){

MYSQL_FTPARSER_PLUGIN,      /* 类型 */

&tft_descriptor,  /* 接口声明 */

"tft",            /* 插件名称 */

"t Corp",              /* 作者 */

"t Full-Text Parser",  /* 描述 */

PLUGIN_LICENSE_GPL,

tft_plugin_init,  /* 加载函数     */

tft_plugin_deinit,/* 卸载函数 */

0x0100,                     /* 版本 */

tft_status,              /* 状态变量 */

NULL,

NULL,

0,

}

mysql_declare_plugin_end;

开发要点

选择一个高效的分词库

了解mysql插件的几个内部数据结构

struct st_mysql_ftparser_param 结构是环境参数, 包含了需要解析数据的具体信息,需要了解内容,但一般不需要修改。

typedef struct st_mysql_ftparser_param{

int (*mysql_parse)(struct st_mysql_ftparser_param *,

char *doc, int doc_len);

int (*mysql_add_word)(struct st_mysql_ftparser_param *,

char *word, int word_len,

MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info);

void *ftparser_state;

void *mysql_ftparam;

struct charset_info_st *cs;

char *doc; // 传入的需要分词的字符串

int length; // 字符串的长度

int flags;

enum enum_ftparser_mode mode;

} MYSQL_FTPARSER_PARAM;

安装全文检索插件

# 安装插件

mysql> INSTALL PLUGIN tft SONAME 'mypluglib.so';

# 卸载插件

mysql> UNINSTALL PLUGIN tft

# 查看当前已经安装的插件

mysql> show plugins;

# 查看插件变量状态

mysql> SHOW STATUS LIKE 'tft%';

+----------------------+--------------------+

| Variable_name        | Value              |

+----------------------+--------------------+

| tft_static           | just a static text |

| tft_called           | 0                  |

+----------------------+--------------------+

使用插件

# 创建表

mysql> CREATE TABLE t (c VARCHAR(255),

->   FULLTEXT (c) WITH PARSER tft

-> ) ENGINE=MyISAM;

Query OK, 0 rows affected (0.01 sec)

# 插入数据

mysql> INSERT INTO t VALUES

->   ('这是一个简单测试'),

->   ('上海 广州 北京'),

->   ('泰山 黄山 嵩山');

Query OK, 3 rows affected (0.02 sec)Records: 3  Duplicates: 0  Warnings: 0

# 查询

mysql> SELECT MATCH(c) AGAINST('上海') FROM t;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值