LCOV - code coverage report
Current view: top level - api - matchspy.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core r Lines: 124 152 81.6 %
Date: 2011-08-21 Functions: 39 55 70.9 %
Branches: 29 58 50.0 %

           Branch data     Line data    Source code
       1                 :            : /** @file matchspy.cc
       2                 :            :  * @brief MatchSpy implementation.
       3                 :            :  */
       4                 :            : /* Copyright (C) 2007,2008,2009,2010 Olly Betts
       5                 :            :  * Copyright (C) 2007,2009 Lemur Consulting Ltd
       6                 :            :  * Copyright (C) 2010 Richard Boulton
       7                 :            :  *
       8                 :            :  * This program is free software; you can redistribute it and/or modify
       9                 :            :  * it under the terms of the GNU General Public License as published by
      10                 :            :  * the Free Software Foundation; either version 2 of the License, or
      11                 :            :  * (at your option) any later version.
      12                 :            :  *
      13                 :            :  * This program is distributed in the hope that it will be useful,
      14                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16                 :            :  * GNU General Public License for more details.
      17                 :            :  *
      18                 :            :  * You should have received a copy of the GNU General Public License
      19                 :            :  * along with this program; if not, write to the Free Software
      20                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
      21                 :            :  */
      22                 :            : 
      23                 :            : #include <config.h>
      24                 :            : #include <xapian/matchspy.h>
      25                 :            : 
      26                 :            : #include <xapian/document.h>
      27                 :            : #include <xapian/error.h>
      28                 :            : #include <xapian/queryparser.h>
      29                 :            : #include <xapian/registry.h>
      30                 :            : 
      31                 :            : #include <map>
      32                 :            : #include <string>
      33                 :            : #include <vector>
      34                 :            : 
      35                 :            : #include "autoptr.h"
      36                 :            : #include "debuglog.h"
      37                 :            : #include "omassert.h"
      38                 :            : #include "serialise.h"
      39                 :            : #include "stringutils.h"
      40                 :            : #include "str.h"
      41                 :            : #include "termlist.h"
      42                 :            : 
      43                 :            : #include <cfloat>
      44                 :            : #include <cmath>
      45                 :            : 
      46                 :            : using namespace std;
      47                 :            : using namespace Xapian;
      48                 :            : 
      49 [ #  # ][ #  # ]:     176984 : MatchSpy::~MatchSpy() {}
                 [ -  + ]
      50                 :            : 
      51                 :            : MatchSpy *
      52                 :          1 : MatchSpy::clone() const {
      53                 :          1 :     throw UnimplementedError("MatchSpy not suitable for use with remote searches - clone() method unimplemented");
      54                 :            : }
      55                 :            : 
      56                 :            : string
      57                 :          1 : MatchSpy::name() const {
      58                 :          1 :     throw UnimplementedError("MatchSpy not suitable for use with remote searches - name() method unimplemented");
      59                 :            : }
      60                 :            : 
      61                 :            : string
      62                 :          1 : MatchSpy::serialise() const {
      63                 :          1 :     throw UnimplementedError("MatchSpy not suitable for use with remote searches - serialise() method unimplemented");
      64                 :            : }
      65                 :            : 
      66                 :            : MatchSpy *
      67                 :          1 : MatchSpy::unserialise(const string &, const Registry &) const {
      68                 :          1 :     throw UnimplementedError("MatchSpy not suitable for use with remote searches - unserialise() method unimplemented");
      69                 :            : }
      70                 :            : 
      71                 :            : string
      72                 :          1 : MatchSpy::serialise_results() const {
      73                 :          1 :     throw UnimplementedError("MatchSpy not suitable for use with remote searches - serialise_results() method unimplemented");
      74                 :            : }
      75                 :            : 
      76                 :            : void
      77                 :          1 : MatchSpy::merge_results(const string &) {
      78                 :          1 :     throw UnimplementedError("MatchSpy not suitable for use with remote searches - merge_results() method unimplemented");
      79                 :            : }
      80                 :            : 
      81                 :            : string
      82                 :          1 : MatchSpy::get_description() const {
      83                 :          1 :     return "Xapian::MatchSpy()";
      84                 :            : }
      85                 :            : 
      86                 :            : XAPIAN_NORETURN(static void unsupported_method());
      87                 :          0 : static void unsupported_method() {
      88                 :          0 :     throw Xapian::InvalidOperationError("Method not supported for this type of termlist");
      89                 :            : }
      90                 :            : 
      91                 :            : /// A termlist iterator over the contents of a ValueCountMatchSpy
      92 [ +  - ][ #  # ]:         56 : class ValueCountTermList : public TermList {
      93                 :            :   private:
      94                 :            :     map<string, Xapian::doccount>::const_iterator it;
      95                 :            :     bool started;
      96                 :            :     Xapian::Internal::RefCntPtr<Xapian::ValueCountMatchSpy::Internal> spy;
      97                 :            :   public:
      98                 :            : 
      99                 :         56 :     ValueCountTermList(ValueCountMatchSpy::Internal * spy_) : spy(spy_) {
     100                 :         56 :         it = spy->values.begin();
     101                 :         56 :         started = false;
     102                 :         56 :     }
     103                 :            : 
     104                 :        242 :     string get_termname() const {
     105                 :            :         Assert(started);
     106                 :            :         Assert(!at_end());
     107                 :        242 :         return it->first;
     108                 :            :     }
     109                 :            : 
     110                 :        242 :     Xapian::doccount get_termfreq() const {
     111                 :            :         Assert(started);
     112                 :            :         Assert(!at_end());
     113                 :        242 :         return it->second;
     114                 :            :     }
     115                 :            : 
     116                 :        298 :     TermList * next() {
     117         [ +  + ]:        298 :         if (!started) {
     118                 :         56 :             started = true;
     119                 :            :         } else {
     120                 :            :             Assert(!at_end());
     121                 :        242 :             ++it;
     122                 :            :         }
     123                 :        298 :         return NULL;
     124                 :            :     }
     125                 :            : 
     126                 :          0 :     TermList * skip_to(const string & term) {
     127 [ #  # ][ #  # ]:          0 :         while (it != spy->values.end() && it->first < term) {
                 [ #  # ]
     128                 :          0 :             ++it;
     129                 :            :         }
     130                 :          0 :         started = true;
     131                 :          0 :         return NULL;
     132                 :            :     }
     133                 :            : 
     134                 :        298 :     bool at_end() const {
     135                 :            :         Assert(started);
     136                 :        298 :         return it == spy->values.end();
     137                 :            :     }
     138                 :            : 
     139                 :          0 :     Xapian::termcount get_approx_size() const { unsupported_method(); return 0; }
     140                 :          0 :     Xapian::termcount get_wdf() const { unsupported_method(); return 0; }
     141                 :          0 :     Xapian::PositionIterator positionlist_begin() const {
     142                 :          0 :         unsupported_method();
     143                 :            :         return Xapian::PositionIterator(NULL);
     144                 :            :     }
     145                 :          0 :     Xapian::termcount positionlist_count() const { unsupported_method(); return 0; }
     146                 :            : };
     147                 :            : 
     148                 :            : /** A string with a corresponding frequency.
     149                 :            :  */
     150                 :      78500 : class StringAndFrequency {
     151                 :            :     std::string str;
     152                 :            :     Xapian::doccount frequency;
     153                 :            :   public:
     154                 :            :     /// Construct a StringAndFrequency object.
     155                 :       6500 :     StringAndFrequency(std::string str_, Xapian::doccount frequency_)
     156                 :       6500 :             : str(str_), frequency(frequency_) {}
     157                 :            : 
     158                 :            :     /// Return the string.
     159                 :      16300 :     std::string get_string() const { return str; }
     160                 :            : 
     161                 :            :     /// Return the frequency.
     162                 :      51420 :     Xapian::doccount get_frequency() const { return frequency; }
     163                 :            : };
     164                 :            : 
     165                 :            : /** Compare two StringAndFrequency objects.
     166                 :            :  *
     167                 :            :  *  The comparison is firstly by frequency (higher is better), then by string
     168                 :            :  *  (earlier lexicographic sort is better).
     169                 :            :  */
     170                 :            : class StringAndFreqCmpByFreq {
     171                 :            :   public:
     172                 :            :     /// Default constructor
     173                 :        820 :     StringAndFreqCmpByFreq() {}
     174                 :            : 
     175                 :            :     /// Return true if a has a higher frequency than b.
     176                 :            :     /// If equal, compare by the str, to provide a stable sort order.
     177                 :      14280 :     bool operator()(const StringAndFrequency &a,
     178                 :            :                     const StringAndFrequency &b) const {
     179         [ +  + ]:      14280 :         if (a.get_frequency() > b.get_frequency()) return true;
     180         [ +  + ]:       9900 :         if (a.get_frequency() < b.get_frequency()) return false;
     181         [ +  + ]:       6620 :         if (a.get_string() > b.get_string()) return false;
     182                 :      14280 :         return true;
     183                 :            :     }
     184                 :            : };
     185                 :            : 
     186                 :            : /// A termlist iterator over a vector of StringAndFrequency objects.
     187 [ +  - ][ #  # ]:       1640 : class StringAndFreqTermList : public TermList {
     188                 :            :   private:
     189                 :            :     vector<StringAndFrequency>::const_iterator it;
     190                 :            :     bool started;
     191                 :            :   public:
     192                 :            :     vector<StringAndFrequency> values;
     193                 :            : 
     194                 :            :     /** init should be called after the values have been set, but before
     195                 :            :      *  iteration begins.
     196                 :            :      */
     197                 :        820 :     void init() {
     198                 :        820 :         it = values.begin();
     199                 :        820 :         started = false;
     200                 :        820 :     }
     201                 :            : 
     202                 :       3060 :     string get_termname() const {
     203                 :            :         Assert(started);
     204                 :            :         Assert(!at_end());
     205                 :       3060 :         return it->get_string();
     206                 :            :     }
     207                 :            : 
     208                 :       3060 :     Xapian::doccount get_termfreq() const {
     209                 :            :         Assert(started);
     210                 :            :         Assert(!at_end());
     211                 :       3060 :         return it->get_frequency();
     212                 :            :     }
     213                 :            : 
     214                 :       3880 :     TermList * next() {
     215         [ +  + ]:       3880 :         if (!started) {
     216                 :        820 :             started = true;
     217                 :            :         } else {
     218                 :            :             Assert(!at_end());
     219                 :       3060 :             ++it;
     220                 :            :         }
     221                 :       3880 :         return NULL;
     222                 :            :     }
     223                 :            : 
     224                 :          0 :     TermList * skip_to(const string & term) {
     225 [ #  # ][ #  # ]:          0 :         while (it != values.end() && it->get_string() < term) {
         [ #  # ][ #  # ]
                 [ #  # ]
     226                 :          0 :             ++it;
     227                 :            :         }
     228                 :          0 :         started = true;
     229                 :          0 :         return NULL;
     230                 :            :     }
     231                 :            : 
     232                 :       3880 :     bool at_end() const {
     233                 :            :         Assert(started);
     234                 :       3880 :         return it == values.end();
     235                 :            :     }
     236                 :            : 
     237                 :          0 :     Xapian::termcount get_approx_size() const { unsupported_method(); return 0; }
     238                 :          0 :     Xapian::termcount get_wdf() const { unsupported_method(); return 0; }
     239                 :          0 :     Xapian::PositionIterator positionlist_begin() const {
     240                 :          0 :         unsupported_method();
     241                 :            :         return Xapian::PositionIterator(NULL);
     242                 :            :     }
     243                 :          0 :     Xapian::termcount positionlist_count() const { unsupported_method(); return 0; }
     244                 :            : };
     245                 :            : 
     246                 :            : /** Get the most frequent items from a map from string to frequency.
     247                 :            :  *
     248                 :            :  *  This takes input such as that in ValueCountMatchSpy::Internal::values and
     249                 :            :  *  returns a vector of the most frequent items in the input.
     250                 :            :  *
     251                 :            :  *  @param result A vector which will be filled with the most frequent
     252                 :            :  *                items, in descending order of frequency.  Items with
     253                 :            :  *                the same frequency will be sorted in ascending
     254                 :            :  *                alphabetical order.
     255                 :            :  *
     256                 :            :  *  @param items The map from string to frequency, from which the most
     257                 :            :  *               frequent items will be selected.
     258                 :            :  *
     259                 :            :  *  @param maxitems The maximum number of items to return.
     260                 :            :  */
     261                 :            : static void
     262                 :        820 : get_most_frequent_items(vector<StringAndFrequency> & result,
     263                 :            :                         const map<string, doccount> & items,
     264                 :            :                         size_t maxitems)
     265                 :            : {
     266                 :        820 :     result.clear();
     267                 :        820 :     result.reserve(maxitems);
     268                 :        820 :     StringAndFreqCmpByFreq cmpfn;
     269                 :        820 :     bool is_heap(false);
     270                 :            : 
     271         [ +  + ]:       7320 :     for (map<string, doccount>::const_iterator i = items.begin();
     272                 :            :          i != items.end(); i++) {
     273                 :            :         Assert(result.size() <= maxitems);
     274                 :       6500 :         result.push_back(StringAndFrequency(i->first, i->second));
     275         [ +  + ]:       6500 :         if (result.size() > maxitems) {
     276                 :            :             // Make the list back into a heap.
     277         [ +  + ]:       1720 :             if (is_heap) {
     278                 :            :                 // Only the new element isn't in the right place.
     279                 :       1340 :                 push_heap(result.begin(), result.end(), cmpfn);
     280                 :            :             } else {
     281                 :            :                 // Need to build heap from scratch.
     282                 :        380 :                 make_heap(result.begin(), result.end(), cmpfn);
     283                 :        380 :                 is_heap = true;
     284                 :            :             }
     285                 :       1720 :             pop_heap(result.begin(), result.end(), cmpfn);
     286                 :       1720 :             result.pop_back();
     287                 :            :         }
     288                 :            :     }
     289                 :            : 
     290         [ +  + ]:        820 :     if (is_heap) {
     291                 :        380 :         sort_heap(result.begin(), result.end(), cmpfn);
     292                 :            :     } else {
     293                 :        440 :         sort(result.begin(), result.end(), cmpfn);
     294                 :            :     }
     295                 :        820 : }
     296                 :            : 
     297                 :            : void
     298                 :       2406 : ValueCountMatchSpy::operator()(const Document &doc, weight) {
     299                 :       2406 :     ++(internal->total);
     300                 :       2406 :     string val(doc.get_value(internal->slot));
     301         [ +  - ]:       2406 :     if (!val.empty()) ++(internal->values[val]);
     302                 :       2406 : }
     303                 :            : 
     304                 :            : TermIterator
     305                 :         56 : ValueCountMatchSpy::values_begin() const
     306                 :            : {
     307                 :         56 :     AutoPtr<ValueCountTermList> termlist(new ValueCountTermList(internal.get()));
     308                 :         56 :     return Xapian::TermIterator(termlist.release());
     309                 :            : }
     310                 :            : 
     311                 :            : TermIterator
     312                 :        820 : ValueCountMatchSpy::top_values_begin(size_t maxvalues) const
     313                 :            : {
     314                 :        820 :     AutoPtr<StringAndFreqTermList> termlist(new StringAndFreqTermList);
     315                 :        820 :     get_most_frequent_items(termlist->values, internal->values, maxvalues);
     316                 :        820 :     termlist->init();
     317                 :        820 :     return Xapian::TermIterator(termlist.release());
     318                 :            : }
     319                 :            : 
     320                 :            : MatchSpy *
     321                 :          0 : ValueCountMatchSpy::clone() const {
     322                 :          0 :     return new ValueCountMatchSpy(internal->slot);
     323                 :            : }
     324                 :            : 
     325                 :            : string
     326                 :       1642 : ValueCountMatchSpy::name() const {
     327                 :       1642 :     return "Xapian::ValueCountMatchSpy";
     328                 :            : }
     329                 :            : 
     330                 :            : string
     331                 :         66 : ValueCountMatchSpy::serialise() const {
     332                 :         66 :     string result;
     333                 :         66 :     result += encode_length(internal->slot);
     334                 :          0 :     return result;
     335                 :            : }
     336                 :            : 
     337                 :            : MatchSpy *
     338                 :         66 : ValueCountMatchSpy::unserialise(const string & s, const Registry &) const
     339                 :            : {
     340                 :         66 :     const char * p = s.data();
     341                 :         66 :     const char * end = p + s.size();
     342                 :            : 
     343                 :         66 :     valueno new_slot = decode_length(&p, end, false);
     344         [ -  + ]:         66 :     if (p != end) {
     345                 :          0 :         throw NetworkError("Junk at end of serialised ValueCountMatchSpy");
     346                 :            :     }
     347                 :            : 
     348                 :         66 :     return new ValueCountMatchSpy(new_slot);
     349                 :            : }
     350                 :            : 
     351                 :            : string
     352                 :         66 : ValueCountMatchSpy::serialise_results() const {
     353                 :            :     LOGCALL(REMOTE, string, "ValueCountMatchSpy::serialise_results", NO_ARGS);
     354                 :         66 :     string result;
     355                 :         66 :     result += encode_length(internal->total);
     356                 :         66 :     result += encode_length(internal->values.size());
     357         [ +  + ]:        432 :     for (map<string, doccount>::const_iterator i = internal->values.begin();
     358                 :            :          i != internal->values.end(); ++i) {
     359                 :        366 :         result += encode_length(i->first.size());
     360                 :        366 :         result += i->first;
     361                 :        366 :         result += encode_length(i->second);
     362                 :            :     }
     363                 :          0 :     RETURN(result);
     364                 :            : }
     365                 :            : 
     366                 :            : void
     367                 :         66 : ValueCountMatchSpy::merge_results(const string & s) {
     368                 :            :     LOGCALL_VOID(REMOTE, "ValueCountMatchSpy::merge_results", s);
     369                 :         66 :     const char * p = s.data();
     370                 :         66 :     const char * end = p + s.size();
     371                 :            : 
     372                 :         66 :     internal->total += decode_length(&p, end, false);
     373                 :            : 
     374                 :         66 :     map<string, doccount>::size_type items = decode_length(&p, end, false);
     375         [ +  + ]:        132 :     while (p != end) {
     376         [ +  + ]:        432 :         while (items != 0) {
     377                 :        366 :             size_t vallen = decode_length(&p, end, true);
     378                 :        366 :             string val(p, vallen);
     379                 :        366 :             p += vallen;
     380                 :        366 :             doccount freq = decode_length(&p, end, false);
     381                 :        366 :             internal->values[val] += freq;
     382                 :        366 :             --items;
     383                 :            :         }
     384                 :            :     }
     385                 :         66 : }
     386                 :            : 
     387                 :            : string
     388                 :          1 : ValueCountMatchSpy::get_description() const {
     389                 :            :     return "Xapian::ValueCountMatchSpy(" + str(internal->total) +
     390                 :          1 :             " docs seen, looking in " + str(internal->values.size()) + " slots)";
     391                 :          0 : }

Generated by: LCOV version 1.8