api/omdatabase.cc

Go to the documentation of this file.
00001 /* omdatabase.cc: External interface for running queries
00002  *
00003  * Copyright 1999,2000,2001 BrightStation PLC
00004  * Copyright 2001,2002 Ananova Ltd
00005  * Copyright 2002,2003,2004,2005,2006,2007,2008 Olly Betts
00006  * Copyright 2006,2008 Lemur Consulting Ltd
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License as
00010  * published by the Free Software Foundation; either version 2 of the
00011  * License, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00021  * USA
00022  */
00023 
00024 #include <config.h>
00025 
00026 #include "autoptr.h"
00027 
00028 #include <xapian/error.h>
00029 #include <xapian/positioniterator.h>
00030 #include <xapian/postingiterator.h>
00031 #include <xapian/termiterator.h>
00032 #include <xapian/unicode.h>
00033 
00034 #include "omassert.h"
00035 #include "omdebug.h"
00036 #include "../backends/multi/multi_postlist.h"
00037 #include "../backends/multi/multi_termlist.h"
00038 #include "alltermslist.h"
00039 #include "multialltermslist.h"
00040 #include "database.h"
00041 #include "editdistance.h"
00042 #include "ortermlist.h"
00043 #include "noreturn.h"
00044 
00045 #include <stdlib.h> // For abs().
00046 
00047 #include <cstring>
00048 #include <vector>
00049 
00050 using namespace std;
00051 
00052 XAPIAN_NORETURN(static void no_subdatabases());
00053 static void no_subdatabases()
00054 {
00055     throw Xapian::DocNotFoundError("No subdatabases");
00056 }
00057 
00058 namespace Xapian {
00059 
00060 Database::Database()
00061 {
00062     DEBUGAPICALL(void, "Database::Database", "");
00063 }
00064 
00065 Database::Database(Database::Internal *internal_)
00066 {
00067     DEBUGAPICALL(void, "Database::Database", "Database::Internal");
00068     Xapian::Internal::RefCntPtr<Database::Internal> newi(internal_);
00069     internal.push_back(newi);
00070 }
00071 
00072 Database::Database(const Database &other)
00073 {
00074     DEBUGAPICALL(void, "Database::Database", "Database");
00075     internal = other.internal;
00076 }
00077 
00078 void
00079 Database::operator=(const Database &other)
00080 {
00081     DEBUGAPICALL(void, "Database::operator=", "Database");
00082     if (this == &other) {
00083         DEBUGLINE(API, "Database assigned to itself");
00084         return;
00085     }
00086 
00087     internal = other.internal;
00088 }
00089 
00090 Database::~Database()
00091 {
00092     DEBUGAPICALL(void, "Database::~Database", "");
00093 }
00094 
00095 void
00096 Database::reopen()
00097 {
00098     DEBUGAPICALL(void, "Database::reopen", "");
00099     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::iterator i;
00100     for (i = internal.begin(); i != internal.end(); ++i) {
00101         (*i)->reopen();
00102     }
00103 }
00104 
00105 void
00106 Database::add_database(const Database & database)
00107 {
00108     DEBUGAPICALL(void, "Database::add_database", "Database");
00109     if (this == &database) {
00110         DEBUGLINE(API, "Database added to itself");
00111         throw Xapian::InvalidArgumentError("Can't add an Database to itself");
00112         return;
00113     }
00114     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00115     for (i = database.internal.begin(); i != database.internal.end(); ++i) {
00116         internal.push_back(*i);
00117     }
00118 }
00119 
00120 PostingIterator
00121 Database::postlist_begin(const string &tname) const
00122 {
00123     DEBUGAPICALL(PostingIterator, "Database::postlist_begin", tname);
00124 
00125     // Don't bother checking that the term exists first.  If it does, we
00126     // just end up doing more work, and if it doesn't, we save very little
00127     // work.
00128 
00129     // Handle the common case of a single database specially.
00130     if (internal.size() == 1)
00131         RETURN(PostingIterator(internal[0]->open_post_list(tname)));
00132 
00133     if (rare(internal.size() == 0))
00134         RETURN(PostingIterator(NULL));
00135 
00136     vector<LeafPostList *> pls;
00137     try {
00138         vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00139         for (i = internal.begin(); i != internal.end(); ++i) {
00140             pls.push_back((*i)->open_post_list(tname));
00141             pls.back()->next();
00142         }
00143         Assert(pls.begin() != pls.end());
00144     } catch (...) {
00145         vector<LeafPostList *>::iterator i;
00146         for (i = pls.begin(); i != pls.end(); ++i) {
00147             delete *i;
00148             *i = 0;
00149         }
00150         throw;
00151     }
00152 
00153     RETURN(PostingIterator(new MultiPostList(pls, *this)));
00154 }
00155 
00156 TermIterator
00157 Database::termlist_begin(Xapian::docid did) const
00158 {
00159     DEBUGAPICALL(TermIterator, "Database::termlist_begin", did);
00160     if (did == 0) throw InvalidArgumentError("Document ID 0 is invalid");
00161 
00162     unsigned int multiplier = internal.size();
00163     if (rare(multiplier == 0))
00164         no_subdatabases();
00165     TermList *tl;
00166     if (multiplier == 1) {
00167         // There's no need for the MultiTermList wrapper in the common case
00168         // where we're only dealing with a single database.
00169         tl = internal[0]->open_term_list(did);
00170     } else {
00171         Assert(multiplier != 0);
00172         Xapian::doccount n = (did - 1) % multiplier; // which actual database
00173         Xapian::docid m = (did - 1) / multiplier + 1; // real docid in that database
00174 
00175         tl = new MultiTermList(internal[n]->open_term_list(m), *this, n);
00176     }
00177     RETURN(TermIterator(tl));
00178 }
00179 
00180 TermIterator
00181 Database::allterms_begin() const
00182 {
00183     return allterms_begin("");
00184 }
00185 
00186 TermIterator
00187 Database::allterms_begin(const std::string & prefix) const
00188 {
00189     DEBUGAPICALL(TermIterator, "Database::allterms_begin", "");
00190     TermList * tl;
00191     if (rare(internal.size() == 0)) {
00192         tl = NULL;
00193     } else if (internal.size() == 1) {
00194         tl = internal[0]->open_allterms(prefix);
00195     } else {
00196         tl = new MultiAllTermsList(internal, prefix);
00197     }
00198     RETURN(TermIterator(tl));
00199 }
00200 
00201 bool
00202 Database::has_positions() const
00203 {
00204     DEBUGAPICALL(bool, "Database::has_positions", "");
00205     // If any sub-database has positions, the combined database does.
00206     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00207     for (i = internal.begin(); i != internal.end(); ++i) {
00208         if ((*i)->has_positions()) RETURN(true);
00209     }
00210     RETURN(false);
00211 }
00212 
00213 PositionIterator
00214 Database::positionlist_begin(Xapian::docid did, const string &tname) const
00215 {
00216     DEBUGAPICALL(PositionIterator, "Database::positionlist_begin",
00217                  did << ", " << tname);
00218     if (tname.empty())
00219         throw InvalidArgumentError("Zero length terms are invalid");
00220     if (did == 0) throw InvalidArgumentError("Document ID 0 is invalid");
00221 
00222     unsigned int multiplier = internal.size();
00223     if (rare(multiplier == 0))
00224         no_subdatabases();
00225     Xapian::doccount n = (did - 1) % multiplier; // which actual database
00226     Xapian::docid m = (did - 1) / multiplier + 1; // real docid in that database
00227     RETURN(PositionIterator(internal[n]->open_position_list(m, tname)));
00228 }
00229 
00230 Xapian::doccount
00231 Database::get_doccount() const
00232 {
00233     DEBUGAPICALL(Xapian::doccount, "Database::get_doccount", "");
00234     Xapian::doccount docs = 0;
00235     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00236     for (i = internal.begin(); i != internal.end(); ++i) {
00237         docs += (*i)->get_doccount();
00238     }
00239     RETURN(docs);
00240 }
00241 
00242 Xapian::docid
00243 Database::get_lastdocid() const
00244 {
00245     DEBUGAPICALL(Xapian::docid, "Database::get_lastdocid", "");
00246     Xapian::docid did = 0;
00247 
00248     unsigned int multiplier = internal.size();
00249     for (Xapian::doccount i = 0; i < multiplier; ++i) {
00250         Xapian::docid did_i = internal[i]->get_lastdocid();
00251         if (did_i) did = std::max(did, (did_i - 1) * multiplier + i + 1);
00252     }
00253     RETURN(did);
00254 }
00255 
00256 Xapian::doclength
00257 Database::get_avlength() const
00258 {
00259     DEBUGAPICALL(Xapian::doclength, "Database::get_avlength", "");
00260     Xapian::doccount docs = 0;
00261     Xapian::doclength totlen = 0;
00262 
00263     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00264     for (i = internal.begin(); i != internal.end(); ++i) {
00265         Xapian::doccount db_doccount = (*i)->get_doccount();
00266         docs += db_doccount;
00267         totlen += (*i)->get_avlength() * db_doccount;
00268     }
00269     DEBUGLINE(UNKNOWN, "get_avlength() = " << totlen << " / " << docs <<
00270               " (from " << internal.size() << " dbs)");
00271 
00272     if (docs == 0) RETURN(0.0);
00273     RETURN(totlen / docs);
00274 }
00275 
00276 Xapian::doccount
00277 Database::get_termfreq(const string & tname) const
00278 {
00279     DEBUGAPICALL(Xapian::doccount, "Database::get_termfreq", tname);
00280     if (tname.empty()) RETURN(get_doccount());
00281 
00282     Xapian::doccount tf = 0;
00283     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00284     for (i = internal.begin(); i != internal.end(); i++) {
00285         tf += (*i)->get_termfreq(tname);
00286     }
00287     RETURN(tf);
00288 }
00289 
00290 Xapian::termcount
00291 Database::get_collection_freq(const string & tname) const
00292 {
00293     DEBUGAPICALL(Xapian::termcount, "Database::get_collection_freq", tname);
00294     if (tname.empty()) RETURN(get_doccount());
00295 
00296     Xapian::termcount cf = 0;
00297     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00298     for (i = internal.begin(); i != internal.end(); i++) {
00299         cf += (*i)->get_collection_freq(tname);
00300     }
00301     RETURN(cf);
00302 }
00303 
00304 Xapian::doclength
00305 Database::get_doclength(Xapian::docid did) const
00306 {
00307     DEBUGAPICALL(Xapian::doclength, "Database::get_doclength", did);
00308     if (did == 0) throw InvalidArgumentError("Document ID 0 is invalid");
00309 
00310     unsigned int multiplier = internal.size();
00311     if (rare(multiplier == 0))
00312         no_subdatabases();
00313     Xapian::doccount n = (did - 1) % multiplier; // which actual database
00314     Xapian::docid m = (did - 1) / multiplier + 1; // real docid in that database
00315     RETURN(internal[n]->get_doclength(m));
00316 }
00317 
00318 Document
00319 Database::get_document(Xapian::docid did) const
00320 {
00321     DEBUGAPICALL(Document, "Database::get_document", did);
00322     if (did == 0) throw InvalidArgumentError("Document ID 0 is invalid");
00323 
00324     unsigned int multiplier = internal.size();
00325     if (rare(multiplier == 0))
00326         no_subdatabases();
00327     Xapian::doccount n = (did - 1) % multiplier; // which actual database
00328     Xapian::docid m = (did - 1) / multiplier + 1; // real docid in that database
00329 
00330     // Open non-lazily so we throw DocNotFoundError if the doc doesn't exist.
00331     RETURN(Document(internal[n]->open_document(m, false)));
00332 }
00333 
00334 bool
00335 Database::term_exists(const string & tname) const
00336 {
00337     DEBUGAPICALL(bool, "Database::term_exists", tname);
00338     if (tname.empty()) {
00339         RETURN(get_doccount() != 0);
00340     }
00341     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00342     for (i = internal.begin(); i != internal.end(); ++i) {
00343         if ((*i)->term_exists(tname)) RETURN(true);
00344     }
00345     RETURN(false);
00346 }
00347 
00348 void
00349 Database::keep_alive()
00350 {
00351     DEBUGAPICALL(void, "Database::keep_alive", "");
00352     vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00353     for (i = internal.begin(); i != internal.end(); ++i) {
00354         (*i)->keep_alive();
00355     }
00356 }
00357 
00358 string
00359 Database::get_description() const
00360 {
00362     return "Database()";
00363 }
00364 
00365 // We sum the character frequency histogram absolute differences to compute a
00366 // lower bound on the edit distance.  Rather than counting each Unicode code
00367 // point uniquely, we use an array with VEC_SIZE elements and tally code points
00368 // modulo VEC_SIZE which can only reduce the bound we calculate.
00369 //
00370 // There will be a trade-off between how good the bound is and how large and
00371 // array is used (a larger array takes more time to clear and sum over).  The
00372 // value 64 is somewhat arbitrary - it works as well as 128 for the testsuite
00373 // but that may not reflect real world performance.  FIXME: profile and tune.
00374 
00375 #define VEC_SIZE 64
00376 
00377 static int
00378 freq_edit_lower_bound(const vector<unsigned> & a, const vector<unsigned> & b)
00379 {
00380     int vec[VEC_SIZE];
00381     memset(vec, 0, sizeof(vec));
00382     vector<unsigned>::const_iterator i;
00383     for (i = a.begin(); i != a.end(); ++i) {
00384         ++vec[(*i) % VEC_SIZE];
00385     }
00386     for (i = b.begin(); i != b.end(); ++i) {
00387         --vec[(*i) % VEC_SIZE];
00388     }
00389     unsigned int total = 0;
00390     for (size_t j = 0; j < VEC_SIZE; ++j) {
00391         total += abs(vec[j]);
00392     }
00393     // Each insertion or deletion adds at most 1 to total.  Each transposition
00394     // doesn't change it at all.  But each substitution can change it by 2 so
00395     // we need to divide it by 2.  Rounding up is OK, since the odd change must
00396     // be due to an actual edit.
00397     return (total + 1) / 2;
00398 }
00399 
00400 // Word must have a trigram score at least this close to the best score seen
00401 // so far.
00402 #define TRIGRAM_SCORE_THRESHOLD 2
00403 
00404 string
00405 Database::get_spelling_suggestion(const string &word,
00406                                   unsigned max_edit_distance) const
00407 {
00408     DEBUGAPICALL(string, "Database::get_spelling_suggestion",
00409                  word << ", " << max_edit_distance);
00410     if (word.size() <= 1) return string();
00411     AutoPtr<TermList> merger;
00412     for (size_t i = 0; i < internal.size(); ++i) {
00413         TermList * tl = internal[i]->open_spelling_termlist(word);
00414         DEBUGLINE(SPELLING, "Sub db " << i << " tl = " << (void*)tl);
00415         if (tl) {
00416             if (merger.get()) {
00417                 merger = new OrTermList(merger.release(), tl);
00418             } else {
00419                 merger = tl;
00420             }
00421         }
00422     }
00423     if (!merger.get()) RETURN(string());
00424 
00425     // Convert word to UTF-32.
00426 #ifdef __SUNPRO_CC
00427     vector<unsigned> utf32_word;
00428     for (Utf8Iterator sunpro_it(word); sunpro_it != Utf8Iterator(); ++sunpro_it) {
00429         utf32_word.push_back(*sunpro_it);
00430     }
00431 #else
00432     // Extra brackets needed to avoid this being misparsed as a function
00433     // prototype.
00434     vector<unsigned> utf32_word((Utf8Iterator(word)), Utf8Iterator());
00435 #endif
00436 
00437     vector<unsigned> utf32_term;
00438 
00439     Xapian::termcount best = 1;
00440     string result;
00441     int edist_best = max_edit_distance;
00442     Xapian::doccount freq_best = 0;
00443     while (true) {
00444         TermList *ret = merger->next();
00445         if (ret) merger = ret;
00446 
00447         if (merger->at_end()) break;
00448 
00449         string term = merger->get_termname();
00450         Xapian::termcount score = merger->get_wdf();
00451 
00452         DEBUGLINE(SPELLING, "Term \"" << term << "\" ngram score " << score);
00453         if (score + TRIGRAM_SCORE_THRESHOLD >= best) {
00454             if (score > best) best = score;
00455 
00456             // There's no point considering a word where the difference
00457             // in length is greater than the smallest number of edits we've
00458             // found so far.
00459 
00460             // First check the length of the encoded UTF-8 version of term.
00461             // Each UTF-32 character is 1-4 bytes in UTF-8.
00462             if (abs((long)term.size() - (long)word.size()) > edist_best * 4) {
00463                 DEBUGLINE(SPELLING, "Lengths much too different");
00464                 continue;
00465             }
00466 
00467             // Now convert to UTF-32, and compare the true lengths more
00468             // strictly.
00469             utf32_term.assign(Utf8Iterator(term), Utf8Iterator());
00470 
00471             if (abs(long(utf32_term.size()) - long(utf32_word.size()))
00472                     > edist_best) {
00473                 DEBUGLINE(SPELLING, "Lengths too different");
00474                 continue;
00475             }
00476 
00477             if (freq_edit_lower_bound(utf32_term, utf32_word) > edist_best) {
00478                 DEBUGLINE(SPELLING, "Rejected by character frequency test");
00479                 continue;
00480             }
00481 
00482             int edist = edit_distance_unsigned(&utf32_term[0],
00483                                                int(utf32_term.size()),
00484                                                &utf32_word[0],
00485                                                int(utf32_word.size()),
00486                                                edist_best);
00487             DEBUGLINE(SPELLING, "Edit distance " << edist);
00488             // If we have an exact match, return an empty string since there's
00489             // no correction required.
00490             if (edist == 0) RETURN(string());
00491 
00492             if (edist <= edist_best) {
00493                 Xapian::doccount freq = 0;
00494                 for (size_t j = 0; j < internal.size(); ++j)
00495                     freq += internal[j]->get_spelling_frequency(term);
00496 
00497                 DEBUGLINE(SPELLING, "Freq " << freq << " best " << freq_best);
00498                 if (edist < edist_best || freq > freq_best) {
00499                     DEBUGLINE(SPELLING, "Best so far: \"" << term <<
00500                                         "\" edist " << edist << " freq " <<
00501                                         freq);
00502                     result = term;
00503                     edist_best = edist;
00504                     freq_best = freq;
00505                 }
00506             }
00507         }
00508     }
00509     RETURN(result);
00510 }
00511 
00512 TermIterator
00513 Database::spellings_begin() const
00514 {
00515     DEBUGAPICALL(TermIterator, "Database::spellings_begin", "");
00516     AutoPtr<TermList> merger;
00517     for (size_t i = 0; i < internal.size(); ++i) {
00518         TermList * tl = internal[i]->open_spelling_wordlist();
00519         if (tl) {
00520             if (merger.get()) {
00521                 merger = new FreqAdderOrTermList(merger.release(), tl);
00522             } else {
00523                 merger = tl;
00524             }
00525         }
00526     }
00527     RETURN(TermIterator(merger.release()));
00528 }
00529 
00530 TermIterator
00531 Database::synonyms_begin(const std::string &term) const
00532 {
00533     DEBUGAPICALL(TermIterator, "Database::synonyms_begin", term);
00534     AutoPtr<TermList> merger;
00535     for (size_t i = 0; i < internal.size(); ++i) {
00536         TermList * tl = internal[i]->open_synonym_termlist(term);
00537         if (tl) {
00538             if (merger.get()) {
00539                 merger = new OrTermList(merger.release(), tl);
00540             } else {
00541                 merger = tl;
00542             }
00543         }
00544     }
00545     RETURN(TermIterator(merger.release()));
00546 }
00547 
00548 TermIterator
00549 Database::synonym_keys_begin(const std::string &prefix) const
00550 {
00551     DEBUGAPICALL(TermIterator, "Database::synonyms_keys_begin", prefix);
00552     AutoPtr<TermList> merger;
00553     for (size_t i = 0; i < internal.size(); ++i) {
00554         TermList * tl = internal[i]->open_synonym_keylist(prefix);
00555         if (tl) {
00556             if (merger.get()) {
00557                 merger = new OrTermList(merger.release(), tl);
00558             } else {
00559                 merger = tl;
00560             }
00561         }
00562     }
00563     RETURN(TermIterator(merger.release()));
00564 }
00565 
00566 string
00567 Database::get_metadata(const string & key) const
00568 {
00569     DEBUGAPICALL(string, "Database::get_metadata", key);
00570     if (key.empty())
00571         throw InvalidArgumentError("Empty metadata keys are invalid");
00572     if (internal.empty()) RETURN(std::string());
00573     RETURN(internal[0]->get_metadata(key));
00574 }
00575 
00576 Xapian::TermIterator
00577 Database::metadata_keys_begin(const std::string &prefix) const
00578 {
00579     DEBUGAPICALL(Xapian::TermIterator, "Database::metadata_keys_begin", "");
00580     if (internal.empty()) RETURN(TermIterator(NULL));
00581     RETURN(TermIterator(internal[0]->open_metadata_keylist(prefix)));
00582 }
00583 
00585 
00586 WritableDatabase::WritableDatabase() : Database()
00587 {
00588     DEBUGAPICALL(void, "WritableDatabase::WritableDatabase", "");
00589 }
00590 
00591 WritableDatabase::WritableDatabase(Database::Internal *internal_)
00592         : Database(internal_)
00593 {
00594     DEBUGAPICALL(void, "WritableDatabase::WritableDatabase",
00595                  "Database::Internal");
00596 }
00597 
00598 WritableDatabase::WritableDatabase(const WritableDatabase &other)
00599         : Database(other)
00600 {
00601     DEBUGAPICALL(void, "WritableDatabase::WritableDatabase", "WritableDatabase");
00602 }
00603 
00604 void
00605 WritableDatabase::operator=(const WritableDatabase &other)
00606 {
00607     DEBUGAPICALL(void, "WritableDatabase::operator=", "WritableDatabase");
00608     Database::operator=(other);
00609 }
00610 
00611 WritableDatabase::~WritableDatabase()
00612 {
00613     DEBUGAPICALL(void, "WritableDatabase::~WritableDatabase", "");
00614 }
00615 
00616 XAPIAN_NORETURN(static void only_one_subdatabase_allowed());
00617 static void only_one_subdatabase_allowed()
00618 {
00619     throw Xapian::InvalidOperationError("WritableDatabase needs exactly one subdatabase");
00620 }
00621 
00622 void
00623 WritableDatabase::flush()
00624 {
00625     DEBUGAPICALL(void, "WritableDatabase::flush", "");
00626     if (internal.size() != 1) only_one_subdatabase_allowed();
00627     internal[0]->flush();
00628 }
00629 
00630 void
00631 WritableDatabase::begin_transaction(bool flushed)
00632 {
00633     DEBUGAPICALL(void, "WritableDatabase::begin_transaction", "");
00634     if (internal.size() != 1) only_one_subdatabase_allowed();
00635     internal[0]->begin_transaction(flushed);
00636 }
00637 
00638 void
00639 WritableDatabase::commit_transaction()
00640 {
00641     DEBUGAPICALL(void, "WritableDatabase::commit_transaction", "");
00642     if (internal.size() != 1) only_one_subdatabase_allowed();
00643     internal[0]->commit_transaction();
00644 }
00645 
00646 void
00647 WritableDatabase::cancel_transaction()
00648 {
00649     DEBUGAPICALL(void, "WritableDatabase::cancel_transaction", "");
00650     if (internal.size() != 1) only_one_subdatabase_allowed();
00651     internal[0]->cancel_transaction();
00652 }
00653 
00654 
00655 Xapian::docid
00656 WritableDatabase::add_document(const Document & document)
00657 {
00658     DEBUGAPICALL(Xapian::docid, "WritableDatabase::add_document", document);
00659     if (internal.size() != 1) only_one_subdatabase_allowed();
00660     RETURN(internal[0]->add_document(document));
00661 }
00662 
00663 void
00664 WritableDatabase::delete_document(Xapian::docid did)
00665 {
00666     DEBUGAPICALL(void, "WritableDatabase::delete_document", did);
00667     if (internal.size() != 1) only_one_subdatabase_allowed();
00668     if (did == 0) throw InvalidArgumentError("Document ID 0 is invalid");
00669     internal[0]->delete_document(did);
00670 }
00671 
00672 void
00673 WritableDatabase::delete_document(const std::string & unique_term)
00674 {
00675     DEBUGAPICALL(void, "WritableDatabase::delete_document", unique_term);
00676     if (internal.size() != 1) only_one_subdatabase_allowed();
00677     if (unique_term.empty())
00678         throw InvalidArgumentError("Empty termnames are invalid");
00679     internal[0]->delete_document(unique_term);
00680 }
00681 
00682 void
00683 WritableDatabase::replace_document(Xapian::docid did, const Document & document)
00684 {
00685     DEBUGAPICALL(void, "WritableDatabase::replace_document",
00686                  did << ", " << document);
00687     if (internal.size() != 1) only_one_subdatabase_allowed();
00688     if (did == 0) throw Xapian::InvalidArgumentError("Document ID 0 is invalid");
00689     internal[0]->replace_document(did, document);
00690 }
00691 
00692 Xapian::docid
00693 WritableDatabase::replace_document(const std::string & unique_term,
00694                                    const Document & document)
00695 {
00696     DEBUGAPICALL(Xapian::docid, "WritableDatabase::replace_document",
00697                  unique_term << ", " << document);
00698     if (internal.size() != 1) only_one_subdatabase_allowed();
00699     if (unique_term.empty())
00700         throw InvalidArgumentError("Empty termnames are invalid");
00701     RETURN(internal[0]->replace_document(unique_term, document));
00702 }
00703 
00704 void
00705 WritableDatabase::add_spelling(const std::string & word,
00706                                Xapian::termcount freqinc) const
00707 {
00708     DEBUGAPICALL(void, "WritableDatabase::add_spelling",
00709                  word << ", " << freqinc);
00710     if (internal.size() != 1) only_one_subdatabase_allowed();
00711     internal[0]->add_spelling(word, freqinc);
00712 }
00713 
00714 void
00715 WritableDatabase::remove_spelling(const std::string & word,
00716                                   Xapian::termcount freqdec) const
00717 {
00718     DEBUGAPICALL(void, "WritableDatabase::remove_spelling",
00719                  word << ", " << freqdec);
00720     if (internal.size() != 1) only_one_subdatabase_allowed();
00721     internal[0]->remove_spelling(word, freqdec);
00722 }
00723 
00724 void
00725 WritableDatabase::add_synonym(const std::string & term,
00726                               const std::string & synonym) const
00727 {
00728     DEBUGAPICALL(void, "WritableDatabase::add_synonym",
00729                  term << ", " << synonym);
00730     if (internal.size() != 1) only_one_subdatabase_allowed();
00731     internal[0]->add_synonym(term, synonym);
00732 }
00733 
00734 void
00735 WritableDatabase::remove_synonym(const std::string & term,
00736                                  const std::string & synonym) const
00737 {
00738     DEBUGAPICALL(void, "WritableDatabase::remove_synonym",
00739                  term << ", " << synonym);
00740     if (internal.size() != 1) only_one_subdatabase_allowed();
00741     internal[0]->remove_synonym(term, synonym);
00742 }
00743 
00744 void
00745 WritableDatabase::clear_synonyms(const std::string & term) const
00746 {
00747     DEBUGAPICALL(void, "WritableDatabase::clear_synonyms", term);
00748     if (internal.size() != 1) only_one_subdatabase_allowed();
00749     internal[0]->clear_synonyms(term);
00750 }
00751 
00752 void
00753 WritableDatabase::set_metadata(const string & key, const string & value)
00754 {
00755     DEBUGAPICALL(void, "WritableDatabase::set_metadata", key << ", " << value);
00756     if (internal.size() != 1) only_one_subdatabase_allowed();
00757     if (key.empty())
00758         throw InvalidArgumentError("Empty metadata keys are invalid");
00759     internal[0]->set_metadata(key, value);
00760 }
00761 
00762 string
00763 WritableDatabase::get_description() const
00764 {
00766     return "WritableDatabase()";
00767 }
00768 
00769 }

Documentation for Xapian (version 1.0.20).
Generated on 28 Apr 2010 by Doxygen 1.5.2.