LCOV - lcov.info - ept/core/xapian.h

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