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

Documentation for Xapian (version 1.0.10).
Generated on 24 Dec 2008 by Doxygen 1.5.2.