00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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 "debuglog.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 "multivaluelist.h"
00041 #include "database.h"
00042 #include "editdistance.h"
00043 #include "ortermlist.h"
00044 #include "noreturn.h"
00045
00046 #include <cstdlib>
00047
00048 #include <cstring>
00049 #include <vector>
00050
00051 using namespace std;
00052
00053 XAPIAN_NORETURN(static void docid_zero_invalid());
00054 static void docid_zero_invalid()
00055 {
00056 throw Xapian::InvalidArgumentError("Document ID 0 is invalid");
00057 }
00058
00059 XAPIAN_NORETURN(static void no_subdatabases());
00060 static void no_subdatabases()
00061 {
00062 throw Xapian::DocNotFoundError("No subdatabases");
00063 }
00064
00065 namespace Xapian {
00066
00067 Database::Database()
00068 {
00069 LOGCALL_CTOR(API, "Database", NO_ARGS);
00070 }
00071
00072 Database::Database(Database::Internal *internal_)
00073 {
00074 LOGCALL_CTOR(API, "Database", internal_);
00075 Xapian::Internal::RefCntPtr<Database::Internal> newi(internal_);
00076 internal.push_back(newi);
00077 }
00078
00079 Database::Database(const Database &other)
00080 {
00081 LOGCALL_CTOR(API, "Database", other);
00082 internal = other.internal;
00083 }
00084
00085 void
00086 Database::operator=(const Database &other)
00087 {
00088 LOGCALL_VOID(API, "Database::operator=", other);
00089 internal = other.internal;
00090 }
00091
00092 Database::~Database()
00093 {
00094 LOGCALL_DTOR(API, "Database");
00095 }
00096
00097 void
00098 Database::reopen()
00099 {
00100 LOGCALL_VOID(API, "Database::reopen", NO_ARGS);
00101 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::iterator i;
00102 for (i = internal.begin(); i != internal.end(); ++i) {
00103 (*i)->reopen();
00104 }
00105 }
00106
00107 void
00108 Database::close()
00109 {
00110 LOGCALL_VOID(API, "Database::close", NO_ARGS);
00111 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::iterator i;
00112 for (i = internal.begin(); i != internal.end(); ++i) {
00113 (*i)->close();
00114 }
00115 }
00116
00117 void
00118 Database::add_database(const Database & database)
00119 {
00120 LOGCALL_VOID(API, "Database::add_database", database);
00121 if (this == &database) {
00122 LOGLINE(API, "Database added to itself");
00123 throw Xapian::InvalidArgumentError("Can't add a Database to itself");
00124 }
00125 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00126 for (i = database.internal.begin(); i != database.internal.end(); ++i) {
00127 internal.push_back(*i);
00128 }
00129 }
00130
00131 PostingIterator
00132 Database::postlist_begin(const string &tname) const
00133 {
00134 LOGCALL(API, PostingIterator, "Database::postlist_begin", tname);
00135
00136
00137
00138
00139
00140
00141 if (internal.size() == 1)
00142 RETURN(PostingIterator(internal[0]->open_post_list(tname)));
00143
00144 if (rare(internal.size() == 0))
00145 RETURN(PostingIterator());
00146
00147 vector<LeafPostList *> pls;
00148 try {
00149 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00150 for (i = internal.begin(); i != internal.end(); ++i) {
00151 pls.push_back((*i)->open_post_list(tname));
00152 pls.back()->next();
00153 }
00154 Assert(pls.begin() != pls.end());
00155 } catch (...) {
00156 vector<LeafPostList *>::iterator i;
00157 for (i = pls.begin(); i != pls.end(); ++i) {
00158 delete *i;
00159 *i = 0;
00160 }
00161 throw;
00162 }
00163
00164 RETURN(PostingIterator(new MultiPostList(pls, *this)));
00165 }
00166
00167 TermIterator
00168 Database::termlist_begin(Xapian::docid did) const
00169 {
00170 LOGCALL(API, TermIterator, "Database::termlist_begin", did);
00171 if (did == 0)
00172 docid_zero_invalid();
00173
00174 unsigned int multiplier = internal.size();
00175 if (rare(multiplier == 0))
00176 no_subdatabases();
00177 TermList *tl;
00178 if (multiplier == 1) {
00179
00180
00181 tl = internal[0]->open_term_list(did);
00182 } else {
00183 Assert(multiplier != 0);
00184 Xapian::doccount n = (did - 1) % multiplier;
00185 Xapian::docid m = (did - 1) / multiplier + 1;
00186
00187 tl = new MultiTermList(internal[n]->open_term_list(m), *this, n);
00188 }
00189 RETURN(TermIterator(tl));
00190 }
00191
00192 TermIterator
00193 Database::allterms_begin() const
00194 {
00195 return allterms_begin(string());
00196 }
00197
00198 TermIterator
00199 Database::allterms_begin(const std::string & prefix) const
00200 {
00201 LOGCALL(API, TermIterator, "Database::allterms_begin", NO_ARGS);
00202 TermList * tl;
00203 if (rare(internal.size() == 0)) {
00204 tl = NULL;
00205 } else if (internal.size() == 1) {
00206 tl = internal[0]->open_allterms(prefix);
00207 } else {
00208 tl = new MultiAllTermsList(internal, prefix);
00209 }
00210 RETURN(TermIterator(tl));
00211 }
00212
00213 bool
00214 Database::has_positions() const
00215 {
00216 LOGCALL(API, bool, "Database::has_positions", NO_ARGS);
00217
00218 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00219 for (i = internal.begin(); i != internal.end(); ++i) {
00220 if ((*i)->has_positions()) RETURN(true);
00221 }
00222 RETURN(false);
00223 }
00224
00225 PositionIterator
00226 Database::positionlist_begin(Xapian::docid did, const string &tname) const
00227 {
00228 LOGCALL(API, PositionIterator, "Database::positionlist_begin", did | tname);
00229 if (tname.empty())
00230 throw InvalidArgumentError("Zero length terms are invalid");
00231 if (did == 0)
00232 docid_zero_invalid();
00233
00234 unsigned int multiplier = internal.size();
00235 if (rare(multiplier == 0))
00236 no_subdatabases();
00237 Xapian::doccount n = (did - 1) % multiplier;
00238 Xapian::docid m = (did - 1) / multiplier + 1;
00239 RETURN(PositionIterator(internal[n]->open_position_list(m, tname)));
00240 }
00241
00242 Xapian::doccount
00243 Database::get_doccount() const
00244 {
00245 LOGCALL(API, Xapian::doccount, "Database::get_doccount", NO_ARGS);
00246 Xapian::doccount docs = 0;
00247 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00248 for (i = internal.begin(); i != internal.end(); ++i) {
00249 docs += (*i)->get_doccount();
00250 }
00251 RETURN(docs);
00252 }
00253
00254 Xapian::docid
00255 Database::get_lastdocid() const
00256 {
00257 LOGCALL(API, Xapian::docid, "Database::get_lastdocid", NO_ARGS);
00258 Xapian::docid did = 0;
00259
00260 unsigned int multiplier = internal.size();
00261 for (Xapian::doccount i = 0; i < multiplier; ++i) {
00262 Xapian::docid did_i = internal[i]->get_lastdocid();
00263 if (did_i) did = std::max(did, (did_i - 1) * multiplier + i + 1);
00264 }
00265 RETURN(did);
00266 }
00267
00268 Xapian::doclength
00269 Database::get_avlength() const
00270 {
00271 LOGCALL(API, Xapian::doclength, "Database::get_avlength", NO_ARGS);
00272 Xapian::doccount docs = 0;
00273 Xapian::doclength totlen = 0;
00274
00275 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00276 for (i = internal.begin(); i != internal.end(); ++i) {
00277 Xapian::doccount db_doccount = (*i)->get_doccount();
00278 docs += db_doccount;
00279 totlen += (*i)->get_avlength() * db_doccount;
00280 }
00281 LOGLINE(UNKNOWN, "get_avlength() = " << totlen << " / " << docs <<
00282 " (from " << internal.size() << " dbs)");
00283
00284 if (docs == 0) RETURN(0.0);
00285 RETURN(totlen / docs);
00286 }
00287
00288 Xapian::doccount
00289 Database::get_termfreq(const string & tname) const
00290 {
00291 LOGCALL(API, Xapian::doccount, "Database::get_termfreq", tname);
00292 if (tname.empty()) RETURN(get_doccount());
00293
00294 Xapian::doccount tf = 0;
00295 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00296 for (i = internal.begin(); i != internal.end(); ++i) {
00297 tf += (*i)->get_termfreq(tname);
00298 }
00299 RETURN(tf);
00300 }
00301
00302 Xapian::termcount
00303 Database::get_collection_freq(const string & tname) const
00304 {
00305 LOGCALL(API, Xapian::termcount, "Database::get_collection_freq", tname);
00306 if (tname.empty()) RETURN(get_doccount());
00307
00308 Xapian::termcount cf = 0;
00309 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00310 for (i = internal.begin(); i != internal.end(); ++i) {
00311 cf += (*i)->get_collection_freq(tname);
00312 }
00313 RETURN(cf);
00314 }
00315
00316 Xapian::doccount
00317 Database::get_value_freq(Xapian::valueno slot) const
00318 {
00319 LOGCALL(API, Xapian::doccount, "Database::get_value_freq", slot);
00320
00321 Xapian::doccount vf = 0;
00322 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00323 for (i = internal.begin(); i != internal.end(); ++i) {
00324 vf += (*i)->get_value_freq(slot);
00325 }
00326 RETURN(vf);
00327 }
00328
00329 string
00330 Database::get_value_lower_bound(Xapian::valueno slot) const
00331 {
00332 LOGCALL(API, string, "Database::get_value_lower_bound", slot);
00333
00334 if (rare(internal.empty())) RETURN(string());
00335
00336 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00337 i = internal.begin();
00338 string full_lb = (*i)->get_value_lower_bound(slot);
00339 while (++i != internal.end()) {
00340 string lb = (*i)->get_value_lower_bound(slot);
00341 if (lb < full_lb) full_lb = lb;
00342 }
00343 RETURN(full_lb);
00344 }
00345
00346 std::string
00347 Database::get_value_upper_bound(Xapian::valueno slot) const
00348 {
00349 LOGCALL(API, std::string, "Database::get_value_upper_bound", slot);
00350
00351 std::string full_ub;
00352 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00353 for (i = internal.begin(); i != internal.end(); ++i) {
00354 std::string ub = (*i)->get_value_upper_bound(slot);
00355 if (ub > full_ub)
00356 full_ub = ub;
00357 }
00358 RETURN(full_ub);
00359 }
00360
00361 Xapian::termcount
00362 Database::get_doclength_lower_bound() const
00363 {
00364 LOGCALL(API, Xapian::termcount, "Database::get_doclength_lower_bound", NO_ARGS);
00365
00366 if (rare(internal.empty())) RETURN(0);
00367
00368 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00369 i = internal.begin();
00370 Xapian::termcount full_lb = (*i)->get_doclength_lower_bound();
00371 while (++i != internal.end()) {
00372 Xapian::termcount lb = (*i)->get_doclength_lower_bound();
00373 if (lb < full_lb) full_lb = lb;
00374 }
00375 RETURN(full_lb);
00376 }
00377
00378 Xapian::termcount
00379 Database::get_doclength_upper_bound() const
00380 {
00381 LOGCALL(API, Xapian::termcount, "Database::get_doclength_upper_bound", NO_ARGS);
00382
00383 Xapian::termcount full_ub = 0;
00384 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00385 for (i = internal.begin(); i != internal.end(); ++i) {
00386 Xapian::termcount ub = (*i)->get_doclength_upper_bound();
00387 if (ub > full_ub) full_ub = ub;
00388 }
00389 RETURN(full_ub);
00390 }
00391
00392 Xapian::termcount
00393 Database::get_wdf_upper_bound(const string & term) const
00394 {
00395 LOGCALL(API, Xapian::termcount, "Database::get_wdf_upper_bound", term);
00396
00397 Xapian::termcount full_ub = 0;
00398 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00399 for (i = internal.begin(); i != internal.end(); ++i) {
00400 Xapian::termcount ub = (*i)->get_wdf_upper_bound(term);
00401 if (ub > full_ub) full_ub = ub;
00402 }
00403 RETURN(full_ub);
00404 }
00405
00406 ValueIterator
00407 Database::valuestream_begin(Xapian::valueno slot) const
00408 {
00409 LOGCALL(API, ValueIterator, "Database::valuestream_begin", slot);
00410 if (internal.size() == 0)
00411 RETURN(ValueIterator());
00412 if (internal.size() != 1)
00413 RETURN(ValueIterator(new MultiValueList(internal, slot)));
00414 RETURN(ValueIterator(internal[0]->open_value_list(slot)));
00415 }
00416
00417 Xapian::termcount
00418 Database::get_doclength(Xapian::docid did) const
00419 {
00420 LOGCALL(API, Xapian::termcount, "Database::get_doclength", did);
00421 if (did == 0)
00422 docid_zero_invalid();
00423
00424 unsigned int multiplier = internal.size();
00425 if (rare(multiplier == 0))
00426 no_subdatabases();
00427 Xapian::doccount n = (did - 1) % multiplier;
00428 Xapian::docid m = (did - 1) / multiplier + 1;
00429 RETURN(internal[n]->get_doclength(m));
00430 }
00431
00432 Document
00433 Database::get_document(Xapian::docid did) const
00434 {
00435 LOGCALL(API, Document, "Database::get_document", did);
00436 if (did == 0)
00437 docid_zero_invalid();
00438
00439 unsigned int multiplier = internal.size();
00440 if (rare(multiplier == 0))
00441 no_subdatabases();
00442 Xapian::doccount n = (did - 1) % multiplier;
00443 Xapian::docid m = (did - 1) / multiplier + 1;
00444
00445
00446 RETURN(Document(internal[n]->open_document(m, false)));
00447 }
00448
00449 Document::Internal *
00450 Database::get_document_lazily(Xapian::docid did) const
00451 {
00452 LOGCALL(DB, Document::Internal *, "Database::get_document_lazily", did);
00453 if (did == 0)
00454 docid_zero_invalid();
00455
00456 unsigned int multiplier = internal.size();
00457 Assert(multiplier != 0);
00458 Xapian::doccount n = (did - 1) % multiplier;
00459 Xapian::docid m = (did - 1) / multiplier + 1;
00460
00461 RETURN(internal[n]->open_document(m, true));
00462 }
00463
00464 bool
00465 Database::term_exists(const string & tname) const
00466 {
00467 LOGCALL(API, bool, "Database::term_exists", tname);
00468 if (tname.empty()) {
00469 RETURN(get_doccount() != 0);
00470 }
00471 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00472 for (i = internal.begin(); i != internal.end(); ++i) {
00473 if ((*i)->term_exists(tname)) RETURN(true);
00474 }
00475 RETURN(false);
00476 }
00477
00478 void
00479 Database::keep_alive()
00480 {
00481 LOGCALL_VOID(API, "Database::keep_alive", NO_ARGS);
00482 vector<Xapian::Internal::RefCntPtr<Database::Internal> >::const_iterator i;
00483 for (i = internal.begin(); i != internal.end(); ++i) {
00484 (*i)->keep_alive();
00485 }
00486 }
00487
00488 string
00489 Database::get_description() const
00490 {
00492 return "Database()";
00493 }
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505 #define VEC_SIZE 64
00506
00507 static int
00508 freq_edit_lower_bound(const vector<unsigned> & a, const vector<unsigned> & b)
00509 {
00510 int vec[VEC_SIZE];
00511 memset(vec, 0, sizeof(vec));
00512 vector<unsigned>::const_iterator i;
00513 for (i = a.begin(); i != a.end(); ++i) {
00514 ++vec[(*i) % VEC_SIZE];
00515 }
00516 for (i = b.begin(); i != b.end(); ++i) {
00517 --vec[(*i) % VEC_SIZE];
00518 }
00519 unsigned int total = 0;
00520 for (size_t j = 0; j < VEC_SIZE; ++j) {
00521 total += abs(vec[j]);
00522 }
00523
00524
00525
00526
00527 return (total + 1) / 2;
00528 }
00529
00530
00531
00532 #define TRIGRAM_SCORE_THRESHOLD 2
00533
00534 string
00535 Database::get_spelling_suggestion(const string &word,
00536 unsigned max_edit_distance) const
00537 {
00538 LOGCALL(API, string, "Database::get_spelling_suggestion", word | max_edit_distance);
00539 if (word.size() <= 1) return string();
00540 AutoPtr<TermList> merger;
00541 for (size_t i = 0; i < internal.size(); ++i) {
00542 TermList * tl = internal[i]->open_spelling_termlist(word);
00543 LOGLINE(SPELLING, "Sub db " << i << " tl = " << (void*)tl);
00544 if (tl) {
00545 if (merger.get()) {
00546 merger.reset(new OrTermList(merger.release(), tl));
00547 } else {
00548 merger.reset(tl);
00549 }
00550 }
00551 }
00552 if (!merger.get()) RETURN(string());
00553
00554
00555 #if ! defined __SUNPRO_CC || __SUNPRO_CC - 0 >= 0x580
00556
00557
00558 vector<unsigned> utf32_word((Utf8Iterator(word)), Utf8Iterator());
00559 #else
00560
00561
00562 vector<unsigned> utf32_word;
00563 for (Utf8Iterator sunpro_it(word); sunpro_it != Utf8Iterator(); ++sunpro_it) {
00564 utf32_word.push_back(*sunpro_it);
00565 }
00566 #endif
00567
00568 vector<unsigned> utf32_term;
00569
00570 Xapian::termcount best = 1;
00571 string result;
00572 int edist_best = max_edit_distance;
00573 Xapian::doccount freq_best = 0;
00574 Xapian::doccount freq_exact = 0;
00575 while (true) {
00576 TermList *ret = merger->next();
00577 if (ret) merger.reset(ret);
00578
00579 if (merger->at_end()) break;
00580
00581 string term = merger->get_termname();
00582 Xapian::termcount score = merger->get_wdf();
00583
00584 LOGLINE(SPELLING, "Term \"" << term << "\" ngram score " << score);
00585 if (score + TRIGRAM_SCORE_THRESHOLD >= best) {
00586 if (score > best) best = score;
00587
00588
00589
00590
00591
00592
00593
00594 if (abs(long(term.size()) - long(word.size())) > edist_best * 4) {
00595 LOGLINE(SPELLING, "Lengths much too different");
00596 continue;
00597 }
00598
00599
00600
00601 utf32_term.assign(Utf8Iterator(term), Utf8Iterator());
00602
00603 if (abs(long(utf32_term.size()) - long(utf32_word.size()))
00604 > edist_best) {
00605 LOGLINE(SPELLING, "Lengths too different");
00606 continue;
00607 }
00608
00609 if (freq_edit_lower_bound(utf32_term, utf32_word) > edist_best) {
00610 LOGLINE(SPELLING, "Rejected by character frequency test");
00611 continue;
00612 }
00613
00614 int edist = edit_distance_unsigned(&utf32_term[0],
00615 int(utf32_term.size()),
00616 &utf32_word[0],
00617 int(utf32_word.size()),
00618 edist_best);
00619 LOGLINE(SPELLING, "Edit distance " << edist);
00620
00621 if (edist <= edist_best) {
00622 Xapian::doccount freq = 0;
00623 for (size_t j = 0; j < internal.size(); ++j)
00624 freq += internal[j]->get_spelling_frequency(term);
00625
00626 LOGLINE(SPELLING, "Freq " << freq << " best " << freq_best);
00627
00628
00629
00630 if (edist == 0) {
00631 freq_exact = freq;
00632 continue;
00633 }
00634
00635 if (edist < edist_best || freq > freq_best) {
00636 LOGLINE(SPELLING, "Best so far: \"" << term <<
00637 "\" edist " << edist << " freq " << freq);
00638 result = term;
00639 edist_best = edist;
00640 freq_best = freq;
00641 }
00642 }
00643 }
00644 }
00645 if (freq_best < freq_exact)
00646 RETURN(string());
00647 RETURN(result);
00648 }
00649
00650 TermIterator
00651 Database::spellings_begin() const
00652 {
00653 LOGCALL(API, TermIterator, "Database::spellings_begin", NO_ARGS);
00654 AutoPtr<TermList> merger;
00655 for (size_t i = 0; i < internal.size(); ++i) {
00656 TermList * tl = internal[i]->open_spelling_wordlist();
00657 if (tl) {
00658 if (merger.get()) {
00659 merger.reset(new FreqAdderOrTermList(merger.release(), tl));
00660 } else {
00661 merger.reset(tl);
00662 }
00663 }
00664 }
00665 RETURN(TermIterator(merger.release()));
00666 }
00667
00668 TermIterator
00669 Database::synonyms_begin(const std::string &term) const
00670 {
00671 LOGCALL(API, TermIterator, "Database::synonyms_begin", term);
00672 AutoPtr<TermList> merger;
00673 for (size_t i = 0; i < internal.size(); ++i) {
00674 TermList * tl = internal[i]->open_synonym_termlist(term);
00675 if (tl) {
00676 if (merger.get()) {
00677 merger.reset(new OrTermList(merger.release(), tl));
00678 } else {
00679 merger.reset(tl);
00680 }
00681 }
00682 }
00683 RETURN(TermIterator(merger.release()));
00684 }
00685
00686 TermIterator
00687 Database::synonym_keys_begin(const std::string &prefix) const
00688 {
00689 LOGCALL(API, TermIterator, "Database::synonyms_keys_begin", prefix);
00690 AutoPtr<TermList> merger;
00691 for (size_t i = 0; i < internal.size(); ++i) {
00692 TermList * tl = internal[i]->open_synonym_keylist(prefix);
00693 if (tl) {
00694 if (merger.get()) {
00695 merger.reset(new OrTermList(merger.release(), tl));
00696 } else {
00697 merger.reset(tl);
00698 }
00699 }
00700 }
00701 RETURN(TermIterator(merger.release()));
00702 }
00703
00704 string
00705 Database::get_metadata(const string & key) const
00706 {
00707 LOGCALL(API, string, "Database::get_metadata", key);
00708 if (key.empty())
00709 throw InvalidArgumentError("Empty metadata keys are invalid");
00710 if (internal.empty()) RETURN(std::string());
00711 RETURN(internal[0]->get_metadata(key));
00712 }
00713
00714 Xapian::TermIterator
00715 Database::metadata_keys_begin(const std::string &prefix) const
00716 {
00717 LOGCALL(API, Xapian::TermIterator, "Database::metadata_keys_begin", NO_ARGS);
00718 if (internal.empty()) RETURN(TermIterator());
00719 RETURN(TermIterator(internal[0]->open_metadata_keylist(prefix)));
00720 }
00721
00722 std::string
00723 Database::get_uuid() const
00724 {
00725 LOGCALL(API, std::string, "Database::get_uuid", NO_ARGS);
00726 string uuid;
00727 for (size_t i = 0; i < internal.size(); ++i) {
00728 string sub_uuid = internal[i]->get_uuid();
00729
00730
00731 if (sub_uuid.empty())
00732 RETURN(sub_uuid);
00733 if (!uuid.empty()) uuid += ':';
00734 uuid += sub_uuid;
00735 }
00736 RETURN(uuid);
00737 }
00738
00740
00741 WritableDatabase::WritableDatabase() : Database()
00742 {
00743 LOGCALL_CTOR(API, "WritableDatabase", NO_ARGS);
00744 }
00745
00746 WritableDatabase::WritableDatabase(Database::Internal *internal_)
00747 : Database(internal_)
00748 {
00749 LOGCALL_CTOR(API, "WritableDatabase", internal_);
00750 }
00751
00752 WritableDatabase::WritableDatabase(const WritableDatabase &other)
00753 : Database(other)
00754 {
00755 LOGCALL_CTOR(API, "WritableDatabase", other);
00756 }
00757
00758 void
00759 WritableDatabase::operator=(const WritableDatabase &other)
00760 {
00761 LOGCALL_VOID(API, "WritableDatabase::operator=", other);
00762 Database::operator=(other);
00763 }
00764
00765 WritableDatabase::~WritableDatabase()
00766 {
00767 LOGCALL_DTOR(API, "WritableDatabase");
00768 }
00769
00770 XAPIAN_NORETURN(static void only_one_subdatabase_allowed());
00771 static void only_one_subdatabase_allowed()
00772 {
00773 throw Xapian::InvalidOperationError("WritableDatabase needs exactly one subdatabase");
00774 }
00775
00776 void
00777 WritableDatabase::commit()
00778 {
00779 LOGCALL_VOID(API, "WritableDatabase::commit", NO_ARGS);
00780 if (internal.size() != 1) only_one_subdatabase_allowed();
00781 internal[0]->commit();
00782 }
00783
00784 void
00785 WritableDatabase::begin_transaction(bool flushed)
00786 {
00787 LOGCALL_VOID(API, "WritableDatabase::begin_transaction", NO_ARGS);
00788 if (internal.size() != 1) only_one_subdatabase_allowed();
00789 internal[0]->begin_transaction(flushed);
00790 }
00791
00792 void
00793 WritableDatabase::commit_transaction()
00794 {
00795 LOGCALL_VOID(API, "WritableDatabase::commit_transaction", NO_ARGS);
00796 if (internal.size() != 1) only_one_subdatabase_allowed();
00797 internal[0]->commit_transaction();
00798 }
00799
00800 void
00801 WritableDatabase::cancel_transaction()
00802 {
00803 LOGCALL_VOID(API, "WritableDatabase::cancel_transaction", NO_ARGS);
00804 if (internal.size() != 1) only_one_subdatabase_allowed();
00805 internal[0]->cancel_transaction();
00806 }
00807
00808
00809 Xapian::docid
00810 WritableDatabase::add_document(const Document & document)
00811 {
00812 LOGCALL(API, Xapian::docid, "WritableDatabase::add_document", document);
00813 if (internal.size() != 1) only_one_subdatabase_allowed();
00814 RETURN(internal[0]->add_document(document));
00815 }
00816
00817 void
00818 WritableDatabase::delete_document(Xapian::docid did)
00819 {
00820 LOGCALL_VOID(API, "WritableDatabase::delete_document", did);
00821 if (internal.size() != 1) only_one_subdatabase_allowed();
00822 if (did == 0)
00823 docid_zero_invalid();
00824 internal[0]->delete_document(did);
00825 }
00826
00827 void
00828 WritableDatabase::delete_document(const std::string & unique_term)
00829 {
00830 LOGCALL_VOID(API, "WritableDatabase::delete_document", unique_term);
00831 if (internal.size() != 1) only_one_subdatabase_allowed();
00832 if (unique_term.empty())
00833 throw InvalidArgumentError("Empty termnames are invalid");
00834 internal[0]->delete_document(unique_term);
00835 }
00836
00837 void
00838 WritableDatabase::replace_document(Xapian::docid did, const Document & document)
00839 {
00840 LOGCALL_VOID(API, "WritableDatabase::replace_document", did | document);
00841 if (internal.size() != 1) only_one_subdatabase_allowed();
00842 if (did == 0)
00843 docid_zero_invalid();
00844 internal[0]->replace_document(did, document);
00845 }
00846
00847 Xapian::docid
00848 WritableDatabase::replace_document(const std::string & unique_term,
00849 const Document & document)
00850 {
00851 LOGCALL(API, Xapian::docid, "WritableDatabase::replace_document", unique_term | document);
00852 if (internal.size() != 1) only_one_subdatabase_allowed();
00853 if (unique_term.empty())
00854 throw InvalidArgumentError("Empty termnames are invalid");
00855 RETURN(internal[0]->replace_document(unique_term, document));
00856 }
00857
00858 void
00859 WritableDatabase::add_spelling(const std::string & word,
00860 Xapian::termcount freqinc) const
00861 {
00862 LOGCALL_VOID(API, "WritableDatabase::add_spelling", word | freqinc);
00863 if (internal.size() != 1) only_one_subdatabase_allowed();
00864 internal[0]->add_spelling(word, freqinc);
00865 }
00866
00867 void
00868 WritableDatabase::remove_spelling(const std::string & word,
00869 Xapian::termcount freqdec) const
00870 {
00871 LOGCALL_VOID(API, "WritableDatabase::remove_spelling", word | freqdec);
00872 if (internal.size() != 1) only_one_subdatabase_allowed();
00873 internal[0]->remove_spelling(word, freqdec);
00874 }
00875
00876 void
00877 WritableDatabase::add_synonym(const std::string & term,
00878 const std::string & synonym) const
00879 {
00880 LOGCALL_VOID(API, "WritableDatabase::add_synonym", term | synonym);
00881 if (internal.size() != 1) only_one_subdatabase_allowed();
00882 internal[0]->add_synonym(term, synonym);
00883 }
00884
00885 void
00886 WritableDatabase::remove_synonym(const std::string & term,
00887 const std::string & synonym) const
00888 {
00889 LOGCALL_VOID(API, "WritableDatabase::remove_synonym", term | synonym);
00890 if (internal.size() != 1) only_one_subdatabase_allowed();
00891 internal[0]->remove_synonym(term, synonym);
00892 }
00893
00894 void
00895 WritableDatabase::clear_synonyms(const std::string & term) const
00896 {
00897 LOGCALL_VOID(API, "WritableDatabase::clear_synonyms", term);
00898 if (internal.size() != 1) only_one_subdatabase_allowed();
00899 internal[0]->clear_synonyms(term);
00900 }
00901
00902 void
00903 WritableDatabase::set_metadata(const string & key, const string & value)
00904 {
00905 LOGCALL_VOID(API, "WritableDatabase::set_metadata", key | value);
00906 if (internal.size() != 1) only_one_subdatabase_allowed();
00907 if (key.empty())
00908 throw InvalidArgumentError("Empty metadata keys are invalid");
00909 internal[0]->set_metadata(key, value);
00910 }
00911
00912 string
00913 WritableDatabase::get_description() const
00914 {
00916 return "WritableDatabase()";
00917 }
00918
00919 }