LCOV - code coverage report
Current view: top level - matcher - multixorpostlist.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core r Lines: 110 152 72.4 %
Date: 2011-08-21 Functions: 14 18 77.8 %
Branches: 70 132 53.0 %

           Branch data     Line data    Source code
       1                 :            : /** @file multixorpostlist.cc
       2                 :            :  * @brief N-way XOR postlist
       3                 :            :  */
       4                 :            : /* Copyright (C) 2007,2009,2010 Olly Betts
       5                 :            :  * Copyright (C) 2009 Lemur Consulting Ltd
       6                 :            :  *
       7                 :            :  * This program is free software; you can redistribute it and/or
       8                 :            :  * modify it under the terms of the GNU General Public License as
       9                 :            :  * published by the Free Software Foundation; either version 2 of the
      10                 :            :  * License, or (at your option) any later version.
      11                 :            :  *
      12                 :            :  * This program is distributed in the hope that it will be useful,
      13                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15                 :            :  * GNU General Public License for more details.
      16                 :            :  *
      17                 :            :  * You should have received a copy of the GNU General Public License
      18                 :            :  * along with this program; if not, write to the Free Software
      19                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
      20                 :            :  */
      21                 :            : 
      22                 :            : #include <config.h>
      23                 :            : 
      24                 :            : #include "multixorpostlist.h"
      25                 :            : 
      26                 :            : #include "debuglog.h"
      27                 :            : #include "multimatch.h"
      28                 :            : #include "omassert.h"
      29                 :            : 
      30                 :            : using namespace std;
      31                 :            : 
      32                 :        134 : MultiXorPostList::~MultiXorPostList()
      33                 :            : {
      34 [ +  - ][ #  # ]:        134 :     if (plist) {
                 [ #  # ]
      35 [ -  + ][ #  # ]:        134 :         for (size_t i = 0; i < n_kids; ++i) {
                 [ #  # ]
      36 [ #  # ][ #  # ]:          0 :             delete plist[i];
                 [ #  # ]
      37                 :            :         }
      38 [ +  - ][ #  # ]:        134 :         delete [] plist;
                 [ #  # ]
      39                 :            :     }
      40 [ +  - ][ #  # ]:        134 : }
                 [ #  # ]
      41                 :            : 
      42                 :            : Xapian::doccount
      43                 :        134 : MultiXorPostList::get_termfreq_min() const
      44                 :            : {
      45                 :            :     // The number of matching documents is minimised when we have the maximum
      46                 :            :     // even overlap between the sub-postlists, but that's rather tricky to
      47                 :            :     // calculate in general, so just return 0 for now.
      48                 :            :     //
      49                 :            :     // FIXME: we can certainly work out a better bound in at least some cases
      50                 :            :     // (e.g. when tf_min(X) > sum(i!=X){ tf_max(i) } for some sub-pl X).
      51                 :        134 :     return 0;
      52                 :            : }
      53                 :            : 
      54                 :            : Xapian::doccount
      55                 :        134 : MultiXorPostList::get_termfreq_max() const
      56                 :            : {
      57                 :            :     // Maximum is if all sub-postlists are disjoint.
      58                 :        134 :     Xapian::doccount result = plist[0]->get_termfreq_max();
      59                 :        134 :     bool all_exact = (result == plist[0]->get_termfreq_min());
      60                 :        134 :     bool overflow = false;
      61         [ +  + ]:        300 :     for (size_t i = 1; i < n_kids; ++i) {
      62                 :        166 :         Xapian::doccount tf_max = plist[i]->get_termfreq_max();
      63                 :        166 :         Xapian::doccount old_result = result;
      64                 :        166 :         result += tf_max;
      65                 :            :         // Catch overflowing the type too.
      66         [ -  + ]:        166 :         if (result < old_result)
      67                 :          0 :             overflow = true;
      68         [ +  + ]:        166 :         if (all_exact)
      69                 :        127 :             all_exact = (tf_max == plist[i]->get_termfreq_min());
      70 [ +  + ][ +  - ]:        166 :         if (!all_exact && (overflow || result >= db_size))
                 [ -  + ]
      71                 :          0 :             return db_size;
      72                 :            :     }
      73 [ +  + ][ +  - ]:        134 :     if (all_exact && (overflow || result > db_size)) {
                 [ +  + ]
      74                 :            :         // If the sub-postlist tfs are all exact, then if the sum of them has
      75                 :            :         // a different odd/even-ness to db_size then max tf of the XOR can't
      76                 :            :         // achieve db_size.
      77                 :         32 :         return db_size - ((result & 1) == (db_size & 1));
      78                 :            :     }
      79                 :        134 :     return result;
      80                 :            : }
      81                 :            : 
      82                 :            : Xapian::doccount
      83                 :        182 : MultiXorPostList::get_termfreq_est() const
      84                 :            : {
      85                 :            :     // We calculate the estimate assuming independence.  The simplest
      86                 :            :     // way to calculate this seems to be a series of (n_kids - 1) pairwise
      87                 :            :     // calculations, which gives the same answer regardless of the order.
      88                 :        182 :     double scale = 1.0 / db_size;
      89                 :        182 :     double P_est = plist[0]->get_termfreq_est() * scale;
      90         [ +  + ]:        396 :     for (size_t i = 1; i < n_kids; ++i) {
      91                 :        214 :         double P_i = plist[i]->get_termfreq_est() * scale;
      92                 :        214 :         P_est += P_i - 2.0 * P_est * P_i;
      93                 :            :     }
      94                 :        182 :     return static_cast<Xapian::doccount>(P_est * db_size + 0.5);
      95                 :            : }
      96                 :            : 
      97                 :            : TermFreqs
      98                 :         32 : MultiXorPostList::get_termfreq_est_using_stats(
      99                 :            :         const Xapian::Weight::Internal & stats) const
     100                 :            : {
     101                 :            :     LOGCALL(MATCH, TermFreqs, "MultiXorPostList::get_termfreq_est_using_stats", stats);
     102                 :            :     // We calculate the estimate assuming independence.  The simplest
     103                 :            :     // way to calculate this seems to be a series of (n_kids - 1) pairwise
     104                 :            :     // calculations, which gives the same answer regardless of the order.
     105                 :         32 :     TermFreqs freqs(plist[0]->get_termfreq_est_using_stats(stats));
     106                 :            : 
     107                 :            :     // Our caller should have ensured this.
     108                 :            :     Assert(stats.collection_size);
     109                 :         32 :     double scale = 1.0 / stats.collection_size;
     110                 :         32 :     double P_est = freqs.termfreq * scale;
     111                 :         32 :     double Pr_est = freqs.reltermfreq * scale;
     112                 :            : 
     113         [ +  + ]:         64 :     for (size_t i = 1; i < n_kids; ++i) {
     114                 :         32 :         double P_i = freqs.termfreq * scale;
     115                 :         32 :         P_est += P_i - 2.0 * P_est * P_i;
     116                 :            :         // If the rset is empty, Pr_est should be 0 already, so leave
     117                 :            :         // it alone.
     118         [ -  + ]:         32 :         if (stats.rset_size != 0) {
     119                 :          0 :             double Pr_i = freqs.reltermfreq / stats.rset_size;
     120                 :          0 :             Pr_est += Pr_i - 2.0 * Pr_est * Pr_i;
     121                 :            :         }
     122                 :            :     }
     123                 :         32 :     RETURN(TermFreqs(Xapian::doccount(P_est * stats.collection_size + 0.5),
     124                 :            :                      Xapian::doccount(Pr_est * stats.rset_size + 0.5)));
     125                 :            : }
     126                 :            : 
     127                 :            : Xapian::weight
     128                 :          6 : MultiXorPostList::get_maxweight() const
     129                 :            : {
     130                 :            :     LOGCALL(MATCH, Xapian::weight, "MultiXorPostList::get_maxweight", NO_ARGS);
     131                 :          6 :     RETURN(max_total);
     132                 :            : }
     133                 :            : 
     134                 :            : Xapian::docid
     135                 :       2213 : MultiXorPostList::get_docid() const
     136                 :            : {
     137                 :       2213 :     return did;
     138                 :            : }
     139                 :            : 
     140                 :            : Xapian::termcount
     141                 :        624 : MultiXorPostList::get_doclength() const
     142                 :            : {
     143                 :            :     Assert(did);
     144                 :        624 :     Xapian::termcount doclength = 0;
     145                 :        624 :     bool doclength_set = false;
     146         [ +  + ]:       1872 :     for (size_t i = 0; i < n_kids; ++i) {
     147         [ +  + ]:       1248 :         if (plist[i]->get_docid() == did) {
     148         [ +  - ]:        624 :             if (doclength_set) {
     149                 :            :                 AssertEq(doclength, plist[i]->get_doclength());
     150                 :            :             } else {
     151                 :        624 :                 doclength = plist[i]->get_doclength();
     152                 :        624 :                 doclength_set = true;
     153                 :            :             }
     154                 :            :         }
     155                 :            :     }
     156                 :            :     Assert(doclength_set);
     157                 :        624 :     return doclength;
     158                 :            : }
     159                 :            : 
     160                 :            : Xapian::weight
     161                 :       1649 : MultiXorPostList::get_weight() const
     162                 :            : {
     163                 :            :     Assert(did);
     164                 :       1649 :     Xapian::weight result = 0;
     165         [ +  + ]:       4993 :     for (size_t i = 0; i < n_kids; ++i) {
     166         [ +  + ]:       3344 :         if (plist[i]->get_docid() == did)
     167                 :       1701 :             result += plist[i]->get_weight();
     168                 :            :     }
     169                 :       1649 :     return result;
     170                 :            : }
     171                 :            : 
     172                 :            : bool
     173                 :       2273 : MultiXorPostList::at_end() const
     174                 :            : {
     175                 :       2273 :     return (did == 0);
     176                 :            : }
     177                 :            : 
     178                 :            : Xapian::weight
     179                 :        134 : MultiXorPostList::recalc_maxweight()
     180                 :            : {
     181                 :            :     LOGCALL(MATCH, Xapian::weight, "MultiXorPostList::recalc_maxweight", NO_ARGS);
     182                 :        134 :     max_total = plist[0]->recalc_maxweight();
     183                 :        134 :     double min_max = max_total;
     184         [ +  + ]:        300 :     for (size_t i = 1; i < n_kids; ++i) {
     185                 :        166 :         Xapian::weight new_max = plist[i]->recalc_maxweight();
     186         [ +  + ]:        166 :         if (new_max < min_max)
     187                 :         54 :             min_max = new_max;
     188                 :        166 :         max_total += new_max;
     189                 :            :     }
     190         [ +  + ]:        134 :     if ((n_kids & 1) == 0) {
     191                 :            :         // If n_kids is even, we omit the child with the smallest maxweight.
     192                 :        102 :         max_total -= min_max;
     193                 :            :     }
     194                 :        134 :     RETURN(max_total);
     195                 :            : }
     196                 :            : 
     197                 :            : PostList *
     198                 :       2574 : MultiXorPostList::next(Xapian::weight w_min)
     199                 :            : {
     200                 :            :     LOGCALL(MATCH, PostList *, "MultiXorPostList::next", w_min);
     201                 :       2574 :     Xapian::docid old_did = did;
     202                 :       2574 :     did = 0;
     203                 :       2574 :     size_t matching_count = 0;
     204         [ +  + ]:       7820 :     for (size_t i = 0; i < n_kids; ++i) {
     205 [ +  + ][ +  + ]:       5246 :         if (old_did == 0 || plist[i]->get_docid() <= old_did) {
                 [ +  + ]
     206                 :            :             // FIXME: calculate the min weight required here...
     207                 :       2959 :             PostList * res = plist[i]->next(0);
     208         [ +  + ]:       2959 :             if (res) {
     209         [ +  - ]:         16 :                 delete plist[i];
     210                 :         16 :                 plist[i] = res;
     211                 :         16 :                 matcher->recalc_maxweight();
     212                 :            :             }
     213                 :            : 
     214         [ +  + ]:       2959 :             if (plist[i]->at_end()) {
     215                 :        166 :                 erase_sublist(i--);
     216                 :        166 :                 continue;
     217                 :            :             }
     218                 :            :         }
     219                 :            : 
     220                 :       5080 :         Xapian::docid new_did = plist[i]->get_docid();
     221   [ +  +  +  + ]:       5080 :         if (did == 0 || new_did < did) {
     222                 :       4356 :             did = new_did;
     223                 :       4356 :             matching_count = 1;
     224         [ +  + ]:        724 :         } else if (new_did == did) {
     225                 :        219 :             ++matching_count;
     226                 :            :         }
     227                 :            :     }
     228                 :            : 
     229         [ +  + ]:       2574 :     if (n_kids == 1) {
     230                 :        134 :         n_kids = 0;
     231                 :        134 :         RETURN(plist[0]);
     232                 :            :     }
     233                 :            : 
     234                 :            :     // We've reached the end of all posting lists.
     235         [ -  + ]:       2440 :     if (did == 0)
     236                 :          0 :         RETURN(NULL);
     237                 :            : 
     238                 :            :     // An odd number of sub-postlists match this docid, so the XOR matches.
     239         [ +  + ]:       2440 :     if (matching_count & 1)
     240                 :       2273 :         RETURN(NULL);
     241                 :            : 
     242                 :            :     // An even number of sub-postlists match this docid, so advance again.
     243                 :       2574 :     RETURN(next(w_min));
     244                 :            : }
     245                 :            : 
     246                 :            : PostList *
     247                 :          0 : MultiXorPostList::skip_to(Xapian::docid did_min, Xapian::weight w_min)
     248                 :            : {
     249                 :            :     LOGCALL(MATCH, PostList *, "MultiXorPostList::skip_to", did_min | w_min);
     250                 :          0 :     Xapian::docid old_did = did;
     251                 :          0 :     did = 0;
     252                 :          0 :     size_t matching_count = 0;
     253         [ #  # ]:          0 :     for (size_t i = 0; i < n_kids; ++i) {
     254 [ #  # ][ #  # ]:          0 :         if (old_did == 0 || plist[i]->get_docid() < did_min) {
                 [ #  # ]
     255                 :            :             // FIXME: calculate the min weight required here...
     256                 :          0 :             PostList * res = plist[i]->skip_to(did_min, 0);
     257         [ #  # ]:          0 :             if (res) {
     258         [ #  # ]:          0 :                 delete plist[i];
     259                 :          0 :                 plist[i] = res;
     260                 :          0 :                 matcher->recalc_maxweight();
     261                 :            :             }
     262                 :            : 
     263         [ #  # ]:          0 :             if (plist[i]->at_end()) {
     264                 :          0 :                 erase_sublist(i--);
     265                 :          0 :                 continue;
     266                 :            :             }
     267                 :            :         }
     268                 :            : 
     269                 :          0 :         Xapian::docid new_did = plist[i]->get_docid();
     270   [ #  #  #  # ]:          0 :         if (did == 0 || new_did < did) {
     271                 :          0 :             did = new_did;
     272                 :          0 :             matching_count = 1;
     273         [ #  # ]:          0 :         } else if (new_did == did) {
     274                 :          0 :             ++matching_count;
     275                 :            :         }
     276                 :            :     }
     277                 :            : 
     278         [ #  # ]:          0 :     if (n_kids == 1) {
     279                 :            :         AssertEq(matching_count, 1);
     280                 :          0 :         n_kids = 0;
     281                 :          0 :         RETURN(plist[0]);
     282                 :            :     }
     283                 :            : 
     284                 :            :     // We've reached the end of all posting lists.
     285         [ #  # ]:          0 :     if (did == 0)
     286                 :          0 :         RETURN(NULL);
     287                 :            : 
     288                 :            :     // An odd number of sub-postlists match this docid, so the XOR matches.
     289         [ #  # ]:          0 :     if (matching_count & 1)
     290                 :          0 :         RETURN(NULL);
     291                 :            : 
     292                 :            :     // An even number of sub-postlists match this docid, so call next.
     293                 :          0 :     RETURN(next(w_min));
     294                 :            : }
     295                 :            : 
     296                 :            : string
     297                 :          0 : MultiXorPostList::get_description() const
     298                 :            : {
     299                 :          0 :     string desc("(");
     300                 :          0 :     desc += plist[0]->get_description();
     301         [ #  # ]:          0 :     for (size_t i = 1; i < n_kids; ++i) {
     302                 :          0 :         desc += " XOR ";
     303                 :          0 :         desc += plist[i]->get_description();
     304                 :            :     }
     305                 :          0 :     desc += ')';
     306                 :          0 :     return desc;
     307                 :            : }
     308                 :            : 
     309                 :            : Xapian::termcount
     310                 :        624 : MultiXorPostList::get_wdf() const
     311                 :            : {
     312                 :        624 :     Xapian::termcount totwdf = 0;
     313         [ +  + ]:       1872 :     for (size_t i = 0; i < n_kids; ++i) {
     314         [ +  + ]:       1248 :         if (plist[i]->get_docid() == did)
     315                 :        624 :             totwdf += plist[i]->get_wdf();
     316                 :            :     }
     317                 :        624 :     return totwdf;
     318                 :            : }
     319                 :            : 
     320                 :            : Xapian::termcount
     321                 :        168 : MultiXorPostList::count_matching_subqs() const
     322                 :            : {
     323                 :        168 :     Xapian::termcount total = 0;
     324         [ +  + ]:        527 :     for (size_t i = 0; i < n_kids; ++i) {
     325         [ +  + ]:        359 :         if (plist[i]->get_docid() == did)
     326                 :        194 :             total += plist[i]->count_matching_subqs();
     327                 :            :     }
     328                 :        168 :     return total;
     329                 :            : }

Generated by: LCOV version 1.8