• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 编写全文分析器插件

    MySQL支持服务器端全文解析器插件与MyISAMInnoDB。有关全文分析器插件的介绍性信息,请参见全文分析器插件。

    全文分析器插件可用于替换或修改内置的全文分析器。本节介绍如何编写名为的全文分析器插件simple_parser。该插件基于比MySQL内置全文分析器所使用的规则更简单的规则来进行分析:单词是空格字符的非空运行。

    这些说明使用plugin/fulltextMySQL源代码分发目录中的源代码,因此将位置更改为该目录。以下过程描述了如何创建插件库:

    1. 要编写全文分析器插件,请在插件源文件中包含以下头文件。根据插件的功能和要求,可能还需要其他MySQL或常规头文件。

      #include <mysql/plugin.h>
      

      plugin.h定义MYSQL_FTPARSER_PLUGIN服务器插件类型和声明插件所需的数据结构。

    2. 设置插件库文件的库描述符。

      该描述符包含服务器插件的常规插件描述符。对于全文分析器插件,类型必须为MYSQL_FTPARSER_PLUGIN。这是一个值,用于在WITH PARSER创建FULLTEXT索引时将插件标识为在子句中合法使用的值。(此条款没有其他插件类型合法。)

      例如,包含单个名为的全文分析器插件的库的库描述符simple_parser如下所示:

      mysql_declare_plugin(ftexample)
      {
        MYSQL_FTPARSER_PLUGIN,      /* type                            */
        &simple_parser_descriptor,  /* descriptor                      */
        "simple_parser",            /* name                            */
        "Oracle Corporation",       /* author                          */
        "Simple Full-Text Parser",  /* description                     */
        PLUGIN_LICENSE_GPL,         /* plugin license                  */
        simple_parser_plugin_init,  /* init function (when loaded)     */
        simple_parser_plugin_deinit,/* deinit function (when unloaded) */
        0x0001,                     /* version                         */
        simple_status,              /* status variables                */
        simple_system_variables,    /* system variables                */
        NULL,
        0
      }
      mysql_declare_plugin_end;
      

      所述name构件(simple_parser)指示要用于如在语句中的插件的引用名称INSTALL PLUGINUNINSTALL PLUGIN。这也是由SHOW PLUGINS或显示的名称INFORMATION_SCHEMA.PLUGINS

      有关更多信息,请参见“服务器插件库和插件描述符”。

    3. 设置特定于类型的插件描述符。

      库描述符中的每个常规插件描述符都指向特定于类型的描述符。对于全文分析器插件,特定于类型的描述符是文件中st_mysql_ftparser结构的一个实例plugin.h

      struct st_mysql_ftparser
      {
        int interface_version;
        int (*parse)(MYSQL_FTPARSER_PARAM *param);
        int (*init)(MYSQL_FTPARSER_PARAM *param);
        int (*deinit)(MYSQL_FTPARSER_PARAM *param);
      };
      

      如结构定义所示,描述符具有接口版本号,并包含指向三个函数的指针。

      接口版本号使用符号指定,格式为:。对于全文分析器插件,符号为。在源代码中,您将找到在中定义的全文分析器插件的实际接口版本号。当前接口版本号为。MYSQL_xxx_INTERFACE_VERSIONMYSQL_FTPARSER_INTERFACE_VERSIONinclude/mysql/plugin_ftparser.h0x0101

      initdeinit成员应指向的函数,或者如果不需要该函数被设置为0。该parse构件必须指向函数执行所述解析。

      In the simple_parser declaration, that descriptor is indicated by &simple_parser_descriptor. The descriptor specifies the version number for the full-text plugin interface(as given by MYSQL_FTPARSER_INTERFACE_VERSION), and the plugin's parsing, initialization, and deinitialization functions:

      static struct st_mysql_ftparser simple_parser_descriptor=
      {
        MYSQL_FTPARSER_INTERFACE_VERSION, /* interface version      */
        simple_parser_parse,              /* parsing function       */
        simple_parser_init,               /* parser init function   */
        simple_parser_deinit              /* parser deinit function */
      };
      

      全文分析器插件用于两个不同的上下文中,即索引编制和搜索。在这两种情况下,服务器都会在处理导致调用该插件的每个SQL语句的开始和结束时调用初始化和取消初始化功能。但是,在语句处理期间,服务器以特定于上下文的方式调用主解析函数:

      • 对于索引,服务器为要索引的每个列值调用解析器。
      • 为了进行搜索,服务器调用解析器来解析搜索字符串。还可以为该语句处理的行调用解析器。在自然语言模式下,服务器无需调用解析器。对于布尔模式短语搜索或具有查询扩展的自然语言搜索,解析器用于解析列值以获取索引中未包含的信息。另外,如果对没有FULLTEXT索引的列进行布尔模式搜索,则将调用内置解析器。(插件与特定索引相关联。如果没有索引,则不使用插件。)

      通用插件描述符中的插件声明具有initdeinit成员,这些成员指向初始化和反初始化函数,它指向的类型特定的插件描述符也是如此。但是,这些功能对具有不同的用途,并且由于不同的原因而被调用:

      • 对于常规插件描述符中的插件声明,在加载和卸载插件时会调用初始化和反初始化函数。
      • 对于特定于类型的插件描述符,将针对使用该插件的每个SQL语句调用初始化和反初始化函数。

      插件描述符中命名的每个接口函数都应返回零(如果成功)或返回非零(失败),并且每个接口函数都接收一个参数,该参数指向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_parse:指向回调函数的指针,该回调函数调用服务器的内置解析器。当插件充当内置解析器的前端时,请使用此回调。也就是说,当调用插件解析函数时,它应处理输入以提取文本并将文本传递给mysql_parse回调。

        此回调函数的第一个参数应为param值本身:

        param->mysql_parse(param, ...);
        

        前端插件可以一次提取文本并将其全部传递给内置解析器,或者一次可以一次提取文本并将其传递给内置解析器。但是,在这种情况下,内置解析器会将文本片段视为好像它们之间存在隐含的分词符。

      • mysql_add_word:指向回调函数的指针,该回调函数将单词添加到全文索引或搜索词列表中。当解析器插件替换内置解析器时,请使用此回调。也就是说,在调用插件解析函数时,它应将输入解析为单词,并mysql_add_word为每个单词调用回调。

        此回调函数的第一个参数应为param值本身:

        param->mysql_add_word(param, ...);
        
      • ftparser_state:这是一个通用指针。该插件可以将其设置为指向内部用于其自身目的的信息。
      • mysql_ftparam:这是由服务器设置的。它作为第一个参数传递给mysql_parseor mysql_add_word回调。
      • cs:指向有关文本字符集的信息的指针;如果没有可用信息,则为0。
      • doc:指向要解析的文本的指针。
      • length:要解析的文本的长度,以字节为单位。
      • flags:解析器标志。如果没有特殊标志,则为零。唯一的非零标志是MYSQL_FTFLAGS_NEED_COPY,这意味着mysql_add_word()必须保存该单词的副本(也就是说,它不能使用指向该单词的指针,因为该单词位于将被覆盖的缓冲区中。)

        MySQL可能在调用解析器插件之前,解析器插件本身或mysql_parse()函数之前设置或重置了此标志。

      • mode:解析模式。该值将是以下常量之一:

        • MYSQL_FTPARSER_SIMPLE_MODE:以快速和简单的模式进行解析,该模式用于索引和自然语言查询。解析器应仅将应索引的单词传递给服务器。如果解析器使用长度限制或停用词列表来确定要忽略的单词,则不应将此类单词传递给服务器。
        • MYSQL_FTPARSER_WITH_STOPWORDS:以停用词模式解析。在布尔搜索中用于短语匹配。解析器应将所有单词,甚至停用词或超出任何正常长度限制的单词传递给服务器。
        • MYSQL_FTPARSER_FULL_BOOLEAN_INFO:以布尔模式解析。这用于解析布尔查询字符串。解析器不仅应识别单词,还应识别布尔模式运算符,并使用mysql_add_word回调将它们作为令牌传递给服务器。为了告诉服务器传递哪种令牌,插件需要填写一个MYSQL_FTPARSER_BOOLEAN_INFO结构并传递一个指向它的指针。
      注意

      对于MyISAM,停用词列表和ft_min_word_lenft_max_word_len在标记器内检查。对于InnoDB,停用词列表和等效词长变量设置(innodb_ft_min_token_sizeinnodb_ft_max_token_size)在标记生成器之外进行检查。因此,InnoDB插件解析器无需检查停用词列表innodb_ft_min_token_sizeinnodb_ft_max_token_size。相反,建议将所有单词都返回给InnoDB。但是,如果要在插件解析器中检查停用词,请使用MYSQL_FTPARSER_SIMPLE_MODE,用于全文搜索索引和自然语言搜索。对于MYSQL_FTPARSER_WITH_STOPWORDSMYSQL_FTPARSER_FULL_BOOLEAN_INFO模式,建议您InnoDB在短语搜索的情况下将所有单词都返回包括停用词在内的所有单词。

      如果以布尔模式调用解析器,则param->mode值为MYSQL_FTPARSER_FULL_BOOLEAN_INFO。该MYSQL_FTPARSER_BOOLEAN_INFO解析器用来传送令牌信息到服务器看起来像这样的结构:

      typedef struct st_mysql_ftparser_boolean_info
      {
        enum enum_ft_token_type type;
        int yesno;
        int weight_adjust;
        char wasign;
        char trunc;
        int position;
        /* These are parser state and must be removed. */
        char prev;
        char *quot;
      } MYSQL_FTPARSER_BOOLEAN_INFO;
      

      解析器应按以下方式填充结构成员:

      • type:令牌类型。下表显示了允许的类型。

        全文分析器令牌类型

        代币价值含义
        FT_TOKEN_EOF数据结束
        FT_TOKEN_WORD普通字
        FT_TOKEN_LEFT_PAREN组或子表达式的开始
        FT_TOKEN_RIGHT_PAREN组或子表达式的结尾
        FT_TOKEN_STOPWORD停用词

      • yesno:是否必须存在单词才能进行匹配。0表示该单词是可选的,但如果存在则增加匹配的相关性。大于0的值表示该单词必须存在。小于0的值表示该单词不能出现。
      • weight_adjust:一个加权系数,用于确定单词匹配的数量。它可用于在相关性计算中增加或减少单词的重要性。零值表示没有重量调整。大于或小于零的值分别表示较高或较低的重量。在这些例子第12.9.2,“布尔全文搜索”使用的,<以及>运营商说明如何加权作品。
      • wasign:加权因子的符号。负值的作用类似于~布尔搜索运算符,它使单词对相关性的贡献为负。
      • trunc:是否应该像*给出布尔模式截断运算符一样进行匹配。
      • position:单词在文档中的开始位置,以字节为单位。用于InnoDB全文搜索。对于以布尔模式调用的现有插件,必须添加对position成员的支持。

      插件不应使用结构的prevquot成员MYSQL_FTPARSER_BOOLEAN_INFO

      注意

      插件解析器框架不支持:

      • @distance布尔运算符。
      • 前导加号(+)或减号(-)布尔运算符,后跟空格,然后是单词('+ apple''- apple')。前导正号或负号必须直接与单词相邻,例如:'+apple''-apple'

      有关布尔全文搜索运算符的信息,请参见“布尔全文搜索”。

    4. 设置插件界面功能。

      库描述符中的常规插件描述符指定服务器在加载和卸载插件时应调用的初始化和取消初始化函数。对于simple_parser,这些函数除了返回零以表示已成功之外,什么也不做:

      static int simple_parser_plugin_init(void *arg __attribute__((unused)))
      {
        return(0);
      }
      
      static int simple_parser_plugin_deinit(void *arg __attribute__((unused)))
      {
        return(0);
      }
      

      由于这些函数实际上没有执行任何操作,因此您可以忽略它们,并在插件声明中为每个函数指定0。

      特定于类型的插件描述符,用于simple_parser命名使用插件时服务器调用的初始化,取消初始化和解析功能。对于simple_parser,初始化和取消初始化功能什么都不做:

      static int simple_parser_init(MYSQL_FTPARSER_PARAM *param
                                    __attribute__((unused)))
      {
        return(0);
      }
      
      static int simple_parser_deinit(MYSQL_FTPARSER_PARAM *param
                                      __attribute__((unused)))
      {
        return(0);
      }
      

      在这里,由于这些功能什么也没做,因此您可以忽略它们,并在插件描述符中为每个函数指定0。

      主要的解析功能simple_parser_parse()代替了内置的全文分析器,因此它需要将文本拆分为单词并将每个单词传递给服务器。解析函数的第一个参数是指向包含解析上下文的结构的指针。该结构的一个doc成员指向要解析的文本,另一个length成员指示文本有多长时间。插件完成的简单解析将空白字符的非空运行视为单词,因此它可以识别如下单词:

      static int simple_parser_parse(MYSQL_FTPARSER_PARAM *param)
      {
        char *end, *start, *docend= param->doc + param->length;
      
        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);
      }
      

      解析器找到每个单词时,它将调用一个函数add_word()将单词传递给服务器。add_word()仅是一个辅助功能;它不是插件界面的一部分。解析器将解析上下文指针传递到add_word(),并将单词和长度值的指针传递给:

      static void add_word(MYSQL_FTPARSER_PARAM *param, char *word, size_t len)
      {
        MYSQL_FTPARSER_BOOLEAN_INFO bool_info=
          { FT_TOKEN_WORD, 0, 0, 0, 0, 0, ' ', 0 };
      
        param->mysql_add_word(param, word, len, &bool_info);
      }
      

      对于布尔模式分析,请按照前面在结构讨论中所述的那样add_word()填充bool_info结构的成员st_mysql_ftparser_boolean_info

    5. 设置状态变量。对于simple_parser插件,以下状态变量数组设置一个状态变量,其值是静态文本,而另一个状态变量的值存储在长整数变量中:

      long number_of_calls= 0;
      
      struct st_mysql_show_var simple_status[]=
      {
        {"simple_parser_static", (char *)"just a static text", SHOW_CHAR},
        {"simple_parser_called", (char *)&number_of_calls,     SHOW_LONG},
        {0,0,0}
      };
      

      通过使用以插件名称开头的状态变量名称,您可以使用以下命令轻松显示插件的变量SHOW STATUS

      mysql> SHOW STATUS LIKE 'simple_parser%';
      +----------------------	+--------------------	+
      | Variable_name	| Value	|
      +----------------------	+--------------------	+
      | simple_parser_static	| just a static text	|
      | simple_parser_called	| 0	|
      +----------------------	+--------------------	+
      
    6. 要编译和安装插件库文件,请使用“编译和安装插件库”中的说明。要使该库文件可供使用,请将其安装在插件目录(由plugin_dir系统变量命名的目录)中。对于该simple_parser插件,当您从源代码构建MySQL时,将对其进行编译和安装。它也包含在二进制发行版中。构建过程将生成一个共享对象库,其名称为mypluglib.so.so后缀可能因平台而异)。
    7. 要使用插件,请在服务器上注册。例如,要在运行时注册插件,请使用以下语句(.so根据需要调整平台的后缀):

      INSTALL PLUGIN simple_parser SONAME 'mypluglib.so';
      

      有关插件加载的更多信息,请参见“MySQL服务器插件”。

    8. 要验证插件安装,请检查INFORMATION_SCHEMA.PLUGINS表或使用以下SHOW PLUGINS语句。请参见“获取服务器插件信息”。
    9. 测试插件以验证其是否正常运行。

      创建一个包含字符串列的表,并将解析器插件与FULLTEXT该列上的索引相关联:

      mysql> CREATE TABLE t (c VARCHAR(255),
      ->   FULLTEXT (c) WITH PARSER simple_parser
      -> ) ENGINE=MyISAM;
      Query OK, 0 rows affected (0.01 sec)
      

      在表中插入一些文本,然后尝试进行一些搜索。这些应该验证解析器插件将所有非空白字符都视为单词字符:

      mysql> INSERT INTO t VALUES
      ->   ('utf8mb4_0900_as_cs is a case-sensitive collation'),
      ->   ('I\'d like a case of oranges'),
      ->   ('this is sensitive information'),
      ->   ('another row'),
      ->   ('yet another row');
      Query OK, 5 rows affected (0.02 sec)
      Records: 5  Duplicates: 0  Warnings: 0
      
      mysql> SELECT c FROM t;
      +--------------------------------------------------	+
      | c	|
      +--------------------------------------------------	+
      | utf8mb4_0900_as_cs is a case-sensitive collation	|
      | I'd like a case of oranges	|
      | this is sensitive information	|
      | another row	|
      | yet another row	|
      +--------------------------------------------------	+
      5 rows in set (0.00 sec)
      
      mysql> SELECT MATCH(c) AGAINST('case') FROM t;
      +--------------------------	+
      | MATCH(c) AGAINST('case')	|
      +--------------------------	+
      |                        0	|
      |          1.2968142032623	|
      |                        0	|
      |                        0	|
      |                        0	|
      +--------------------------	+
      5 rows in set (0.00 sec)
      
      mysql> SELECT MATCH(c) AGAINST('sensitive') FROM t;
      +-------------------------------	+
      | MATCH(c) AGAINST('sensitive')	|
      +-------------------------------	+
      |                             0	|
      |                             0	|
      |               1.3253291845322	|
      |                             0	|
      |                             0	|
      +-------------------------------	+
      5 rows in set (0.01 sec)
      
      mysql> SELECT MATCH(c) AGAINST('case-sensitive') FROM t;
      +------------------------------------	+
      | MATCH(c) AGAINST('case-sensitive')	|
      +------------------------------------	+
      |                    1.3109166622162	|
      |                                  0	|
      |                                  0	|
      |                                  0	|
      |                                  0	|
      +------------------------------------	+
      5 rows in set (0.01 sec)
      
      mysql> SELECT MATCH(c) AGAINST('I\'d') FROM t;
      +--------------------------	+
      | MATCH(c) AGAINST('I\'d')	|
      +--------------------------	+
      |                        0	|
      |          1.2968142032623	|
      |                        0	|
      |                        0	|
      |                        0	|
      +--------------------------	+
      5 rows in set (0.01 sec)
      

      “大小写”和“不区分大小写”都没有像内置解析器那样匹配“不区分大小写”。