LCOV - code coverage report
Current view: top level - src/backend/utils/adt - tsrank.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 73.1 % 435 318
Test Date: 2026-03-01 15:14:58 Functions: 66.7 % 24 16
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-2026, 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            9 : word_distance(int32 w)
      46              : {
      47            9 :     if (w > 100)
      48            0 :         return 1e-30f;
      49              : 
      50            9 :     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          213 : find_wordentry(TSVector t, TSQuery q, QueryOperand *item, int32 *nitem)
      88              : {
      89          213 :     WordEntry  *StopLow = ARRPTR(t);
      90          213 :     WordEntry  *StopHigh = (WordEntry *) STRPTR(t);
      91          213 :     WordEntry  *StopMiddle = StopHigh;
      92              :     int         difference;
      93              : 
      94          213 :     *nitem = 0;
      95              : 
      96              :     /* Loop invariant: StopLow <= item < StopHigh */
      97          573 :     while (StopLow < StopHigh)
      98              :     {
      99          549 :         StopMiddle = StopLow + (StopHigh - StopLow) / 2;
     100          549 :         difference = WordECompareQueryItem(STRPTR(t), GETOPERAND(q), StopMiddle, item, false);
     101          549 :         if (difference == 0)
     102              :         {
     103          189 :             StopHigh = StopMiddle;
     104          189 :             *nitem = 1;
     105          189 :             break;
     106              :         }
     107          360 :         else if (difference > 0)
     108          126 :             StopLow = StopMiddle + 1;
     109              :         else
     110          234 :             StopHigh = StopMiddle;
     111              :     }
     112              : 
     113          213 :     if (item->prefix)
     114              :     {
     115           27 :         if (StopLow >= StopHigh)
     116           27 :             StopMiddle = StopHigh;
     117              : 
     118           27 :         *nitem = 0;
     119              : 
     120          111 :         while (StopMiddle < (WordEntry *) STRPTR(t) &&
     121           42 :                WordECompareQueryItem(STRPTR(t), GETOPERAND(q), StopMiddle, item, true) == 0)
     122              :         {
     123           42 :             (*nitem)++;
     124           42 :             StopMiddle++;
     125              :         }
     126              :     }
     127              : 
     128          213 :     return (*nitem > 0) ? StopHigh : NULL;
     129              : }
     130              : 
     131              : 
     132              : /*
     133              :  * sort QueryOperands by (length, word)
     134              :  */
     135              : static int
     136           54 : compareQueryOperand(const void *a, const void *b, void *arg)
     137              : {
     138           54 :     char       *operand = (char *) arg;
     139           54 :     QueryOperand *qa = (*(QueryOperand *const *) a);
     140           54 :     QueryOperand *qb = (*(QueryOperand *const *) b);
     141              : 
     142          108 :     return tsCompareString(operand + qa->distance, qa->length,
     143           54 :                            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           27 : SortAndUniqItems(TSQuery q, int *size)
     156              : {
     157           27 :     char       *operand = GETOPERAND(q);
     158           27 :     QueryItem  *item = GETQUERY(q);
     159              :     QueryOperand **res,
     160              :               **ptr,
     161              :               **prevptr;
     162              : 
     163           27 :     ptr = res = palloc_array(QueryOperand *, *size);
     164              : 
     165              :     /* Collect all operands from the tree to res */
     166          108 :     while ((*size)--)
     167              :     {
     168           81 :         if (item->type == QI_VAL)
     169              :         {
     170           54 :             *ptr = (QueryOperand *) item;
     171           54 :             ptr++;
     172              :         }
     173           81 :         item++;
     174              :     }
     175              : 
     176           27 :     *size = ptr - res;
     177           27 :     if (*size < 2)
     178            0 :         return res;
     179              : 
     180           27 :     qsort_arg(res, *size, sizeof(QueryOperand *), compareQueryOperand, operand);
     181              : 
     182           27 :     ptr = res + 1;
     183           27 :     prevptr = res;
     184              : 
     185              :     /* remove duplicates */
     186           54 :     while (ptr - res < *size)
     187              :     {
     188           27 :         if (compareQueryOperand(ptr, prevptr, operand) != 0)
     189              :         {
     190           27 :             prevptr++;
     191           27 :             *prevptr = *ptr;
     192              :         }
     193           27 :         ptr++;
     194              :     }
     195              : 
     196           27 :     *size = prevptr + 1 - res;
     197           27 :     return res;
     198              : }
     199              : 
     200              : static float
     201            9 : 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            9 :     float       res = -1.0;
     219              :     QueryOperand **item;
     220            9 :     int         size = q->size;
     221              : 
     222            9 :     item = SortAndUniqItems(q, &size);
     223            9 :     if (size < 2)
     224              :     {
     225            0 :         pfree(item);
     226            0 :         return calc_rank_or(w, t, q);
     227              :     }
     228            9 :     pos = palloc0_array(WordEntryPosVector *, q->size);
     229              : 
     230              :     /* A dummy WordEntryPos array to use when haspos is false */
     231            9 :     posnull.npos = 1;
     232            9 :     posnull.pos[0] = 0;
     233            9 :     WEP_SETPOS(posnull.pos[0], MAXENTRYPOS - 1);
     234            9 :     POSNULL = (WordEntryPosVector *) &posnull;
     235              : 
     236           27 :     for (i = 0; i < size; i++)
     237              :     {
     238           18 :         firstentry = entry = find_wordentry(t, q, item[i], &nitem);
     239           18 :         if (!entry)
     240            0 :             continue;
     241              : 
     242           36 :         while (entry - firstentry < nitem)
     243              :         {
     244           18 :             if (entry->haspos)
     245           18 :                 pos[i] = _POSVECPTR(t, entry);
     246              :             else
     247            0 :                 pos[i] = POSNULL;
     248              : 
     249           18 :             dimt = pos[i]->npos;
     250           18 :             post = pos[i]->pos;
     251           27 :             for (k = 0; k < i; k++)
     252              :             {
     253            9 :                 if (!pos[k])
     254            0 :                     continue;
     255            9 :                 lenct = pos[k]->npos;
     256            9 :                 ct = pos[k]->pos;
     257           18 :                 for (l = 0; l < dimt; l++)
     258              :                 {
     259           18 :                     for (p = 0; p < lenct; p++)
     260              :                     {
     261            9 :                         dist = abs((int) WEP_GETPOS(post[l]) - (int) WEP_GETPOS(ct[p]));
     262            9 :                         if (dist || (dist == 0 && (pos[i] == POSNULL || pos[k] == POSNULL)))
     263              :                         {
     264              :                             float       curw;
     265              : 
     266            9 :                             if (!dist)
     267            0 :                                 dist = MAXENTRYPOS;
     268            9 :                             curw = sqrt(wpos(post[l]) * wpos(ct[p]) * word_distance(dist));
     269            9 :                             res = (res < 0) ? curw : 1.0 - (1.0 - res) * (1.0 - curw);
     270              :                         }
     271              :                     }
     272              :                 }
     273              :             }
     274              : 
     275           18 :             entry++;
     276              :         }
     277              :     }
     278            9 :     pfree(pos);
     279            9 :     pfree(item);
     280            9 :     return res;
     281              : }
     282              : 
     283              : static float
     284           18 : 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           18 :     float       res = 0.0;
     295              :     QueryOperand **item;
     296           18 :     int         size = q->size;
     297              : 
     298              :     /* A dummy WordEntryPos array to use when haspos is false */
     299           18 :     posnull.npos = 1;
     300           18 :     posnull.pos[0] = 0;
     301              : 
     302           18 :     item = SortAndUniqItems(q, &size);
     303              : 
     304           54 :     for (i = 0; i < size; i++)
     305              :     {
     306              :         float       resj,
     307              :                     wjm;
     308              :         int32       jm;
     309              : 
     310           36 :         firstentry = entry = find_wordentry(t, q, item[i], &nitem);
     311           36 :         if (!entry)
     312            3 :             continue;
     313              : 
     314           66 :         while (entry - firstentry < nitem)
     315              :         {
     316           33 :             if (entry->haspos)
     317              :             {
     318           33 :                 dimt = POSDATALEN(t, entry);
     319           33 :                 post = POSDATAPTR(t, entry);
     320              :             }
     321              :             else
     322              :             {
     323            0 :                 dimt = posnull.npos;
     324            0 :                 post = posnull.pos;
     325              :             }
     326              : 
     327           33 :             resj = 0.0;
     328           33 :             wjm = -1.0;
     329           33 :             jm = 0;
     330           66 :             for (j = 0; j < dimt; j++)
     331              :             {
     332           33 :                 resj = resj + wpos(post[j]) / ((j + 1) * (j + 1));
     333           33 :                 if (wpos(post[j]) > wjm)
     334              :                 {
     335           33 :                     wjm = wpos(post[j]);
     336           33 :                     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           33 :             res = res + (wjm + resj - wjm / ((jm + 1) * (jm + 1))) / 1.64493406685;
     347              : 
     348           33 :             entry++;
     349              :         }
     350              :     }
     351           18 :     if (size > 0)
     352           18 :         res = res / size;
     353           18 :     pfree(item);
     354           18 :     return res;
     355              : }
     356              : 
     357              : static float
     358           27 : calc_rank(const float *w, TSVector t, TSQuery q, int32 method)
     359              : {
     360           27 :     QueryItem  *item = GETQUERY(q);
     361           27 :     float       res = 0.0;
     362              :     int         len;
     363              : 
     364           27 :     if (!t->size || !q->size)
     365            0 :         return 0.0;
     366              : 
     367              :     /* XXX: What about NOT? */
     368           27 :     res = (item->type == QI_OPR && (item->qoperator.oper == OP_AND ||
     369           18 :                                     item->qoperator.oper == OP_PHRASE)) ?
     370           36 :         calc_rank_and(w, t, q) :
     371           18 :         calc_rank_or(w, t, q);
     372              : 
     373           27 :     if (res < 0)
     374            0 :         res = 1e-20f;
     375              : 
     376           27 :     if ((method & RANK_NORM_LOGLENGTH) && t->size > 0)
     377            0 :         res /= log((double) (cnt_length(t) + 1)) / log(2.0);
     378              : 
     379           27 :     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           27 :     if ((method & RANK_NORM_UNIQ) && t->size > 0)
     389            0 :         res /= (float) (t->size);
     390              : 
     391           27 :     if ((method & RANK_NORM_LOGUNIQ) && t->size > 0)
     392            0 :         res /= log((double) (t->size + 1)) / log(2.0);
     393              : 
     394           27 :     if (method & RANK_NORM_RDIVRPLUS1)
     395            0 :         res /= (res + 1);
     396              : 
     397           27 :     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           27 : ts_rank_tt(PG_FUNCTION_ARGS)
     492              : {
     493           27 :     TSVector    txt = PG_GETARG_TSVECTOR(0);
     494           27 :     TSQuery     query = PG_GETARG_TSQUERY(1);
     495              :     float       res;
     496              : 
     497           27 :     res = calc_rank(default_weights, txt, query, DEF_NORM_METHOD);
     498              : 
     499           27 :     PG_FREE_IF_COPY(txt, 0);
     500           27 :     PG_FREE_IF_COPY(query, 1);
     501           27 :     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          174 : compareDocR(const void *va, const void *vb)
     525              : {
     526          174 :     const DocRepresentation *a = (const DocRepresentation *) va;
     527          174 :     const DocRepresentation *b = (const DocRepresentation *) vb;
     528              : 
     529          174 :     if (WEP_GETPOS(a->pos) == WEP_GETPOS(b->pos))
     530              :     {
     531           18 :         if (WEP_GETWEIGHT(a->pos) == WEP_GETWEIGHT(b->pos))
     532              :         {
     533            3 :             if (a->data.map.entry == b->data.map.entry)
     534            3 :                 return 0;
     535              : 
     536            0 :             return (a->data.map.entry > b->data.map.entry) ? 1 : -1;
     537              :         }
     538              : 
     539           15 :         return (WEP_GETWEIGHT(a->pos) > WEP_GETWEIGHT(b->pos)) ? 1 : -1;
     540              :     }
     541              : 
     542          156 :     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          579 : checkcondition_QueryOperand(void *checkval, QueryOperand *val,
     569              :                             ExecPhraseData *data)
     570              : {
     571          579 :     QueryRepresentation *qr = (QueryRepresentation *) checkval;
     572          579 :     QueryRepresentationOperand *opData = QR_GET_OPERAND_DATA(qr, val);
     573              : 
     574          579 :     if (!opData->operandexists)
     575          222 :         return TS_NO;
     576              : 
     577          357 :     if (data)
     578              :     {
     579          174 :         data->npos = opData->npos;
     580          174 :         data->pos = opData->pos;
     581          174 :         if (opData->reverseinsert)
     582           54 :             data->pos += MAXQROPOS - opData->npos;
     583              :     }
     584              : 
     585          357 :     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          249 : resetQueryRepresentation(QueryRepresentation *qr, bool reverseinsert)
     599              : {
     600              :     int         i;
     601              : 
     602         1008 :     for (i = 0; i < qr->query->size; i++)
     603              :     {
     604          759 :         qr->operandData[i].operandexists = false;
     605          759 :         qr->operandData[i].reverseinsert = reverseinsert;
     606          759 :         qr->operandData[i].npos = 0;
     607              :     }
     608          249 : }
     609              : 
     610              : static void
     611          360 : fillQueryRepresentationData(QueryRepresentation *qr, DocRepresentation *entry)
     612              : {
     613              :     int         i;
     614              :     int         lastPos;
     615              :     QueryRepresentationOperand *opData;
     616              : 
     617          723 :     for (i = 0; i < entry->data.query.nitem; i++)
     618              :     {
     619          363 :         if (entry->data.query.items[i]->type != QI_VAL)
     620            0 :             continue;
     621              : 
     622          363 :         opData = QR_GET_OPERAND_DATA(qr, entry->data.query.items[i]);
     623              : 
     624          363 :         opData->operandexists = true;
     625              : 
     626          363 :         if (opData->npos == 0)
     627              :         {
     628          330 :             lastPos = (opData->reverseinsert) ? (MAXQROPOS - 1) : 0;
     629          330 :             opData->pos[lastPos] = entry->pos;
     630          330 :             opData->npos++;
     631          330 :             continue;
     632              :         }
     633              : 
     634           66 :         lastPos = opData->reverseinsert ?
     635           33 :             (MAXQROPOS - opData->npos) :
     636           33 :             (opData->npos - 1);
     637              : 
     638           33 :         if (WEP_GETPOS(opData->pos[lastPos]) != WEP_GETPOS(entry->pos))
     639              :         {
     640           42 :             lastPos = opData->reverseinsert ?
     641           21 :                 (MAXQROPOS - 1 - opData->npos) :
     642           21 :                 (opData->npos);
     643              : 
     644           21 :             opData->pos[lastPos] = entry->pos;
     645           21 :             opData->npos++;
     646              :         }
     647              :     }
     648          360 : }
     649              : 
     650              : static bool
     651          162 : Cover(DocRepresentation *doc, int len, QueryRepresentation *qr, CoverExt *ext)
     652              : {
     653              :     DocRepresentation *ptr;
     654          162 :     int         lastpos = ext->pos;
     655          162 :     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          162 :     check_stack_depth();
     662              : 
     663          162 :     resetQueryRepresentation(qr, false);
     664              : 
     665          162 :     ext->p = INT_MAX;
     666          162 :     ext->q = 0;
     667          162 :     ptr = doc + ext->pos;
     668              : 
     669              :     /* find upper bound of cover from current position, move up */
     670          303 :     while (ptr - doc < len)
     671              :     {
     672          228 :         fillQueryRepresentationData(qr, ptr);
     673              : 
     674          228 :         if (TS_execute(GETQUERY(qr->query), qr,
     675              :                        TS_EXEC_EMPTY, checkcondition_QueryOperand))
     676              :         {
     677           87 :             if (WEP_GETPOS(ptr->pos) > ext->q)
     678              :             {
     679           87 :                 ext->q = WEP_GETPOS(ptr->pos);
     680           87 :                 ext->end = ptr;
     681           87 :                 lastpos = ptr - doc;
     682           87 :                 found = true;
     683              :             }
     684           87 :             break;
     685              :         }
     686          141 :         ptr++;
     687              :     }
     688              : 
     689          162 :     if (!found)
     690           75 :         return false;
     691              : 
     692           87 :     resetQueryRepresentation(qr, true);
     693              : 
     694           87 :     ptr = doc + lastpos;
     695              : 
     696              :     /* find lower bound of cover from found upper bound, move down */
     697          132 :     while (ptr >= doc + ext->pos)
     698              :     {
     699              :         /*
     700              :          * we scan doc from right to left, so pos info in reverse order!
     701              :          */
     702          132 :         fillQueryRepresentationData(qr, ptr);
     703              : 
     704          132 :         if (TS_execute(GETQUERY(qr->query), qr,
     705              :                        TS_EXEC_EMPTY, checkcondition_QueryOperand))
     706              :         {
     707           87 :             if (WEP_GETPOS(ptr->pos) < ext->p)
     708              :             {
     709           87 :                 ext->begin = ptr;
     710           87 :                 ext->p = WEP_GETPOS(ptr->pos);
     711              :             }
     712           87 :             break;
     713              :         }
     714           45 :         ptr--;
     715              :     }
     716              : 
     717           87 :     if (ext->p <= ext->q)
     718              :     {
     719              :         /*
     720              :          * set position for next try to next lexeme after beginning of found
     721              :          * cover
     722              :          */
     723           87 :         ext->pos = (ptr - doc) + 1;
     724           87 :         return true;
     725              :     }
     726              : 
     727            0 :     ext->pos++;
     728            0 :     return Cover(doc, len, qr, ext);
     729              : }
     730              : 
     731              : static DocRepresentation *
     732           78 : get_docrep(TSVector txt, QueryRepresentation *qr, int *doclen)
     733              : {
     734           78 :     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           78 :     int         len = qr->query->size * 4,
     743           78 :                 cur = 0;
     744              :     DocRepresentation *doc;
     745              : 
     746           78 :     doc = palloc_array(DocRepresentation, len);
     747              : 
     748              :     /*
     749              :      * Iterate through query to make DocRepresentation for words and it's
     750              :      * entries satisfied by query
     751              :      */
     752          318 :     for (i = 0; i < qr->query->size; i++)
     753              :     {
     754              :         QueryOperand *curoperand;
     755              : 
     756          240 :         if (item[i].type != QI_VAL)
     757           81 :             continue;
     758              : 
     759          159 :         curoperand = &item[i].qoperand;
     760              : 
     761          159 :         firstentry = entry = find_wordentry(txt, qr->query, curoperand, &nitem);
     762          159 :         if (!entry)
     763            3 :             continue;
     764              : 
     765              :         /* iterations over entries in tsvector */
     766          327 :         while (entry - firstentry < nitem)
     767              :         {
     768          171 :             if (entry->haspos)
     769              :             {
     770          165 :                 dimt = POSDATALEN(txt, entry);
     771          165 :                 post = POSDATAPTR(txt, entry);
     772              :             }
     773              :             else
     774              :             {
     775              :                 /* ignore words without positions */
     776            6 :                 entry++;
     777            6 :                 continue;
     778              :             }
     779              : 
     780          165 :             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          357 :             for (j = 0; j < dimt; j++)
     788              :             {
     789          192 :                 if (curoperand->weight == 0 ||
     790           15 :                     curoperand->weight & (1 << WEP_GETWEIGHT(post[j])))
     791              :                 {
     792          186 :                     doc[cur].pos = post[j];
     793          186 :                     doc[cur].data.map.entry = entry;
     794          186 :                     doc[cur].data.map.item = (QueryItem *) curoperand;
     795          186 :                     cur++;
     796              :                 }
     797              :             }
     798              : 
     799          165 :             entry++;
     800              :         }
     801              :     }
     802              : 
     803           78 :     if (cur > 0)
     804              :     {
     805           75 :         DocRepresentation *rptr = doc + 1,
     806           75 :                    *wptr = doc,
     807              :                     storage;
     808              : 
     809              :         /*
     810              :          * Sort representation in ascending order by pos and entry
     811              :          */
     812           75 :         qsort(doc, cur, sizeof(DocRepresentation), compareDocR);
     813              : 
     814              :         /*
     815              :          * Join QueryItem per WordEntry and its position
     816              :          */
     817           75 :         storage.pos = doc->pos;
     818           75 :         storage.data.query.items = palloc_array(QueryItem *, qr->query->size);
     819           75 :         storage.data.query.items[0] = doc->data.map.item;
     820           75 :         storage.data.query.nitem = 1;
     821              : 
     822          186 :         while (rptr - doc < cur)
     823              :         {
     824          111 :             if (rptr->pos == (rptr - 1)->pos &&
     825            3 :                 rptr->data.map.entry == (rptr - 1)->data.map.entry)
     826              :             {
     827            3 :                 storage.data.query.items[storage.data.query.nitem] = rptr->data.map.item;
     828            3 :                 storage.data.query.nitem++;
     829              :             }
     830              :             else
     831              :             {
     832          108 :                 *wptr = storage;
     833          108 :                 wptr++;
     834          108 :                 storage.pos = rptr->pos;
     835          108 :                 storage.data.query.items = palloc_array(QueryItem *, qr->query->size);
     836          108 :                 storage.data.query.items[0] = rptr->data.map.item;
     837          108 :                 storage.data.query.nitem = 1;
     838              :             }
     839              : 
     840          111 :             rptr++;
     841              :         }
     842              : 
     843           75 :         *wptr = storage;
     844           75 :         wptr++;
     845              : 
     846           75 :         *doclen = wptr - doc;
     847           75 :         return doc;
     848              :     }
     849              : 
     850            3 :     pfree(doc);
     851            3 :     return NULL;
     852              : }
     853              : 
     854              : static float4
     855           78 : calc_rank_cd(const float4 *arrdata, TSVector txt, TSQuery query, int method)
     856              : {
     857              :     DocRepresentation *doc;
     858              :     int         len,
     859              :                 i,
     860           78 :                 doclen = 0;
     861              :     CoverExt    ext;
     862           78 :     double      Wdoc = 0.0;
     863              :     double      invws[NUM_WEIGHTS];
     864           78 :     double      SumDist = 0.0,
     865           78 :                 PrevExtPos = 0.0;
     866           78 :     int         NExtent = 0;
     867              :     QueryRepresentation qr;
     868              : 
     869              : 
     870          390 :     for (i = 0; i < NUM_WEIGHTS; i++)
     871              :     {
     872          312 :         invws[i] = ((double) ((arrdata[i] >= 0) ? arrdata[i] : default_weights[i]));
     873          312 :         if (invws[i] > 1.0)
     874            0 :             ereport(ERROR,
     875              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     876              :                      errmsg("weight out of range")));
     877          312 :         invws[i] = 1.0 / invws[i];
     878              :     }
     879              : 
     880           78 :     qr.query = query;
     881           78 :     qr.operandData = palloc0_array(QueryRepresentationOperand, query->size);
     882              : 
     883           78 :     doc = get_docrep(txt, &qr, &doclen);
     884           78 :     if (!doc)
     885              :     {
     886            3 :         pfree(qr.operandData);
     887            3 :         return 0.0;
     888              :     }
     889              : 
     890          375 :     MemSet(&ext, 0, sizeof(CoverExt));
     891          162 :     while (Cover(doc, doclen, &qr, &ext))
     892              :     {
     893           87 :         double      Cpos = 0.0;
     894           87 :         double      InvSum = 0.0;
     895              :         double      CurExtPos;
     896              :         int         nNoise;
     897           87 :         DocRepresentation *ptr = ext.begin;
     898              : 
     899          219 :         while (ptr <= ext.end)
     900              :         {
     901          132 :             InvSum += invws[WEP_GETWEIGHT(ptr->pos)];
     902          132 :             ptr++;
     903              :         }
     904              : 
     905           87 :         Cpos = ((double) (ext.end - ext.begin + 1)) / InvSum;
     906              : 
     907              :         /*
     908              :          * if doc are big enough then ext.q may be equal to ext.p due to limit
     909              :          * of positional information. In this case we approximate number of
     910              :          * noise word as half cover's length
     911              :          */
     912           87 :         nNoise = (ext.q - ext.p) - (ext.end - ext.begin);
     913           87 :         if (nNoise < 0)
     914            0 :             nNoise = (ext.end - ext.begin) / 2;
     915           87 :         Wdoc += Cpos / ((double) (1 + nNoise));
     916              : 
     917           87 :         CurExtPos = ((double) (ext.q + ext.p)) / 2.0;
     918           87 :         if (NExtent > 0 && CurExtPos > PrevExtPos /* prevent division by
     919              :                                                      * zero in a case of
     920              :               * multiple lexize */ )
     921           21 :             SumDist += 1.0 / (CurExtPos - PrevExtPos);
     922              : 
     923           87 :         PrevExtPos = CurExtPos;
     924           87 :         NExtent++;
     925              :     }
     926              : 
     927           75 :     if ((method & RANK_NORM_LOGLENGTH) && txt->size > 0)
     928            0 :         Wdoc /= log((double) (cnt_length(txt) + 1));
     929              : 
     930           75 :     if (method & RANK_NORM_LENGTH)
     931              :     {
     932            0 :         len = cnt_length(txt);
     933            0 :         if (len > 0)
     934            0 :             Wdoc /= (double) len;
     935              :     }
     936              : 
     937           75 :     if ((method & RANK_NORM_EXTDIST) && NExtent > 0 && SumDist > 0)
     938            0 :         Wdoc /= ((double) NExtent) / SumDist;
     939              : 
     940           75 :     if ((method & RANK_NORM_UNIQ) && txt->size > 0)
     941            0 :         Wdoc /= (double) (txt->size);
     942              : 
     943           75 :     if ((method & RANK_NORM_LOGUNIQ) && txt->size > 0)
     944            0 :         Wdoc /= log((double) (txt->size + 1)) / log(2.0);
     945              : 
     946           75 :     if (method & RANK_NORM_RDIVRPLUS1)
     947            0 :         Wdoc /= (Wdoc + 1);
     948              : 
     949           75 :     pfree(doc);
     950              : 
     951           75 :     pfree(qr.operandData);
     952              : 
     953           75 :     return (float4) Wdoc;
     954              : }
     955              : 
     956              : Datum
     957            0 : ts_rankcd_wttf(PG_FUNCTION_ARGS)
     958              : {
     959            0 :     ArrayType  *win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
     960            0 :     TSVector    txt = PG_GETARG_TSVECTOR(1);
     961            0 :     TSQuery     query = PG_GETARG_TSQUERY(2);
     962            0 :     int         method = PG_GETARG_INT32(3);
     963              :     float       weights[NUM_WEIGHTS];
     964              :     float       res;
     965              : 
     966            0 :     getWeights(win, weights);
     967            0 :     res = calc_rank_cd(weights, txt, query, method);
     968              : 
     969            0 :     PG_FREE_IF_COPY(win, 0);
     970            0 :     PG_FREE_IF_COPY(txt, 1);
     971            0 :     PG_FREE_IF_COPY(query, 2);
     972            0 :     PG_RETURN_FLOAT4(res);
     973              : }
     974              : 
     975              : Datum
     976            0 : ts_rankcd_wtt(PG_FUNCTION_ARGS)
     977              : {
     978            0 :     ArrayType  *win = (ArrayType *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
     979            0 :     TSVector    txt = PG_GETARG_TSVECTOR(1);
     980            0 :     TSQuery     query = PG_GETARG_TSQUERY(2);
     981              :     float       weights[NUM_WEIGHTS];
     982              :     float       res;
     983              : 
     984            0 :     getWeights(win, weights);
     985            0 :     res = calc_rank_cd(weights, txt, query, DEF_NORM_METHOD);
     986              : 
     987            0 :     PG_FREE_IF_COPY(win, 0);
     988            0 :     PG_FREE_IF_COPY(txt, 1);
     989            0 :     PG_FREE_IF_COPY(query, 2);
     990            0 :     PG_RETURN_FLOAT4(res);
     991              : }
     992              : 
     993              : Datum
     994            0 : ts_rankcd_ttf(PG_FUNCTION_ARGS)
     995              : {
     996            0 :     TSVector    txt = PG_GETARG_TSVECTOR(0);
     997            0 :     TSQuery     query = PG_GETARG_TSQUERY(1);
     998            0 :     int         method = PG_GETARG_INT32(2);
     999              :     float       res;
    1000              : 
    1001            0 :     res = calc_rank_cd(default_weights, txt, query, method);
    1002              : 
    1003            0 :     PG_FREE_IF_COPY(txt, 0);
    1004            0 :     PG_FREE_IF_COPY(query, 1);
    1005            0 :     PG_RETURN_FLOAT4(res);
    1006              : }
    1007              : 
    1008              : Datum
    1009           78 : ts_rankcd_tt(PG_FUNCTION_ARGS)
    1010              : {
    1011           78 :     TSVector    txt = PG_GETARG_TSVECTOR(0);
    1012           78 :     TSQuery     query = PG_GETARG_TSQUERY(1);
    1013              :     float       res;
    1014              : 
    1015           78 :     res = calc_rank_cd(default_weights, txt, query, DEF_NORM_METHOD);
    1016              : 
    1017           78 :     PG_FREE_IF_COPY(txt, 0);
    1018           78 :     PG_FREE_IF_COPY(query, 1);
    1019           78 :     PG_RETURN_FLOAT4(res);
    1020              : }
        

Generated by: LCOV version 2.0-1