ft_nlq_search.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:9k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
  2.    This program is free software; you can redistribute it and/or modify
  3.    it under the terms of the GNU General Public License as published by
  4.    the Free Software Foundation; either version 2 of the License, or
  5.    (at your option) any later version.
  6.    This program is distributed in the hope that it will be useful,
  7.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  8.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  9.    GNU General Public License for more details.
  10.    You should have received a copy of the GNU General Public License
  11.    along with this program; if not, write to the Free Software
  12.    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
  13. /* Written by Sergei A. Golubchik, who has a shared copyright to this code */
  14. #define FT_CORE
  15. #include "ftdefs.h"
  16. /* search with natural language queries */
  17. typedef struct ft_doc_rec
  18. {
  19.   my_off_t  dpos;
  20.   double    weight;
  21. } FT_DOC;
  22. struct st_ft_info
  23. {
  24.   struct _ft_vft *please;
  25.   MI_INFO  *info;
  26.   int       ndocs;
  27.   int       curdoc;
  28.   FT_DOC    doc[1];
  29. };
  30. typedef struct st_all_in_one
  31. {
  32.   MI_INFO    *info;
  33.   uint       keynr;
  34.   CHARSET_INFO *charset;
  35.   uchar      *keybuff;
  36.   TREE       dtree;
  37. } ALL_IN_ONE;
  38. typedef struct st_ft_superdoc
  39. {
  40.     FT_DOC   doc;
  41.     FT_WORD *word_ptr;
  42.     double   tmp_weight;
  43. } FT_SUPERDOC;
  44. static int FT_SUPERDOC_cmp(void* cmp_arg __attribute__((unused)),
  45.    FT_SUPERDOC *p1, FT_SUPERDOC *p2)
  46. {
  47.   if (p1->doc.dpos < p2->doc.dpos)
  48.     return -1;
  49.   if (p1->doc.dpos == p2->doc.dpos)
  50.     return 0;
  51.   return 1;
  52. }
  53. static int walk_and_match(FT_WORD *word, uint32 count, ALL_IN_ONE *aio)
  54. {
  55.   int        subkeys, r;
  56.   uint        keylen, doc_cnt;
  57.   FT_SUPERDOC  sdoc, *sptr;
  58.   TREE_ELEMENT *selem;
  59.   double       gweight=1;
  60.   MI_INFO      *info=aio->info;
  61.   uchar        *keybuff=aio->keybuff;
  62.   MI_KEYDEF    *keyinfo=info->s->keyinfo+aio->keynr;
  63.   my_off_t     key_root=info->s->state.key_root[aio->keynr];
  64.   uint         extra=HA_FT_WLEN+info->s->base.rec_reflength;
  65. #if HA_FT_WTYPE == HA_KEYTYPE_FLOAT
  66.   float tmp_weight;
  67. #else
  68. #error
  69. #endif
  70.   DBUG_ENTER("walk_and_match");
  71.   word->weight=LWS_FOR_QUERY;
  72.   keylen=_ft_make_key(info,aio->keynr,(char*) keybuff,word,0);
  73.   keylen-=HA_FT_WLEN;
  74.   doc_cnt=0;
  75.   /* Skip rows inserted by current inserted */
  76.   for (r=_mi_search(info, keyinfo, keybuff, keylen, SEARCH_FIND, key_root) ;
  77.        !r &&
  78.          (subkeys=ft_sintXkorr(info->lastkey+info->lastkey_length-extra)) > 0 &&
  79.          info->lastpos >= info->state->data_file_length ;
  80.        r= _mi_search_next(info, keyinfo, info->lastkey,
  81.                           info->lastkey_length, SEARCH_BIGGER, key_root))
  82.     ;
  83.   info->update|= HA_STATE_AKTIV;              /* for _mi_test_if_changed() */
  84.   /* The following should be safe, even if we compare doubles */
  85.   while (!r && gweight)
  86.   {
  87.     if (keylen &&
  88.         mi_compare_text(aio->charset,info->lastkey+1,
  89.                         info->lastkey_length-extra-1, keybuff+1,keylen-1,0,0))
  90.      break;
  91.     if (subkeys<0)
  92.     {
  93.       if (doc_cnt)
  94.         DBUG_RETURN(1); /* index is corrupted */
  95.       /*
  96.         TODO here: unsafe optimization, should this word
  97.         be skipped (based on subkeys) ?
  98.       */
  99.       keybuff+=keylen;
  100.       keyinfo=& info->s->ft2_keyinfo;
  101.       key_root=info->lastpos;
  102.       keylen=0;
  103.       r=_mi_search_first(info, keyinfo, key_root);
  104.       goto do_skip;
  105.     }
  106. #if HA_FT_WTYPE == HA_KEYTYPE_FLOAT
  107.     tmp_weight=*(float*)&subkeys;
  108. #else
  109. #error
  110. #endif
  111.   /* The following should be safe, even if we compare doubles */
  112.     if (tmp_weight==0)
  113.       DBUG_RETURN(doc_cnt); /* stopword, doc_cnt should be 0 */
  114.     sdoc.doc.dpos=info->lastpos;
  115.     /* saving document matched into dtree */
  116.     if (!(selem=tree_insert(&aio->dtree, &sdoc, 0, aio->dtree.custom_arg)))
  117.       DBUG_RETURN(1);
  118.     sptr=(FT_SUPERDOC *)ELEMENT_KEY((&aio->dtree), selem);
  119.     if (selem->count==1) /* document's first match */
  120.       sptr->doc.weight=0;
  121.     else
  122.       sptr->doc.weight+=sptr->tmp_weight*sptr->word_ptr->weight;
  123.     sptr->word_ptr=word;
  124.     sptr->tmp_weight=tmp_weight;
  125.     doc_cnt++;
  126.     gweight=word->weight*GWS_IN_USE;
  127.     if (gweight < 0 || doc_cnt > 2000000)
  128.       gweight=0;
  129.     if (_mi_test_if_changed(info) == 0)
  130. r=_mi_search_next(info, keyinfo, info->lastkey, info->lastkey_length,
  131.                           SEARCH_BIGGER, key_root);
  132.     else
  133. r=_mi_search(info, keyinfo, info->lastkey, info->lastkey_length,
  134.                      SEARCH_BIGGER, key_root);
  135. do_skip:
  136.     while ((subkeys=ft_sintXkorr(info->lastkey+info->lastkey_length-extra)) > 0 &&
  137.            !r && info->lastpos >= info->state->data_file_length)
  138.       r= _mi_search_next(info, keyinfo, info->lastkey, info->lastkey_length,
  139.                          SEARCH_BIGGER, key_root);
  140.   }
  141.   word->weight=gweight;
  142.   DBUG_RETURN(0);
  143. }
  144. static int walk_and_copy(FT_SUPERDOC *from,
  145.  uint32 count __attribute__((unused)), FT_DOC **to)
  146. {
  147.   DBUG_ENTER("walk_and_copy");
  148.   from->doc.weight+=from->tmp_weight*from->word_ptr->weight;
  149.   (*to)->dpos=from->doc.dpos;
  150.   (*to)->weight=from->doc.weight;
  151.   (*to)++;
  152.   DBUG_RETURN(0);
  153. }
  154. static int walk_and_push(FT_SUPERDOC *from,
  155.  uint32 count __attribute__((unused)), QUEUE *best)
  156. {
  157.   DBUG_ENTER("walk_and_copy");
  158.   from->doc.weight+=from->tmp_weight*from->word_ptr->weight;
  159.   set_if_smaller(best->elements, ft_query_expansion_limit-1);
  160.   queue_insert(best, (byte *)& from->doc);
  161.   DBUG_RETURN(0);
  162. }
  163. static int FT_DOC_cmp(void *unused __attribute__((unused)),
  164.                       FT_DOC *a, FT_DOC *b)
  165. {
  166.   return sgn(b->weight - a->weight);
  167. }
  168. FT_INFO *ft_init_nlq_search(MI_INFO *info, uint keynr, byte *query,
  169.     uint query_len, uint flags, byte *record)
  170. {
  171.   TREE       wtree;
  172.   ALL_IN_ONE  aio;
  173.   FT_DOC     *dptr;
  174.   FT_INFO    *dlist=NULL;
  175.   my_off_t    saved_lastpos=info->lastpos;
  176.   DBUG_ENTER("ft_init_nlq_search");
  177. /* black magic ON */
  178.   if ((int) (keynr = _mi_check_index(info,keynr)) < 0)
  179.     DBUG_RETURN(NULL);
  180.   if (_mi_readinfo(info,F_RDLCK,1))
  181.     DBUG_RETURN(NULL);
  182. /* black magic OFF */
  183.   aio.info=info;
  184.   aio.keynr=keynr;
  185.   aio.charset=info->s->keyinfo[keynr].seg->charset;
  186.   aio.keybuff=info->lastkey+info->s->base.max_key_length;
  187.   bzero(&wtree,sizeof(wtree));
  188.   init_tree(&aio.dtree,0,0,sizeof(FT_SUPERDOC),(qsort_cmp2)&FT_SUPERDOC_cmp,0,
  189.             NULL, NULL);
  190.   ft_parse_init(&wtree, aio.charset);
  191.   if (ft_parse(&wtree,query,query_len,0))
  192.     goto err;
  193.   if (tree_walk(&wtree, (tree_walk_action)&walk_and_match, &aio,
  194. left_root_right))
  195.     goto err;
  196.   if (flags & FT_EXPAND && ft_query_expansion_limit)
  197.   {
  198.     QUEUE best;
  199.     init_queue(&best,ft_query_expansion_limit,0,0, (queue_compare) &FT_DOC_cmp,
  200.        0);
  201.     tree_walk(&aio.dtree, (tree_walk_action) &walk_and_push,
  202.               &best, left_root_right);
  203.     while (best.elements)
  204.     {
  205.       my_off_t docid=((FT_DOC *)queue_remove(& best, 0))->dpos;
  206.       if (!(*info->read_record)(info,docid,record))
  207.       {
  208.         info->update|= HA_STATE_AKTIV;
  209.         _mi_ft_parse(&wtree, info, keynr, record,1);
  210.       }
  211.     }
  212.     delete_queue(&best);
  213.     reset_tree(&aio.dtree);
  214.     if (tree_walk(&wtree, (tree_walk_action)&walk_and_match, &aio,
  215.                   left_root_right))
  216.       goto err;
  217.   }
  218.   /*
  219.     If ndocs == 0, this will not allocate RAM for FT_INFO.doc[],
  220.     so if ndocs == 0, FT_INFO.doc[] must not be accessed.
  221.    */
  222.   dlist=(FT_INFO *)my_malloc(sizeof(FT_INFO)+
  223.      sizeof(FT_DOC)*
  224.      (int)(aio.dtree.elements_in_tree-1),
  225.      MYF(0));
  226.   if (!dlist)
  227.     goto err;
  228.   dlist->please= (struct _ft_vft *) & _ft_vft_nlq;
  229.   dlist->ndocs=aio.dtree.elements_in_tree;
  230.   dlist->curdoc=-1;
  231.   dlist->info=aio.info;
  232.   dptr=dlist->doc;
  233.   tree_walk(&aio.dtree, (tree_walk_action) &walk_and_copy,
  234.     &dptr, left_root_right);
  235.   if (flags & FT_SORTED)
  236.     qsort2(dlist->doc, dlist->ndocs, sizeof(FT_DOC), (qsort2_cmp)&FT_DOC_cmp, 0);
  237. err:
  238.   delete_tree(&aio.dtree);
  239.   delete_tree(&wtree);
  240.   info->lastpos=saved_lastpos;
  241.   DBUG_RETURN(dlist);
  242. }
  243. int ft_nlq_read_next(FT_INFO *handler, char *record)
  244. {
  245.   MI_INFO *info= (MI_INFO *) handler->info;
  246.   if (++handler->curdoc >= handler->ndocs)
  247.   {
  248.     --handler->curdoc;
  249.     return HA_ERR_END_OF_FILE;
  250.   }
  251.   info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
  252.   info->lastpos=handler->doc[handler->curdoc].dpos;
  253.   if (!(*info->read_record)(info,info->lastpos,record))
  254.   {
  255.     info->update|= HA_STATE_AKTIV; /* Record is read */
  256.     return 0;
  257.   }
  258.   return my_errno;
  259. }
  260. float ft_nlq_find_relevance(FT_INFO *handler,
  261.     byte *record __attribute__((unused)),
  262.     uint length __attribute__((unused)))
  263. {
  264.   int a,b,c;
  265.   FT_DOC  *docs=handler->doc;
  266.   my_off_t docid=handler->info->lastpos;
  267.   if (docid == HA_POS_ERROR)
  268.     return -5.0;
  269.   /* Assuming docs[] is sorted by dpos... */
  270.   for (a=0, b=handler->ndocs, c=(a+b)/2; b-a>1; c=(a+b)/2)
  271.   {
  272.     if (docs[c].dpos > docid)
  273.       b=c;
  274.     else
  275.       a=c;
  276.   }
  277.   /* bounds check to avoid accessing unallocated handler->doc  */
  278.   if (a < handler->ndocs && docs[a].dpos == docid)
  279.     return (float) docs[a].weight;
  280.   else
  281.     return 0.0;
  282. }
  283. void ft_nlq_close_search(FT_INFO *handler)
  284. {
  285.   my_free((gptr)handler,MYF(0));
  286. }
  287. float ft_nlq_get_relevance(FT_INFO *handler)
  288. {
  289.   return (float) handler->doc[handler->curdoc].weight;
  290. }
  291. void ft_nlq_reinit_search(FT_INFO *handler)
  292. {
  293.   handler->curdoc=-1;
  294. }