zgマニュアルのMysql開発(1)--中国語全文検索プラグイン開発


現在の中国語検索クエリースキーム
  • データベースベースのあいまいな一致(実行時の文字列検索、クエリー速度が遅い)
  • 独自の全文検索エンジン(sphinx,luceneなど)
  • 私はかつて1つのプロジェクトに出会って、データ量は百万レベルで、高級な全文検索方式(複雑なマッチング需要がなくて、複雑なフィルタ条件がありません)を必要としないで、ただキーワードによってデータを検索する必要があって、当時採用したmysql全文検索プラグインの方式は十分なプロジェクトの需要を満たします.
    Mysqlの中国語全文検索プラグイン開発
  • MysqlのMyISAMエンジンはサードパーティの全文検索プラグインをサポートし、デフォルトの全文検索プラグインをサードパーティのプラグインに置き換えることができます.
  • 全文検索プラグインには、MyISAMがどのように分詞するかを教え、インデックスを作成する中国語の分詞アルゴリズムが提供されています.
  • クエリー時にプラグイン分詞により、クエリーインデックスがデータレコードをすばやく位置決めします.

  • プラグイン開発の具体的な方法
    主にコード注釈によりプラグインの開発方法を記述し、ファイルtftを作成する.c,コードは以下の通りである.
    #include <stdlib.h>
    #include <ctype.h>
    
    // mysql           
    #include <mysql/plugin.h>
    
    //             ,      ,            。
    #include <st_darts.h>
    #include <st_utils.h>
    
    #if !defined(__attribute__) && (defined(__cplusplus) \
    || !defined(__GNUC__)  || __GNUC__ == 2 && __GNUC_MINOR__ < 8)
    #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 < c_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 < uWordsCount; ++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;

    Mysqlフルテキスト検索プラグインの開発ドキュメント
    原文リンク:zgマニュアルのMysql開発(1)--中国語全文検索プラグイン開発