LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tsrank.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 319 436 73.2 %
Date: 2024-11-21 08:14:44 Functions: 16 24 66.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * tsrank.c
       4             :  *      rank tsvector by tsquery
       5             :  *
       6             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/utils/adt/tsrank.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include <limits.h>
      17             : #include <math.h>
      18             : 
      19             : #include "miscadmin.h"
      20             : #include "tsearch/ts_utils.h"
      21             : #include "utils/array.h"
      22             : #include "utils/fmgrprotos.h"
      23             : 
      24             : #define NUM_WEIGHTS 4
      25             : static const float default_weights[NUM_WEIGHTS] = {0.1f, 0.2f, 0.4f, 1.0f};
      26             : 
      27             : #define wpos(wep)   ( w[ WEP_GETWEIGHT(wep) ] )
      28             : 
      29             : #define RANK_NO_NORM            0x00
      30             : #define RANK_NORM_LOGLENGTH     0x01
      31             : #define RANK_NORM_LENGTH        0x02
      32             : #define RANK_NORM_EXTDIST       0x04
      33             : #define RANK_NORM_UNIQ          0x08
      34             : #define RANK_NORM_LOGUNIQ       0x10
      35             : #define RANK_NORM_RDIVRPLUS1    0x20
      36             : #define DEF_NORM_METHOD         RANK_NO_NORM
      37             : 
      38             : static float calc_rank_or(const float *w, TSVector t, TSQuery q);
      39             : static float calc_rank_and(const float *w, TSVector t, TSQuery q);
      40             : 
      41             : /*
      42             :  * Returns a weight of a word collocation
      43             :  */
      44             : static float4
      45          18 : word_distance(int32 w)
      46             : {
      47          18 :     if (w > 100)
      48           0 :         return 1e-30f;
      49             : 
      50          18 :     return 1.0 / (1.005 + 0.05 * exp(((float4) w) / 1.5 - 2));
      51             : }
      52             : 
      53             : static int
      54           0 : cnt_length(TSVector t)
      55             : {
      56           0 :     WordEntry  *ptr = ARRPTR(t),
      57           0 :                *end = (WordEntry *) STRPTR(t);
      58           0 :     int         len = 0;
      59             : 
      60           0 :     while (ptr < end)
      61             :     {
      62           0 :         int         clen = POSDATALEN(t, ptr);
      63             : 
      64           0 :         if (clen == 0)
      65           0 :             len += 1;
      66             :         else
      67           0 :             len += clen;
      68             : 
      69           0 :         ptr++;
      70             :     }
      71             : 
      72           0 :     return len;
      73             : }
      74             : 
      75             : 
      76             : #define WordECompareQueryItem(e,q,p,i,m) \
      77             :     tsCompareString((q) + (i)->distance, (i)->length, \
      78             :                     (e) + (p)->pos, (p)->len, (m))
      79             : 
      80             : 
      81             : /*
      82             :  * Returns a pointer to a WordEntry's array corresponding to 'item' from
      83             :  * tsvector 't'. 'q' is the TSQuery containing 'item'.
      84             :  * Returns NULL if not found.
      85             :  */
      86             : static WordEntry *
      87         426 : find_wordentry(TSVector t, TSQuery q, QueryOperand *item, int32 *nitem)
      88             : {
      89         426 :     WordEntry  *StopLow = ARRPTR(t);
      90         426 :     WordEntry  *StopHigh = (WordEntry *) STRPTR(t);
      91         426 :     WordEntry  *StopMiddle = StopHigh;
      92             :     int         difference;
      93             : 
      94         426 :     *nitem = 0;
      95             : 
      96             :     /* Loop invariant: StopLow <= item < StopHigh */
      97        1146 :     while (StopLow < StopHigh)
      98             :     {
      99        1098 :         StopMiddle = StopLow + (StopHigh - StopLow) / 2;
     100        1098 :         difference = WordECompareQueryItem(STRPTR(t), GETOPERAND(q), StopMiddle, item, false);
     101        1098 :         if (difference == 0)
     102             :         {
     103         378 :             StopHigh = StopMiddle;
     104         378 :             *nitem = 1;
     105         378 :             break;
     106             :         }
     107         720 :         else if (difference > 0)
     108         252 :             StopLow = StopMiddle + 1;
     109             :         else
     110         468 :             StopHigh = StopMiddle;
     111             :     }
     112             : 
     113         426 :     if (item->prefix)
     114             :     {
     115          54 :         if (StopLow >= StopHigh)
     116          54 :             StopMiddle = StopHigh;
     117             : 
     118          54 :         *nitem = 0;
     119             : 
     120         222 :         while (StopMiddle < (WordEntry *) STRPTR(t) &&
     121          84 :                WordECompareQueryItem(STRPTR(t), GETOPERAND(q), StopMiddle, item, true) == 0)
     122             :         {
     123          84 :             (*nitem)++;
     124          84 :             StopMiddle++;
     125             :         }
     126             :     }
     127             : 
     128         426 :     return (*nitem > 0) ? StopHigh : NULL;
     129             : }
     130             : 
     131             : 
     132             : /*
     133             :  * sort QueryOperands by (length, word)
     134             :  */
     135             : static int
     136         108 : compareQueryOperand(const void *a, const void *b, void *arg)
     137             : {
     138         108 :     char       *operand = (char *) arg;
     139         108 :     QueryOperand *qa = (*(QueryOperand *const *) a);
     140         108 :     QueryOperand *qb = (*(QueryOperand *const *) b);
     141             : 
     142         216 :     return tsCompareString(operand + qa->distance, qa->length,
     143         108 :                            operand + qb->distance, qb->length,
     144             :                            false);
     145             : }
     146             : 
     147             : /*
     148             :  * Returns a sorted, de-duplicated array of QueryOperands in a query.
     149             :  * The returned QueryOperands are pointers to the original QueryOperands
     150             :  * in the query.
     151             :  *
     152             :  * Length of the returned array is stored in *size
     153             :  */
     154             : static QueryOperand **
     155          54 : SortAndUniqItems(TSQuery q, int *size)
     156             : {
     157          54 :     char       *operand = GETOPERAND(q);
     158          54 :     QueryItem  *item = GETQUERY(q);
     159             :     QueryOperand **res,
     160             :               **ptr,
     161             :               **prevptr;
     162             : 
     163          54 :     ptr = res = (QueryOperand **) palloc(sizeof(QueryOperand *) * *size);
     164             : 
     165             :     /* Collect all operands from the tree to res */
     166         216 :     while ((*size)--)
     167             :     {
     168         162 :         if (item->type == QI_VAL)
     169             :         {
     170         108 :             *ptr = (QueryOperand *) item;
     171         108 :             ptr++;
     172             :         }
     173         162 :         item++;
     174             :     }
     175             : 
     176          54 :     *size = ptr - res;
     177          54 :     if (*size < 2)
     178           0 :         return res;
     179             : 
     180          54 :     qsort_arg(res, *size, sizeof(QueryOperand *), compareQueryOperand, operand);
     181             : 
     182          54 :     ptr = res + 1;
     183          54 :     prevptr = res;
     184             : 
     185             :     /* remove duplicates */
     186         108 :     while (ptr - res < *size)
     187             :     {
     188          54 :         if (compareQueryOperand((void *) ptr, (void *) prevptr, (void *) operand) != 0)
     189             :         {
     190          54 :             prevptr++;
     191          54 :             *prevptr = *ptr;
     192             :         }
     193          54 :         ptr++;
     194             :     }
     195             : 
     196          54 :     *size = prevptr + 1 - res;
     197          54 :     return res;
     198             : }
     199             : 
     200             : static float
     201          18 : calc_rank_and(const float *w, TSVector t, TSQuery q)
     202             : {
     203             :     WordEntryPosVector **pos;
     204             :     WordEntryPosVector1 posnull;
     205             :     WordEntryPosVector *POSNULL;
     206             :     int         i,
     207             :                 k,
     208             :                 l,
     209             :                 p;
     210             :     WordEntry  *entry,
     211             :                *firstentry;
     212             :     WordEntryPos *post,
     213             :                *ct;
     214             :     int32       dimt,
     215             :                 lenct,
     216             :                 dist,
     217             :                 nitem;
     218          18 :     float       res = -1.0;
     219             :     QueryOperand **item;
     220          18 :     int         size = q->size;
     221             : 
     222          18 :     item = SortAndUniqItems(q, &size);
     223          18 :     if (size < 2)
     224             :     {
     225           0 :         pfree(item);
     226           0 :         return calc_rank_or(w, t, q);
     227             :     }
     228          18 :     pos = (WordEntryPosVector **) palloc0(sizeof(WordEntryPosVector *) * q->size);
     229             : 
     230             :     /* A dummy WordEntryPos array to use when haspos is false */
     231          18 :     posnull.npos = 1;
     232          18 :     posnull.pos[0] = 0;
     233          18 :     WEP_SETPOS(posnull.pos[0], MAXENTRYPOS - 1);
     234          18 :     POSNULL = (WordEntryPosVector *) &posnull;
     235             : 
     236          54 :     for (i = 0; i < size; i++)
     237             :     {
     238          36 :         firstentry = entry = find_wordentry(t, q, item[i], &nitem);
     239          36 :         if (!entry)
     240           0 :             continue;
     241             : 
     242          72 :         while (entry - firstentry < nitem)
     243             :         {
     244          36 :             if (entry->haspos)
     245          36 :                 pos[i] = _POSVECPTR(t, entry);
     246             :             else
     247           0 :                 pos[i] = POSNULL;
     248             : 
     249          36 :             dimt = pos[i]->npos;
     250          36 :             post = pos[i]->pos;
     251          54 :             for (k = 0; k < i; k++)
     252             :             {
     253          18 :                 if (!pos[k])
     254           0 :                     continue;
     255          18 :                 lenct = pos[k]->npos;
     256          18 :                 ct = pos[k]->pos;
     257          36 :                 for (l = 0; l < dimt; l++)
     258             :                 {
     259          36 :                     for (p = 0; p < lenct; p++)
     260             :                     {
     261          18 :                         dist = abs((int) WEP_GETPOS(post[l]) - (int) WEP_GETPOS(ct[p]));
     262          18 :                         if (dist || (dist == 0 && (pos[i] == POSNULL || pos[k] == POSNULL)))
     263             :                         {
     264             :                             float       curw;
     265             : 
     266          18 :                             if (!dist)
     267           0 :                                 dist = MAXENTRYPOS;
     268          18 :                             curw = sqrt(wpos(post[l]) * wpos(ct[p]) * word_distance(dist));
     269          18 :                             res = (res < 0) ? curw : 1.0 - (1.0 - res) * (1.0 - curw);
     270             :                         }
     271             :                     }
     272             :                 }
     273             :             }
     274             : 
     275          36 :             entry++;
     276             :         }
     277             :     }
     278          18 :     pfree(pos);
     279          18 :     pfree(item);
     280          18 :     return res;
     281             : }
     282             : 
     283             : static float
     284          36 : calc_rank_or(const float *w, TSVector t, TSQuery q)
     285             : {
     286             :     WordEntry  *entry,
     287             :                *firstentry;
     288             :     WordEntryPosVector1 posnull;
     289             :     WordEntryPos *post;
     290             :     int32       dimt,
     291             :                 j,
     292             :                 i,
     293             :                 nitem;
     294          36 :     float       res = 0.0;
     295             :     QueryOperand **item;
     296          36 :     int         size = q->size;
     297             : 
     298             :     /* A dummy WordEntryPos array to use when haspos is false */
     299          36 :     posnull.npos = 1;
     300          36 :     posnull.pos[0] = 0;
     301             : 
     302          36 :     item = SortAndUniqItems(q, &size);
     303             : 
     304         108 :     for (i = 0; i < size; i++)
     305             :     {
     306             :         float       resj,
     307             :                     wjm;
     308             :         int32       jm;
     309             : 
     310          72 :         firstentry = entry = find_wordentry(t, q, item[i], &nitem);
     311          72 :         if (!entry)
     312           6 :             continue;
     313             : 
     314         132 :         while (entry - firstentry < nitem)
     315             :         {
     316          66 :             if (entry->haspos)
     317             :             {
     318          66 :                 dimt = POSDATALEN(t, entry);
     319          66 :                 post = POSDATAPTR(t, entry);
     320             :             }
     321             :             else
     322             :             {
     323           0 :                 dimt = posnull.npos;
     324           0 :                 post = posnull.pos;
     325             :             }
     326             : 
     327          66 :             resj = 0.0;
     328          66 :             wjm = -1.0;
     329          66 :             jm = 0;
     330         132 :             for (j = 0; j < dimt; j++)
     331             :             {
     332          66 :                 resj = resj + wpos(post[j]) / ((j + 1) * (j + 1));
     333          66 :                 if (wpos(post[j]) > wjm)
     334             :                 {
     335          66 :                     wjm = wpos(post[j]);
     336          66 :                     jm = j;
     337             :                 }
     338             :             }
     339             : /*
     340             :             limit (sum(1/i^2),i=1,inf) = pi^2/6
     341             :             resj = sum(wi/i^2),i=1,noccurrence,
     342             :             wi - should be sorted desc,
     343             :             don't sort for now, just choose maximum weight. This should be corrected
     344             :             Oleg Bartunov
     345             : */
     346          66 :             res = res + (wjm + resj - wjm / ((jm + 1) * (jm + 1))) / 1.64493406685;
     347             : 
     348          66 :             entry++;
     349             :         }
     350             :     }
     351          36 :     if (size > 0)
     352          36 :         res = res / size;
     353          36 :     pfree(item);
     354          36 :     return res;
     355             : }
     356             : 
     357             : static float
     358          54 : calc_rank(const float *w, TSVector t, TSQuery q, int32 method)
     359             : {
     360          54 :     QueryItem  *item = GETQUERY(q);
     361          54 :     float       res = 0.0;
     362             :     int         len;
     363             : 
     364          54 :     if (!t->size || !q->size)
     365           0 :         return 0.0;
     366             : 
     367             :     /* XXX: What about NOT? */
     368          54 :     res = (item->type == QI_OPR && (item->qoperator.oper == OP_AND ||
     369          36 :                                     item->qoperator.oper == OP_PHRASE)) ?
     370          72 :         calc_rank_and(w, t, q) :
     371          36 :         calc_rank_or(w, t, q);
     372             : 
     373          54 :     if (res < 0)
     374           0 :         res = 1e-20f;
     375             : 
     376          54 :     if ((method & RANK_NORM_LOGLENGTH) && t->size > 0)
     377           0 :         res /= log((double) (cnt_length(t) + 1)) / log(2.0);
     378             : 
     379          54 :     if (method & RANK_NORM_LENGTH)
     380             :     {
     381           0 :         len = cnt_length(t);
     382           0 :         if (len > 0)
     383           0 :             res /= (float) len;
     384             :     }
     385             : 
     386             :     /* RANK_NORM_EXTDIST not applicable */
     387             : 
     388          54 :     if ((method & RANK_NORM_UNIQ) && t->size > 0)
     389           0 :         res /= (float) (t->size);
     390             : 
     391          54 :     if ((method & RANK_NORM_LOGUNIQ) && t->size > 0)
     392           0 :         res /= log((double) (t->size + 1)) / log(2.0);
     393             : 
     394          54 :     if (method & RANK_NORM_RDIVRPLUS1)
     395           0 :         res /= (res + 1);
     396             : 
     397          54 :     return res;
     398             : }
     399             : 
     400             : /*
     401             :  * Extract weights from an array. The weights are stored in *ws, which must
     402             :  * have space for NUM_WEIGHTS elements.
     403             :  */
     404             : static void
     405           0 : getWeights(ArrayType *win, float *ws)
     406             : {
     407             :     int         i;
     408             :     float4     *arrdata;
     409             : 
     410             :     Assert(win != NULL);
     411             : 
     412           0 :     if (ARR_NDIM(win) != 1)
     413           0 :         ereport(ERROR,
     414             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     415             :                  errmsg("array of weight must be one-dimensional")));
     416             : 
     417           0 :     if (ArrayGetNItems(ARR_NDIM(win), ARR_DIMS(win)) < NUM_WEIGHTS)
     418           0 :         ereport(ERROR,
     419             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     420             :                  errmsg("array of weight is too short")));
     421             : 
     422           0 :     if (array_contains_nulls(win))
     423           0 :         ereport(ERROR,
     424             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     425             :                  errmsg("array of weight must not contain nulls")));
     426             : 
     427           0 :     arrdata = (float4 *) ARR_DATA_PTR(win);
     428           0 :     for (i = 0; i < NUM_WEIGHTS; i++)
     429             :     {
     430           0 :         ws[i] = (arrdata[i] >= 0) ? arrdata[i] : default_weights[i];
     431           0 :         if (ws[i] > 1.0)
     432           0 :             ereport(ERROR,
     433             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     434             :                      errmsg("weight out of range")));
     435             :     }
     436           0 : }
     437             : 
     438             : Datum
     439           0 : ts_rank_wttf(PG_FUNCTION_ARGS)
     440             : {
     441           0 :     ArrayType  *win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
     442           0 :     TSVector    txt = PG_GETARG_TSVECTOR(1);
     443           0 :     TSQuery     query = PG_GETARG_TSQUERY(2);
     444           0 :     int         method = PG_GETARG_INT32(3);
     445             :     float       weights[NUM_WEIGHTS];
     446             :     float       res;
     447             : 
     448           0 :     getWeights(win, weights);
     449           0 :     res = calc_rank(weights, txt, query, method);
     450             : 
     451           0 :     PG_FREE_IF_COPY(win, 0);
     452           0 :     PG_FREE_IF_COPY(txt, 1);
     453           0 :     PG_FREE_IF_COPY(query, 2);
     454           0 :     PG_RETURN_FLOAT4(res);
     455             : }
     456             : 
     457             : Datum
     458           0 : ts_rank_wtt(PG_FUNCTION_ARGS)
     459             : {
     460           0 :     ArrayType  *win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
     461           0 :     TSVector    txt = PG_GETARG_TSVECTOR(1);
     462           0 :     TSQuery     query = PG_GETARG_TSQUERY(2);
     463             :     float       weights[NUM_WEIGHTS];
     464             :     float       res;
     465             : 
     466           0 :     getWeights(win, weights);
     467           0 :     res = calc_rank(weights, txt, query, DEF_NORM_METHOD);
     468             : 
     469           0 :     PG_FREE_IF_COPY(win, 0);
     470           0 :     PG_FREE_IF_COPY(txt, 1);
     471           0 :     PG_FREE_IF_COPY(query, 2);
     472           0 :     PG_RETURN_FLOAT4(res);
     473             : }
     474             : 
     475             : Datum
     476           0 : ts_rank_ttf(PG_FUNCTION_ARGS)
     477             : {
     478           0 :     TSVector    txt = PG_GETARG_TSVECTOR(0);
     479           0 :     TSQuery     query = PG_GETARG_TSQUERY(1);
     480           0 :     int         method = PG_GETARG_INT32(2);
     481             :     float       res;
     482             : 
     483           0 :     res = calc_rank(default_weights, txt, query, method);
     484             : 
     485           0 :     PG_FREE_IF_COPY(txt, 0);
     486           0 :     PG_FREE_IF_COPY(query, 1);
     487           0 :     PG_RETURN_FLOAT4(res);
     488             : }
     489             : 
     490             : Datum
     491          54 : ts_rank_tt(PG_FUNCTION_ARGS)
     492             : {
     493          54 :     TSVector    txt = PG_GETARG_TSVECTOR(0);
     494          54 :     TSQuery     query = PG_GETARG_TSQUERY(1);
     495             :     float       res;
     496             : 
     497          54 :     res = calc_rank(default_weights, txt, query, DEF_NORM_METHOD);
     498             : 
     499          54 :     PG_FREE_IF_COPY(txt, 0);
     500          54 :     PG_FREE_IF_COPY(query, 1);
     501          54 :     PG_RETURN_FLOAT4(res);
     502             : }
     503             : 
     504             : typedef struct
     505             : {
     506             :     union
     507             :     {
     508             :         struct
     509             :         {                       /* compiled doc representation */
     510             :             QueryItem **items;
     511             :             int16       nitem;
     512             :         }           query;
     513             :         struct
     514             :         {                       /* struct is used for preparing doc
     515             :                                  * representation */
     516             :             QueryItem  *item;
     517             :             WordEntry  *entry;
     518             :         }           map;
     519             :     }           data;
     520             :     WordEntryPos pos;
     521             : } DocRepresentation;
     522             : 
     523             : static int
     524         348 : compareDocR(const void *va, const void *vb)
     525             : {
     526         348 :     const DocRepresentation *a = (const DocRepresentation *) va;
     527         348 :     const DocRepresentation *b = (const DocRepresentation *) vb;
     528             : 
     529         348 :     if (WEP_GETPOS(a->pos) == WEP_GETPOS(b->pos))
     530             :     {
     531          36 :         if (WEP_GETWEIGHT(a->pos) == WEP_GETWEIGHT(b->pos))
     532             :         {
     533           6 :             if (a->data.map.entry == b->data.map.entry)
     534           6 :                 return 0;
     535             : 
     536           0 :             return (a->data.map.entry > b->data.map.entry) ? 1 : -1;
     537             :         }
     538             : 
     539          30 :         return (WEP_GETWEIGHT(a->pos) > WEP_GETWEIGHT(b->pos)) ? 1 : -1;
     540             :     }
     541             : 
     542         312 :     return (WEP_GETPOS(a->pos) > WEP_GETPOS(b->pos)) ? 1 : -1;
     543             : }
     544             : 
     545             : #define MAXQROPOS   MAXENTRYPOS
     546             : typedef struct
     547             : {
     548             :     bool        operandexists;
     549             :     bool        reverseinsert;  /* indicates insert order, true means
     550             :                                  * descending order */
     551             :     uint32      npos;
     552             :     WordEntryPos pos[MAXQROPOS];
     553             : } QueryRepresentationOperand;
     554             : 
     555             : typedef struct
     556             : {
     557             :     TSQuery     query;
     558             :     QueryRepresentationOperand *operandData;
     559             : } QueryRepresentation;
     560             : 
     561             : #define QR_GET_OPERAND_DATA(q, v) \
     562             :     ( (q)->operandData + (((QueryItem*)(v)) - GETQUERY((q)->query)) )
     563             : 
     564             : /*
     565             :  * TS_execute callback for matching a tsquery operand to QueryRepresentation
     566             :  */
     567             : static TSTernaryValue
     568        1158 : checkcondition_QueryOperand(void *checkval, QueryOperand *val,
     569             :                             ExecPhraseData *data)
     570             : {
     571        1158 :     QueryRepresentation *qr = (QueryRepresentation *) checkval;
     572        1158 :     QueryRepresentationOperand *opData = QR_GET_OPERAND_DATA(qr, val);
     573             : 
     574        1158 :     if (!opData->operandexists)
     575         444 :         return TS_NO;
     576             : 
     577         714 :     if (data)
     578             :     {
     579         348 :         data->npos = opData->npos;
     580         348 :         data->pos = opData->pos;
     581         348 :         if (opData->reverseinsert)
     582         108 :             data->pos += MAXQROPOS - opData->npos;
     583             :     }
     584             : 
     585         714 :     return TS_YES;
     586             : }
     587             : 
     588             : typedef struct
     589             : {
     590             :     int         pos;
     591             :     int         p;
     592             :     int         q;
     593             :     DocRepresentation *begin;
     594             :     DocRepresentation *end;
     595             : } CoverExt;
     596             : 
     597             : static void
     598         498 : resetQueryRepresentation(QueryRepresentation *qr, bool reverseinsert)
     599             : {
     600             :     int         i;
     601             : 
     602        2016 :     for (i = 0; i < qr->query->size; i++)
     603             :     {
     604        1518 :         qr->operandData[i].operandexists = false;
     605        1518 :         qr->operandData[i].reverseinsert = reverseinsert;
     606        1518 :         qr->operandData[i].npos = 0;
     607             :     }
     608         498 : }
     609             : 
     610             : static void
     611         720 : fillQueryRepresentationData(QueryRepresentation *qr, DocRepresentation *entry)
     612             : {
     613             :     int         i;
     614             :     int         lastPos;
     615             :     QueryRepresentationOperand *opData;
     616             : 
     617        1446 :     for (i = 0; i < entry->data.query.nitem; i++)
     618             :     {
     619         726 :         if (entry->data.query.items[i]->type != QI_VAL)
     620           0 :             continue;
     621             : 
     622         726 :         opData = QR_GET_OPERAND_DATA(qr, entry->data.query.items[i]);
     623             : 
     624         726 :         opData->operandexists = true;
     625             : 
     626         726 :         if (opData->npos == 0)
     627             :         {
     628         660 :             lastPos = (opData->reverseinsert) ? (MAXQROPOS - 1) : 0;
     629         660 :             opData->pos[lastPos] = entry->pos;
     630         660 :             opData->npos++;
     631         660 :             continue;
     632             :         }
     633             : 
     634         132 :         lastPos = opData->reverseinsert ?
     635          66 :             (MAXQROPOS - opData->npos) :
     636          66 :             (opData->npos - 1);
     637             : 
     638          66 :         if (WEP_GETPOS(opData->pos[lastPos]) != WEP_GETPOS(entry->pos))
     639             :         {
     640          84 :             lastPos = opData->reverseinsert ?
     641          42 :                 (MAXQROPOS - 1 - opData->npos) :
     642          42 :                 (opData->npos);
     643             : 
     644          42 :             opData->pos[lastPos] = entry->pos;
     645          42 :             opData->npos++;
     646             :         }
     647             :     }
     648         720 : }
     649             : 
     650             : static bool
     651         324 : Cover(DocRepresentation *doc, int len, QueryRepresentation *qr, CoverExt *ext)
     652             : {
     653             :     DocRepresentation *ptr;
     654         324 :     int         lastpos = ext->pos;
     655         324 :     bool        found = false;
     656             : 
     657             :     /*
     658             :      * since this function recurses, it could be driven to stack overflow.
     659             :      * (though any decent compiler will optimize away the tail-recursion.
     660             :      */
     661         324 :     check_stack_depth();
     662             : 
     663         324 :     resetQueryRepresentation(qr, false);
     664             : 
     665         324 :     ext->p = INT_MAX;
     666         324 :     ext->q = 0;
     667         324 :     ptr = doc + ext->pos;
     668             : 
     669             :     /* find upper bound of cover from current position, move up */
     670         606 :     while (ptr - doc < len)
     671             :     {
     672         456 :         fillQueryRepresentationData(qr, ptr);
     673             : 
     674         456 :         if (TS_execute(GETQUERY(qr->query), (void *) qr,
     675             :                        TS_EXEC_EMPTY, checkcondition_QueryOperand))
     676             :         {
     677         174 :             if (WEP_GETPOS(ptr->pos) > ext->q)
     678             :             {
     679         174 :                 ext->q = WEP_GETPOS(ptr->pos);
     680         174 :                 ext->end = ptr;
     681         174 :                 lastpos = ptr - doc;
     682         174 :                 found = true;
     683             :             }
     684         174 :             break;
     685             :         }
     686         282 :         ptr++;
     687             :     }
     688             : 
     689         324 :     if (!found)
     690         150 :         return false;
     691             : 
     692         174 :     resetQueryRepresentation(qr, true);
     693             : 
     694         174 :     ptr = doc + lastpos;
     695             : 
     696             :     /* find lower bound of cover from found upper bound, move down */
     697         264 :     while (ptr >= doc + ext->pos)
     698             :     {
     699             :         /*
     700             :          * we scan doc from right to left, so pos info in reverse order!
     701             :          */
     702         264 :         fillQueryRepresentationData(qr, ptr);
     703             : 
     704         264 :         if (TS_execute(GETQUERY(qr->query), (void *) qr,
     705             :                        TS_EXEC_EMPTY, checkcondition_QueryOperand))
     706             :         {
     707         174 :             if (WEP_GETPOS(ptr->pos) < ext->p)
     708             :             {
     709         174 :                 ext->begin = ptr;
     710         174 :                 ext->p = WEP_GETPOS(ptr->pos);
     711             :             }
     712         174 :             break;
     713             :         }
     714          90 :         ptr--;
     715             :     }
     716             : 
     717         174 :     if (ext->p <= ext->q)
     718             :     {
     719             :         /*
     720             :          * set position for next try to next lexeme after beginning of found
     721             :          * cover
     722             :          */
     723         174 :         ext->pos = (ptr - doc) + 1;
     724         174 :         return true;
     725             :     }
     726             : 
     727           0 :     ext->pos++;
     728           0 :     return Cover(doc, len, qr, ext);
     729             : }
     730             : 
     731             : static DocRepresentation *
     732         156 : get_docrep(TSVector txt, QueryRepresentation *qr, int *doclen)
     733             : {
     734         156 :     QueryItem  *item = GETQUERY(qr->query);
     735             :     WordEntry  *entry,
     736             :                *firstentry;
     737             :     WordEntryPos *post;
     738             :     int32       dimt,           /* number of 'post' items */
     739             :                 j,
     740             :                 i,
     741             :                 nitem;
     742         156 :     int         len = qr->query->size * 4,
     743         156 :                 cur = 0;
     744             :     DocRepresentation *doc;
     745             : 
     746         156 :     doc = (DocRepresentation *) palloc(sizeof(DocRepresentation) * len);
     747             : 
     748             :     /*
     749             :      * Iterate through query to make DocRepresentation for words and it's
     750             :      * entries satisfied by query
     751             :      */
     752         636 :     for (i = 0; i < qr->query->size; i++)
     753             :     {
     754             :         QueryOperand *curoperand;
     755             : 
     756         480 :         if (item[i].type != QI_VAL)
     757         162 :             continue;
     758             : 
     759         318 :         curoperand = &item[i].qoperand;
     760             : 
     761         318 :         firstentry = entry = find_wordentry(txt, qr->query, curoperand, &nitem);
     762         318 :         if (!entry)
     763           6 :             continue;
     764             : 
     765             :         /* iterations over entries in tsvector */
     766         654 :         while (entry - firstentry < nitem)
     767             :         {
     768         342 :             if (entry->haspos)
     769             :             {
     770         330 :                 dimt = POSDATALEN(txt, entry);
     771         330 :                 post = POSDATAPTR(txt, entry);
     772             :             }
     773             :             else
     774             :             {
     775             :                 /* ignore words without positions */
     776          12 :                 entry++;
     777          12 :                 continue;
     778             :             }
     779             : 
     780         330 :             while (cur + dimt >= len)
     781             :             {
     782           0 :                 len *= 2;
     783           0 :                 doc = (DocRepresentation *) repalloc(doc, sizeof(DocRepresentation) * len);
     784             :             }
     785             : 
     786             :             /* iterations over entry's positions */
     787         714 :             for (j = 0; j < dimt; j++)
     788             :             {
     789         384 :                 if (curoperand->weight == 0 ||
     790          30 :                     curoperand->weight & (1 << WEP_GETWEIGHT(post[j])))
     791             :                 {
     792         372 :                     doc[cur].pos = post[j];
     793         372 :                     doc[cur].data.map.entry = entry;
     794         372 :                     doc[cur].data.map.item = (QueryItem *) curoperand;
     795         372 :                     cur++;
     796             :                 }
     797             :             }
     798             : 
     799         330 :             entry++;
     800             :         }
     801             :     }
     802             : 
     803         156 :     if (cur > 0)
     804             :     {
     805         150 :         DocRepresentation *rptr = doc + 1,
     806         150 :                    *wptr = doc,
     807             :                     storage;
     808             : 
     809             :         /*
     810             :          * Sort representation in ascending order by pos and entry
     811             :          */
     812         150 :         qsort(doc, cur, sizeof(DocRepresentation), compareDocR);
     813             : 
     814             :         /*
     815             :          * Join QueryItem per WordEntry and it's position
     816             :          */
     817         150 :         storage.pos = doc->pos;
     818         150 :         storage.data.query.items = palloc(sizeof(QueryItem *) * qr->query->size);
     819         150 :         storage.data.query.items[0] = doc->data.map.item;
     820         150 :         storage.data.query.nitem = 1;
     821             : 
     822         372 :         while (rptr - doc < cur)
     823             :         {
     824         222 :             if (rptr->pos == (rptr - 1)->pos &&
     825           6 :                 rptr->data.map.entry == (rptr - 1)->data.map.entry)
     826             :             {
     827           6 :                 storage.data.query.items[storage.data.query.nitem] = rptr->data.map.item;
     828           6 :                 storage.data.query.nitem++;
     829             :             }
     830             :             else
     831             :             {
     832         216 :                 *wptr = storage;
     833         216 :                 wptr++;
     834         216 :                 storage.pos = rptr->pos;
     835         216 :                 storage.data.query.items = palloc(sizeof(QueryItem *) * qr->query->size);
     836         216 :                 storage.data.query.items[0] = rptr->data.map.item;
     837         216 :                 storage.data.query.nitem = 1;
     838             :             }
     839             : 
     840         222 :             rptr++;
     841             :         }
     842             : 
     843         150 :         *wptr = storage;
     844         150 :         wptr++;
     845             : 
     846         150 :         *doclen = wptr - doc;
     847         150 :         return doc;
     848             :     }
     849             : 
     850           6 :     pfree(doc);
     851           6 :     return NULL;
     852             : }
     853             : 
     854             : static float4
     855         156 : calc_rank_cd(const float4 *arrdata, TSVector txt, TSQuery query, int method)
     856             : {
     857             :     DocRepresentation *doc;
     858             :     int         len,
     859             :                 i,
     860         156 :                 doclen = 0;
     861             :     CoverExt    ext;
     862         156 :     double      Wdoc = 0.0;
     863             :     double      invws[NUM_WEIGHTS];
     864         156 :     double      SumDist = 0.0,
     865         156 :                 PrevExtPos = 0.0;
     866         156 :     int         NExtent = 0;
     867             :     QueryRepresentation qr;
     868             : 
     869             : 
     870         780 :     for (i = 0; i < NUM_WEIGHTS; i++)
     871             :     {
     872         624 :         invws[i] = ((double) ((arrdata[i] >= 0) ? arrdata[i] : default_weights[i]));
     873         624 :         if (invws[i] > 1.0)
     874           0 :             ereport(ERROR,
     875             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     876             :                      errmsg("weight out of range")));
     877         624 :         invws[i] = 1.0 / invws[i];
     878             :     }
     879             : 
     880         156 :     qr.query = query;
     881         156 :     qr.operandData = (QueryRepresentationOperand *)
     882         156 :         palloc0(sizeof(QueryRepresentationOperand) * query->size);
     883             : 
     884         156 :     doc = get_docrep(txt, &qr, &doclen);
     885         156 :     if (!doc)
     886             :     {
     887           6 :         pfree(qr.operandData);
     888           6 :         return 0.0;
     889             :     }
     890             : 
     891         750 :     MemSet(&ext, 0, sizeof(CoverExt));
     892         324 :     while (Cover(doc, doclen, &qr, &ext))
     893             :     {
     894         174 :         double      Cpos = 0.0;
     895         174 :         double      InvSum = 0.0;
     896             :         double      CurExtPos;
     897             :         int         nNoise;
     898         174 :         DocRepresentation *ptr = ext.begin;
     899             : 
     900         438 :         while (ptr <= ext.end)
     901             :         {
     902         264 :             InvSum += invws[WEP_GETWEIGHT(ptr->pos)];
     903         264 :             ptr++;
     904             :         }
     905             : 
     906         174 :         Cpos = ((double) (ext.end - ext.begin + 1)) / InvSum;
     907             : 
     908             :         /*
     909             :          * if doc are big enough then ext.q may be equal to ext.p due to limit
     910             :          * of positional information. In this case we approximate number of
     911             :          * noise word as half cover's length
     912             :          */
     913         174 :         nNoise = (ext.q - ext.p) - (ext.end - ext.begin);
     914         174 :         if (nNoise < 0)
     915           0 :             nNoise = (ext.end - ext.begin) / 2;
     916         174 :         Wdoc += Cpos / ((double) (1 + nNoise));
     917             : 
     918         174 :         CurExtPos = ((double) (ext.q + ext.p)) / 2.0;
     919         174 :         if (NExtent > 0 && CurExtPos > PrevExtPos /* prevent division by
     920             :                                                      * zero in a case of
     921             :               * multiple lexize */ )
     922          42 :             SumDist += 1.0 / (CurExtPos - PrevExtPos);
     923             : 
     924         174 :         PrevExtPos = CurExtPos;
     925         174 :         NExtent++;
     926             :     }
     927             : 
     928         150 :     if ((method & RANK_NORM_LOGLENGTH) && txt->size > 0)
     929           0 :         Wdoc /= log((double) (cnt_length(txt) + 1));
     930             : 
     931         150 :     if (method & RANK_NORM_LENGTH)
     932             :     {
     933           0 :         len = cnt_length(txt);
     934           0 :         if (len > 0)
     935           0 :             Wdoc /= (double) len;
     936             :     }
     937             : 
     938         150 :     if ((method & RANK_NORM_EXTDIST) && NExtent > 0 && SumDist > 0)
     939           0 :         Wdoc /= ((double) NExtent) / SumDist;
     940             : 
     941         150 :     if ((method & RANK_NORM_UNIQ) && txt->size > 0)
     942           0 :         Wdoc /= (double) (txt->size);
     943             : 
     944         150 :     if ((method & RANK_NORM_LOGUNIQ) && txt->size > 0)
     945           0 :         Wdoc /= log((double) (txt->size + 1)) / log(2.0);
     946             : 
     947         150 :     if (method & RANK_NORM_RDIVRPLUS1)
     948           0 :         Wdoc /= (Wdoc + 1);
     949             : 
     950         150 :     pfree(doc);
     951             : 
     952         150 :     pfree(qr.operandData);
     953             : 
     954         150 :     return (float4) Wdoc;
     955             : }
     956             : 
     957             : Datum
     958           0 : ts_rankcd_wttf(PG_FUNCTION_ARGS)
     959             : {
     960           0 :     ArrayType  *win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
     961           0 :     TSVector    txt = PG_GETARG_TSVECTOR(1);
     962           0 :     TSQuery     query = PG_GETARG_TSQUERY(2);
     963           0 :     int         method = PG_GETARG_INT32(3);
     964             :     float       weights[NUM_WEIGHTS];
     965             :     float       res;
     966             : 
     967           0 :     getWeights(win, weights);
     968           0 :     res = calc_rank_cd(weights, txt, query, method);
     969             : 
     970           0 :     PG_FREE_IF_COPY(win, 0);
     971           0 :     PG_FREE_IF_COPY(txt, 1);
     972           0 :     PG_FREE_IF_COPY(query, 2);
     973           0 :     PG_RETURN_FLOAT4(res);
     974             : }
     975             : 
     976             : Datum
     977           0 : ts_rankcd_wtt(PG_FUNCTION_ARGS)
     978             : {
     979           0 :     ArrayType  *win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
     980           0 :     TSVector    txt = PG_GETARG_TSVECTOR(1);
     981           0 :     TSQuery     query = PG_GETARG_TSQUERY(2);
     982             :     float       weights[NUM_WEIGHTS];
     983             :     float       res;
     984             : 
     985           0 :     getWeights(win, weights);
     986           0 :     res = calc_rank_cd(weights, txt, query, DEF_NORM_METHOD);
     987             : 
     988           0 :     PG_FREE_IF_COPY(win, 0);
     989           0 :     PG_FREE_IF_COPY(txt, 1);
     990           0 :     PG_FREE_IF_COPY(query, 2);
     991           0 :     PG_RETURN_FLOAT4(res);
     992             : }
     993             : 
     994             : Datum
     995           0 : ts_rankcd_ttf(PG_FUNCTION_ARGS)
     996             : {
     997           0 :     TSVector    txt = PG_GETARG_TSVECTOR(0);
     998           0 :     TSQuery     query = PG_GETARG_TSQUERY(1);
     999           0 :     int         method = PG_GETARG_INT32(2);
    1000             :     float       res;
    1001             : 
    1002           0 :     res = calc_rank_cd(default_weights, txt, query, method);
    1003             : 
    1004           0 :     PG_FREE_IF_COPY(txt, 0);
    1005           0 :     PG_FREE_IF_COPY(query, 1);
    1006           0 :     PG_RETURN_FLOAT4(res);
    1007             : }
    1008             : 
    1009             : Datum
    1010         156 : ts_rankcd_tt(PG_FUNCTION_ARGS)
    1011             : {
    1012         156 :     TSVector    txt = PG_GETARG_TSVECTOR(0);
    1013         156 :     TSQuery     query = PG_GETARG_TSQUERY(1);
    1014             :     float       res;
    1015             : 
    1016         156 :     res = calc_rank_cd(default_weights, txt, query, DEF_NORM_METHOD);
    1017             : 
    1018         156 :     PG_FREE_IF_COPY(txt, 0);
    1019         156 :     PG_FREE_IF_COPY(query, 1);
    1020         156 :     PG_RETURN_FLOAT4(res);
    1021             : }

Generated by: LCOV version 1.14