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

Generated by: LCOV version 1.11