LTP GCOV extension - code coverage report
Current view: directory - ept/core - xapian.h
Test: lcov.info
Date: 2008-08-14 Instrumented lines: 109
Code covered: 87.2 % Executed lines: 95

       1                 : // -*- C++ -*-
       2                 : #include <xapian.h>
       3                 : #include <ept/core/apt.h>
       4                 : #include <wibble/regexp.h>
       5                 : #include <wibble/sys/pipe.h>
       6                 : #include <wibble/sys/exec.h>
       7                 : 
       8                 : #ifndef EPT_XAPIAN_H
       9                 : #define EPT_XAPIAN_H
      10                 : 
      11                 : namespace ept {
      12                 : namespace core {
      13                 : namespace xapian {
      14                 : 
      15                 : // Allocate value indexes for known values
      16                 : const Xapian::valueno VAL_APT_INSTALLED_SIZE      =  1;
      17                 : const Xapian::valueno VAL_APT_PACKAGE_SIZE        =  2;
      18                 : const Xapian::valueno VAL_POPCON                  = 10;
      19                 : const Xapian::valueno VAL_ITERATING_RATING        = 20;
      20                 : const Xapian::valueno VAL_ITERATING_FUNCTIONALITY = 21;
      21                 : const Xapian::valueno VAL_ITERATING_USABILITY     = 22;
      22                 : const Xapian::valueno VAL_ITERATING_SECURITY      = 23;
      23                 : const Xapian::valueno VAL_ITERATING_PERFORMANCE   = 24;
      24                 : const Xapian::valueno VAL_ITERATING_QUALITY       = 25;
      25                 : const Xapian::valueno VAL_ITERATING_SUPPORT       = 26;
      26                 : const Xapian::valueno VAL_ITERATING_ADOPTION      = 27;
      27                 : 
      28                 : struct TagFilter : public Xapian::ExpandDecider
      29               8 : {
      30            1760 :     virtual bool operator()(const std::string &term) const {
      31            1760 :         return term[0] == 'X' && term[1] == 'T';
      32                 :     }
      33                 : };
      34                 : 
      35              91 : struct List {
      36                 :     char m_enqPlace[sizeof(Xapian::Enquire)];
      37                 :     mutable Xapian::MSet m_matches;
      38                 :     mutable Xapian::MSet::const_iterator m_iter;
      39                 :     mutable int m_pos;
      40                 :     typedef List Type;
      41                 : 
      42                 :     static const size_t chunkSize = 20;
      43                 : 
      44                 :     List head() const {
      45                 :         seek();
      46                 :         return *this;
      47                 :     }
      48                 : 
      49              15 :     Token token() const {
      50              15 :         Token t;
      51              15 :         t._id = m_iter.get_document().get_data();
      52               0 :         return t;
      53                 :     }
      54                 : 
      55                 :     bool operator<( const List &o ) const {
      56                 :         return token() < o.token();
      57                 :     }
      58                 : 
      59              58 :     void seek() const {
      60              58 :         if ( m_matches.size() == chunkSize && m_iter == m_matches.end() ) {
      61               0 :             m_matches = enq().get_mset( m_pos, chunkSize );
      62               0 :             m_iter = m_matches.begin();
      63               0 :             m_pos += chunkSize;
      64                 :         }
      65              58 :     }
      66                 : 
      67              30 :     bool empty() const {
      68              30 :         if ( m_pos == -1 )
      69               0 :             return true;
      70              30 :         seek();
      71              30 :         return m_matches.size() < 30 && m_iter == m_matches.end();
      72                 :     }
      73                 : 
      74              28 :     List tail() const {
      75              28 :         List t = *this;
      76              28 :         t.seek();
      77              28 :         t.m_iter ++;
      78               0 :         return t;
      79                 :     }
      80                 : 
      81               6 :     Xapian::Enquire const &enq() const {
      82               6 :         return *reinterpret_cast< Xapian::Enquire const * >( m_enqPlace );
      83                 :     }
      84                 : 
      85               3 :     List( Xapian::Enquire _enq )
      86               3 :     {
      87               3 :         Xapian::Enquire *e = new (m_enqPlace) Xapian::Enquire( _enq );
      88               3 :         assert_eq( e, &enq() );
      89               6 :         m_matches = enq().get_mset( 0, chunkSize );
      90               3 :         m_iter = m_matches.begin();
      91               3 :         m_pos = chunkSize;
      92               3 :     }
      93                 : 
      94                 :     List() {}
      95                 : };
      96                 : 
      97              13 : struct Query {
      98                 :     Xapian::Database *m_db;
      99                 :     Xapian::Enquire m_enq;
     100                 :     Xapian::Stem m_stem;
     101                 :     typedef std::set< std::string > Terms;
     102                 :     Terms m_include, m_exclude;
     103                 :     int m_cutoff;
     104                 :     bool m_expand;
     105                 : 
     106               5 :     void setQualityCutoff( int c ) {
     107               5 :         m_cutoff = c;
     108               5 :     }
     109                 : 
     110               5 :     void setExpand( bool e ) { m_expand = e; }
     111                 : 
     112               9 :     Query( Xapian::Database &e ) : m_db( &e ), m_enq( e ) {
     113               9 :         m_cutoff = 50;
     114               9 :         m_expand = true;
     115               9 :     }
     116                 : 
     117               5 :     wibble::Tokenizer queryTokenizer( std::string q ) const {
     118               5 :         return wibble::Tokenizer( q, "[A-Za-z0-9_+:-]+", REG_EXTENDED );
     119                 :     }
     120                 : 
     121                 :     template< typename Out >
     122               5 :     void tokenizeQuery( std::string q, Out o ) const
     123                 :     {
     124               5 :         wibble::Tokenizer tok = queryTokenizer( q );
     125              15 :         for (wibble::Tokenizer::const_iterator i = tok.begin(); i != tok.end(); ++i )
     126                 :         {
     127              10 :             if ( (*i).find( "::" ) != std::string::npos ) { // assume tag
     128               0 :                 *o++ = ("XT" + *i);
     129                 :             } else {
     130              10 :                 std::string t = wibble::str::tolower(*i);
     131              10 :                 std::string s = m_stem(t);
     132              10 :                 *o++ = t;
     133              10 :                 if (s != t)
     134               5 :                     *o++ = ("Z" + s);
     135                 :             }
     136                 :         }
     137               5 :     }
     138                 : 
     139                 :     template< typename Out >
     140               4 :     void expand( Out o ) const
     141                 :     {
     142               4 :         Xapian::RSet rset;
     143                 :         // Get the top 5 results as 'good ones' to compute the search expansion
     144               4 :         Xapian::MSet mset = m_enq.get_mset(0, 5);
     145              24 :         for ( Xapian::MSet::iterator i = mset.begin(); i != mset.end(); ++i )
     146              24 :             rset.add_document(i);
     147                 :         // Get the expanded set, only expanding the query with tag names
     148               4 :         TagFilter tagf;
     149               4 :         Xapian::ESet eset = m_enq.get_eset(5, rset, &tagf);
     150               4 :         for ( Xapian::ESetIterator i = eset.begin(); i != eset.end(); ++i )
     151               4 :             *o++ = *i;
     152               4 :     }
     153                 : 
     154               8 :     void updateEnquire() {
     155                 :         // set up query now
     156                 :         Xapian::Query inc( Xapian::Query::OP_OR,
     157                 :                            m_include.begin(),
     158               8 :                            m_include.end() ),
     159                 :             exc( Xapian::Query::OP_OR,
     160                 :                  m_exclude.begin(),
     161               8 :                  m_exclude.end() ),
     162               8 :             query( Xapian::Query::OP_AND_NOT, inc, exc );
     163                 : 
     164               8 :         m_enq.set_query( query );
     165                 : 
     166               8 :         if ( m_expand ) {
     167               4 :             m_expand = false;
     168               4 :             expand( std::inserter( m_include, m_include.begin() ) );
     169               4 :             updateEnquire();
     170               4 :             m_expand = true;
     171               4 :             return;
     172                 :         }
     173                 : 
     174               4 :         Xapian::MSet first = m_enq.get_mset(0, 1, 0, 0, 0);
     175               4 :         Xapian::MSetIterator ifirst = first.begin();
     176               4 :         if ( ifirst != first.end() ) {
     177               4 :             Xapian::percent cutoff = ifirst.get_percent() * m_cutoff / 100;
     178               4 :             m_enq.set_cutoff(cutoff);
     179               4 :         }
     180                 :     }
     181                 : 
     182               3 :     List results() {
     183               3 :         updateEnquire();
     184               3 :         return List( m_enq );
     185                 :     }
     186                 : 
     187                 :     std::map< std::string, int > relevantTags( int n = 30 ) {
     188                 :         updateEnquire();
     189                 :         std::map< std::string, int > relev;
     190                 :         Xapian::RSet rset;
     191                 :         Xapian::MSet mset = m_enq.get_mset(0, 100);
     192                 :         for ( Xapian::MSet::iterator i = mset.begin(); i != mset.end(); ++i )
     193                 :             rset.add_document(i);
     194                 :         // Get the expanded set, only expanding the query with tag names
     195                 :         TagFilter tagf;
     196                 :         Xapian::ESet eset = m_enq.get_eset(n, rset, &tagf);
     197                 :         for ( Xapian::ESetIterator i = eset.begin(); i != eset.end(); ++i )
     198                 :             relev.insert( relev.begin(),
     199                 :                           std::make_pair(
     200                 :                               std::string( *i, 2, std::string::npos ),
     201                 :                               i.get_weight() ) );
     202                 :         return relev;
     203                 :     }
     204                 : 
     205               5 :     void addTerms( std::string t, bool partial = false, bool exclude = false ) {
     206               5 :         Terms &to = exclude ? m_exclude : m_include;
     207               5 :         std::vector< std::string > tok;
     208               5 :         tokenizeQuery( t, std::back_inserter( tok ) );
     209               5 :         if ( partial ) {
     210               0 :             if ( tok.back().size() == 1 ) {
     211               0 :                 tok.pop_back();
     212                 :             } else {
     213                 :                 std::copy(
     214                 :                     m_db->allterms_begin( tok.back() ),
     215                 :                     m_db->allterms_end( tok.back() ),
     216               0 :                     std::back_inserter( tok ) );
     217                 :             }
     218                 :         }
     219               5 :         std::copy( tok.begin(), tok.end(), std::inserter( to, to.begin() ) );
     220               5 :     }
     221                 : 
     222                 :     void addTerms( const Terms &t, bool exclude = false ) {
     223                 :         Terms &to = exclude ? m_exclude : m_include;
     224                 :         std::copy( t.begin(), t.end(), std::inserter( to, to.begin() ) );
     225                 :     }
     226                 : 
     227                 : };
     228                 : 
     229                 : struct Source
     230               4 : {
     231                 : protected:
     232                 :     mutable Xapian::Database m_db;
     233                 :     Xapian::Stem m_stem;
     234                 :     mutable bool m_opened;
     235                 : 
     236                 :     /// Return a lowercased copy of the string
     237                 :     static std::string toLower(const std::string& str);
     238                 : 
     239                 :     /**
     240                 :      * Add normalised tokens computed from the string to the document doc.
     241                 :      *
     242                 :      * pos is used as a sequence generator for entering the token position in
     243                 :      * the document.
     244                 :      */
     245                 :     void normalize_and_add(Xapian::Document& doc, const std::string& term,
     246                 :                            int& pos) const;
     247                 : 
     248                 : public:
     249                 :     Source();
     250                 : 
     251                 :     /// Access the Xapian database
     252               9 :     Xapian::Database& db() {
     253               9 :         open();
     254               9 :         return m_db;
     255                 :     }
     256                 : 
     257                 :     /// Access the Xapian database
     258               0 :     const Xapian::Database& db() const {
     259               0 :         open();
     260               0 :         return m_db;
     261                 :     }
     262                 : 
     263                 :     void open() const;
     264                 :     void invalidate() {
     265                 :         m_db = Xapian::Database();
     266                 :         m_opened = false;
     267                 :     }
     268                 : 
     269                 :     /// Timestamp of when the Xapian database was last updated
     270                 :     time_t timestamp() const;
     271                 : 
     272                 :     void updateLeniently( AptDatabase &apt, OpProgress *op = 0 ) {
     273                 :         if (apt.timestamp() - timestamp() > 86400 * 8) // a little over a week
     274                 :             update( op );
     275                 :     }
     276                 : 
     277                 :     void update( OpProgress *op = 0 ) {
     278                 :         if ( !op )
     279                 :             op = new OpProgress();
     280                 : 
     281                 :         wibble::exception::AddContext _ctx( "Rebuilding Xapian database." );
     282                 :         int outfd;
     283                 :         std::string op_str;
     284                 : 
     285                 :         wibble::sys::Exec ex( "update-apt-xapian-index" );
     286                 :         ex.args.push_back( "--batch-mode" );
     287                 :         ex.searchInPath = true;
     288                 :         ex.forkAndRedirect( 0, &outfd, 0 );
     289                 : 
     290                 :         wibble::sys::Pipe monit( outfd );
     291                 :         while ( !monit.eof() ) {
     292                 :             std::string line = monit.nextLine();
     293                 :             if ( line.empty() ) {
     294                 :                 usleep( 100000 );
     295                 :                 continue;
     296                 :             }
     297                 :             std::cerr << "got : " << line << std::endl;
     298                 :             if ( wibble::str::startsWith( line, "begin: " ) ) {
     299                 :                 op_str = std::string( line, 7, std::string::npos );
     300                 :                 op->OverallProgress( 0, 100, 100, op_str );
     301                 :                     
     302                 :             } else if ( wibble::str::startsWith( line, "done: " ) ) {
     303                 :                 op->Done();
     304                 :             } else if ( wibble::str::startsWith( line, "progress: " ) ) {
     305                 :                 wibble::ERegexp re( "progress: ([0-9]+)/([0-9]+)", 3 );
     306                 :                 if ( re.match( line ) ) {
     307                 :                     assert_eq( re[2], "100" );
     308                 :                     op->OverallProgress( atoi( re[1].c_str() ), 100, 100, op_str );
     309                 :                 }
     310                 :             }
     311                 :         }
     312                 :         ex.waitForSuccess();
     313                 :         invalidate();
     314                 :     }
     315                 : 
     316                 :     /// Returns true if the index has data
     317                 :     bool hasData() const { return timestamp() > 0; }
     318                 : 
     319                 :     Query query( const std::string &s,
     320                 :                  bool expand = true,
     321               5 :                  int qualityCutoff = 50 )
     322                 :     {
     323               5 :         Query q( db() );
     324               5 :         q.setQualityCutoff( qualityCutoff );
     325               5 :         q.setExpand( expand );
     326               5 :         q.addTerms( s );
     327               0 :         return q;
     328                 :     }
     329                 : 
     330                 :     Query partialQuery( const std::string &s ) {
     331                 :         Query q( db() );
     332                 :         q.addTerms( s, true ); // partial
     333                 :         return q;
     334                 :     }
     335                 : 
     336                 :     /// Returns true if the index is older than the Apt database information
     337                 :     // bool needsRebuild(apt::Apt& apt);
     338                 : 
     339                 :     Xapian::docid docidByName(const std::string& pkgname) const;
     340                 : 
     341                 :     /**
     342                 :      * Tokenize the string and build an OR query with the resulting keywords
     343                 :      */
     344                 :     Xapian::Query makeORQuery(const std::string& keywords) const;
     345                 : 
     346                 :     /**
     347                 :      * Tokenize the string and build an OR query with the resulting keywords.
     348                 :      *
     349                 :      * The last token in keywords is considered to be typed only partially, to
     350                 :      * implement proper search-as-you-type.
     351                 :      */
     352                 :     Xapian::Query makePartialORQuery(const std::string& keywords) const;
     353                 : 
     354                 :     /**
     355                 :      * Build a query with the given keywords, specified as iterators of strings
     356                 :      */
     357                 :     template<typename ITER>
     358                 :     Xapian::Query makeORQuery(const ITER& begin, const ITER& end) const
     359                 :     {
     360                 :         return Xapian::Query(Xapian::Query::OP_OR, begin, end);
     361                 :     }
     362                 : 
     363                 :     /// Return a list of tag-based terms that can be used to expand an OR query
     364                 :     std::vector<std::string> expand(Xapian::Enquire& enq) const;
     365                 : 
     366                 : //      std::vector<std::string> similar(const std::string& pkg);
     367                 : 
     368                 :     /**
     369                 :      * Create a query to look for packages similar to the given one
     370                 :      */
     371                 :     Xapian::Query makeRelatedQuery(const std::string& pkgname) const;
     372                 : 
     373                 :     /**
     374                 :      * Get the integer value for
     375                 :      */
     376                 :     double getDoubleValue(const std::string& pkgname,
     377                 :                           Xapian::valueno val_id) const;
     378                 : 
     379                 :     /**
     380                 :      * Get the integer value for
     381                 :      */
     382                 :     int getIntValue(const std::string& pkgname, Xapian::valueno val_id) const;
     383                 : };
     384                 : 
     385                 : }
     386                 : }
     387                 : }
     388                 : 
     389                 : #endif

Generated by: LTP GCOV extension version 1.6