tests/api_anydb.cc

Go to the documentation of this file.
00001 /* api_anydb.cc: tests which work with any backend
00002  *
00003  * Copyright 1999,2000,2001 BrightStation PLC
00004  * Copyright 2002 Ananova Ltd
00005  * Copyright 2002,2003,2004,2005,2006,2007,2008,2009 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 "api_anydb.h"
00027 
00028 #include <algorithm>
00029 #include <string>
00030 
00031 #include <xapian.h>
00032 #include "backendmanager_local.h"
00033 #include "testsuite.h"
00034 #include "testutils.h"
00035 #include "utils.h"
00036 
00037 #include "apitest.h"
00038 
00039 #include <list>
00040 
00041 using namespace std;
00042 
00043 static void
00044 print_mset_weights(const Xapian::MSet &mset)
00045 {
00046     Xapian::MSetIterator i = mset.begin();
00047     for ( ; i != mset.end(); ++i) {
00048         tout << " " << i.get_weight();
00049     }
00050 }
00051 
00052 static void
00053 print_mset_percentages(const Xapian::MSet &mset)
00054 {
00055     Xapian::MSetIterator i = mset.begin();
00056     for ( ; i != mset.end(); ++i) {
00057         tout << " " << mset.convert_to_percent(i);
00058     }
00059 }
00060 
00061 static Xapian::Query
00062 query(Xapian::Query::op op,
00063       const string & t1 = string(), const string & t2 = string(),
00064       const string & t3 = string(), const string & t4 = string(),
00065       const string & t5 = string(), const string & t6 = string(),
00066       const string & t7 = string(), const string & t8 = string(),
00067       const string & t9 = string(), const string & t10 = string())
00068 {
00069     vector<string> v;
00070     Xapian::Stem stemmer("english");
00071     if (!t1.empty()) v.push_back(stemmer(t1));
00072     if (!t2.empty()) v.push_back(stemmer(t2));
00073     if (!t3.empty()) v.push_back(stemmer(t3));
00074     if (!t4.empty()) v.push_back(stemmer(t4));
00075     if (!t5.empty()) v.push_back(stemmer(t5));
00076     if (!t6.empty()) v.push_back(stemmer(t6));
00077     if (!t7.empty()) v.push_back(stemmer(t7));
00078     if (!t8.empty()) v.push_back(stemmer(t8));
00079     if (!t9.empty()) v.push_back(stemmer(t9));
00080     if (!t10.empty()) v.push_back(stemmer(t10));
00081     return Xapian::Query(op, v.begin(), v.end());
00082 }
00083 
00084 static Xapian::Query
00085 query(Xapian::Query::op op, Xapian::termcount parameter,
00086       const string & t1 = string(), const string & t2 = string(),
00087       const string & t3 = string(), const string & t4 = string(),
00088       const string & t5 = string(), const string & t6 = string(),
00089       const string & t7 = string(), const string & t8 = string(),
00090       const string & t9 = string(), const string & t10 = string())
00091 {
00092     vector<string> v;
00093     Xapian::Stem stemmer("english");
00094     if (!t1.empty()) v.push_back(stemmer(t1));
00095     if (!t2.empty()) v.push_back(stemmer(t2));
00096     if (!t3.empty()) v.push_back(stemmer(t3));
00097     if (!t4.empty()) v.push_back(stemmer(t4));
00098     if (!t5.empty()) v.push_back(stemmer(t5));
00099     if (!t6.empty()) v.push_back(stemmer(t6));
00100     if (!t7.empty()) v.push_back(stemmer(t7));
00101     if (!t8.empty()) v.push_back(stemmer(t8));
00102     if (!t9.empty()) v.push_back(stemmer(t9));
00103     if (!t10.empty()) v.push_back(stemmer(t10));
00104     return Xapian::Query(op, v.begin(), v.end(), parameter);
00105 }
00106 
00107 static Xapian::Query
00108 query(const string &t)
00109 {
00110     return Xapian::Query(Xapian::Stem("english")(t));
00111 }
00112 
00113 // #######################################################################
00114 // # Tests start here
00115 
00116 // tests that the backend doesn't return zero docids
00117 DEFINE_TESTCASE(zerodocid1, backend) {
00118     // open the database (in this case a simple text file
00119     // we prepared earlier)
00120 
00121     Xapian::Database mydb(get_database("apitest_onedoc"));
00122 
00123     Xapian::Enquire enquire(mydb);
00124 
00125     // make a simple query, with one word in it - "word".
00126     enquire.set_query(Xapian::Query("word"));
00127 
00128     // retrieve the top ten results (we only expect one)
00129     Xapian::MSet mymset = enquire.get_mset(0, 10);
00130 
00131     // We've done the query, now check that the result is what
00132     // we expect (1 document, with non-zero docid)
00133     TEST_MSET_SIZE(mymset, 1);
00134 
00135     TEST_AND_EXPLAIN(*(mymset.begin()) != 0,
00136                      "A query on a database returned a zero docid");
00137 
00138     return true;
00139 }
00140 
00141 // tests that an empty query returns no matches
00142 DEFINE_TESTCASE(emptyquery1, backend) {
00143     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00144 
00145     enquire.set_query(Xapian::Query());
00146     Xapian::MSet mymset = enquire.get_mset(0, 10);
00147     TEST_MSET_SIZE(mymset, 0);
00148     TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
00149     TEST_EQUAL(mymset.get_matches_upper_bound(), 0);
00150     TEST_EQUAL(mymset.get_matches_estimated(), 0);
00151 
00152     vector<Xapian::Query> v;
00153     enquire.set_query(Xapian::Query(Xapian::Query::OP_AND, v.begin(), v.end()));
00154     mymset = enquire.get_mset(0, 10);
00155     TEST_MSET_SIZE(mymset, 0);
00156     TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
00157     TEST_EQUAL(mymset.get_matches_upper_bound(), 0);
00158     TEST_EQUAL(mymset.get_matches_estimated(), 0);
00159 
00160     return true;
00161 }
00162 
00163 // tests the document count for a simple query
00164 DEFINE_TESTCASE(simplequery1, backend) {
00165     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00166     enquire.set_query(Xapian::Query("word"));
00167     Xapian::MSet mymset = enquire.get_mset(0, 10);
00168     TEST_MSET_SIZE(mymset, 2);
00169     return true;
00170 }
00171 
00172 // tests for the right documents and weights returned with simple query
00173 DEFINE_TESTCASE(simplequery2, backend) {
00174     // open the database (in this case a simple text file
00175     // we prepared earlier)
00176     Xapian::Database db = get_database("apitest_simpledata");
00177     Xapian::Enquire enquire(db);
00178     enquire.set_query(Xapian::Query("word"));
00179 
00180     // retrieve the top results
00181     Xapian::MSet mymset = enquire.get_mset(0, 10);
00182 
00183     // We've done the query, now check that the result is what
00184     // we expect (documents 2 and 4)
00185     mset_expect_order(mymset, 2, 4);
00186 
00187     // Check the weights
00188     Xapian::MSetIterator i = mymset.begin();
00189     // These weights are for BM25Weight(1,0,1,0.5,0.5)
00190     TEST_EQUAL_DOUBLE(i.get_weight(), 1.04648168717725);
00191     i++;
00192     TEST_EQUAL_DOUBLE(i.get_weight(), 0.640987686595914);
00193 
00194     return true;
00195 }
00196 
00197 // tests for the right document count for another simple query
00198 DEFINE_TESTCASE(simplequery3, backend) {
00199     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00200     enquire.set_query(query("this"));
00201     Xapian::MSet mymset = enquire.get_mset(0, 10);
00202 
00203     // Check that 6 documents were returned.
00204     TEST_MSET_SIZE(mymset, 6);
00205 
00206     return true;
00207 }
00208 
00209 // tests for the right document count for a wildcard query
00210 // FIXME: move this to querytest (and just use an InMemory DB).
00211 DEFINE_TESTCASE(wildquery1, backend) {
00212     Xapian::QueryParser queryparser;
00213     unsigned flags = Xapian::QueryParser::FLAG_WILDCARD |
00214                      Xapian::QueryParser::FLAG_LOVEHATE;
00215     queryparser.set_stemmer(Xapian::Stem("english"));
00216     queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_ALL);
00217     Xapian::Database db = get_database("apitest_simpledata");
00218     queryparser.set_database(db);
00219     Xapian::Enquire enquire(db);
00220 
00221     Xapian::Query qobj = queryparser.parse_query("th*", flags);
00222     tout << qobj.get_description() << endl;
00223     enquire.set_query(qobj);
00224     Xapian::MSet mymset = enquire.get_mset(0, 10);
00225     // Check that 6 documents were returned.
00226     TEST_MSET_SIZE(mymset, 6);
00227 
00228     qobj = queryparser.parse_query("notindb* \"this\"", flags);
00229     tout << qobj.get_description() << endl;
00230     enquire.set_query(qobj);
00231     mymset = enquire.get_mset(0, 10);
00232     // Check that 6 documents were returned.
00233     TEST_MSET_SIZE(mymset, 6);
00234 
00235     qobj = queryparser.parse_query("+notindb* \"this\"", flags);
00236     tout << qobj.get_description() << endl;
00237     enquire.set_query(qobj);
00238     mymset = enquire.get_mset(0, 10);
00239     // Check that 0 documents were returned.
00240     TEST_MSET_SIZE(mymset, 0);
00241 
00242     return true;
00243 }
00244 
00245 // tests a query across multiple databases
00246 DEFINE_TESTCASE(multidb1, backend) {
00247     Xapian::Database mydb1(get_database("apitest_simpledata", "apitest_simpledata2"));
00248     Xapian::Enquire enquire1(mydb1);
00249 
00250     Xapian::Database mydb2(get_database("apitest_simpledata"));
00251     mydb2.add_database(get_database("apitest_simpledata2"));
00252     Xapian::Enquire enquire2(mydb2);
00253 
00254     // make a simple query, with one word in it - "word".
00255     Xapian::Query myquery("word");
00256     enquire1.set_query(myquery);
00257     enquire2.set_query(myquery);
00258 
00259     // retrieve the top ten results from each method of accessing
00260     // multiple text files
00261     Xapian::MSet mymset1 = enquire1.get_mset(0, 10);
00262     Xapian::MSet mymset2 = enquire2.get_mset(0, 10);
00263 
00264     TEST_EQUAL(mymset1.size(), mymset2.size());
00265     TEST(mset_range_is_same_weights(mymset1, 0, mymset2, 0, mymset1.size()));
00266     return true;
00267 }
00268 
00269 // tests a query across multiple databases with terms only
00270 // in one of the two databases
00271 DEFINE_TESTCASE(multidb2, backend && !multi) {
00272     Xapian::Database mydb1(get_database("apitest_simpledata",
00273                                   "apitest_simpledata2"));
00274     Xapian::Enquire enquire1(mydb1);
00275 
00276     Xapian::Database mydb2(get_database("apitest_simpledata"));
00277     mydb2.add_database(get_database("apitest_simpledata2"));
00278     Xapian::Enquire enquire2(mydb2);
00279 
00280     // make a simple query
00281     Xapian::Query myquery = query(Xapian::Query::OP_OR, "inmemory", "word");
00282     enquire1.set_query(myquery);
00283     enquire2.set_query(myquery);
00284 
00285     // retrieve the top ten results from each method of accessing
00286     // multiple text files
00287     Xapian::MSet mymset1 = enquire1.get_mset(0, 10);
00288     Xapian::MSet mymset2 = enquire2.get_mset(0, 10);
00289 
00290     TEST_EQUAL(mymset1.size(), mymset2.size());
00291     TEST(mset_range_is_same_weights(mymset1, 0, mymset2, 0, mymset1.size()));
00292     return true;
00293 }
00294 
00295 // test that a multidb with 2 dbs query returns correct docids
00296 DEFINE_TESTCASE(multidb3, backend && !multi) {
00297     Xapian::Database mydb2(get_database("apitest_simpledata"));
00298     mydb2.add_database(get_database("apitest_simpledata2"));
00299     Xapian::Enquire enquire(mydb2);
00300 
00301     // make a query
00302     Xapian::Query myquery = query(Xapian::Query::OP_OR, "inmemory", "word");
00303     enquire.set_weighting_scheme(Xapian::BoolWeight());
00304     enquire.set_query(myquery);
00305 
00306     // retrieve the top ten results
00307     Xapian::MSet mymset = enquire.get_mset(0, 10);
00308     mset_expect_order(mymset, 2, 3, 7);
00309 
00310     return true;
00311 }
00312 
00313 // test that a multidb with 3 dbs query returns correct docids
00314 DEFINE_TESTCASE(multidb4, backend && !multi) {
00315     Xapian::Database mydb2(get_database("apitest_simpledata"));
00316     mydb2.add_database(get_database("apitest_simpledata2"));
00317     mydb2.add_database(get_database("apitest_termorder"));
00318     Xapian::Enquire enquire(mydb2);
00319 
00320     // make a query
00321     Xapian::Query myquery = query(Xapian::Query::OP_OR, "inmemory", "word");
00322     enquire.set_weighting_scheme(Xapian::BoolWeight());
00323     enquire.set_query(myquery);
00324 
00325     // retrieve the top ten results
00326     Xapian::MSet mymset = enquire.get_mset(0, 10);
00327     mset_expect_order(mymset, 2, 3, 4, 10);
00328 
00329     return true;
00330 }
00331 
00332 // tests MultiPostList::skip_to().
00333 DEFINE_TESTCASE(multidb5, backend && !multi) {
00334     Xapian::Database mydb2(get_database("apitest_simpledata"));
00335     mydb2.add_database(get_database("apitest_simpledata2"));
00336     Xapian::Enquire enquire(mydb2);
00337 
00338     // make a query
00339     Xapian::Query myquery = query(Xapian::Query::OP_AND, "inmemory", "word");
00340     enquire.set_weighting_scheme(Xapian::BoolWeight());
00341     enquire.set_query(myquery);
00342 
00343     // retrieve the top ten results
00344     Xapian::MSet mymset = enquire.get_mset(0, 10);
00345     mset_expect_order(mymset, 2);
00346 
00347     return true;
00348 }
00349 
00350 // tests that when specifying maxitems to get_mset, no more than
00351 // that are returned.
00352 DEFINE_TESTCASE(msetmaxitems1, backend) {
00353     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00354     enquire.set_query(query("this"));
00355     Xapian::MSet mymset = enquire.get_mset(0, 1);
00356     TEST_MSET_SIZE(mymset, 1);
00357 
00358     mymset = enquire.get_mset(0, 5);
00359     TEST_MSET_SIZE(mymset, 5);
00360 
00361     return true;
00362 }
00363 
00364 // tests the returned weights are as expected (regression test for remote
00365 // backend which was using the average weight rather than the actual document
00366 // weight for computing weights - fixed in 1.0.0).
00367 DEFINE_TESTCASE(expandweights1, backend) {
00368     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00369     enquire.set_query(Xapian::Query("this"));
00370 
00371     Xapian::MSet mymset = enquire.get_mset(0, 10);
00372 
00373     Xapian::RSet myrset;
00374     Xapian::MSetIterator i = mymset.begin();
00375     myrset.add_document(*i);
00376     myrset.add_document(*(++i));
00377 
00378     Xapian::ESet eset = enquire.get_eset(3, myrset, enquire.USE_EXACT_TERMFREQ);
00379     TEST_EQUAL(eset.size(), 3);
00380     TEST_EQUAL_DOUBLE(eset[0].get_weight(), 6.08904001099445);
00381     TEST_EQUAL_DOUBLE(eset[1].get_weight(), 6.08904001099445);
00382     TEST_EQUAL_DOUBLE(eset[2].get_weight(), 4.73383620844021);
00383 
00384     return true;
00385 }
00386 
00387 // Just like test_expandweights1 but without USE_EXACT_TERMFREQ.
00388 DEFINE_TESTCASE(expandweights2, backend) {
00389     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00390     enquire.set_query(Xapian::Query("this"));
00391 
00392     Xapian::MSet mymset = enquire.get_mset(0, 10);
00393 
00394     Xapian::RSet myrset;
00395     Xapian::MSetIterator i = mymset.begin();
00396     myrset.add_document(*i);
00397     myrset.add_document(*(++i));
00398 
00399     Xapian::ESet eset = enquire.get_eset(3, myrset);
00400     TEST_EQUAL(eset.size(), 3);
00401     if (strcmp(get_dbtype(), "multi") != 0) {
00402         // For a single database, the weights should be the same with or
00403         // without USE_EXACT_TERMFREQ.
00404         TEST_EQUAL_DOUBLE(eset[0].get_weight(), 6.08904001099445);
00405         TEST_EQUAL_DOUBLE(eset[1].get_weight(), 6.08904001099445);
00406         TEST_EQUAL_DOUBLE(eset[2].get_weight(), 4.73383620844021);
00407     } else {
00408         // For multiple databases, we expect that using USE_EXACT_TERMFREQ
00409         // will result in different weights in some cases.
00410         TEST_NOT_EQUAL_DOUBLE(eset[0].get_weight(), 6.08904001099445);
00411         TEST_EQUAL_DOUBLE(eset[1].get_weight(), 6.08904001099445);
00412         TEST_NOT_EQUAL_DOUBLE(eset[2].get_weight(), 4.73383620844021);
00413     }
00414 
00415     return true;
00416 }
00417 
00418 // tests that when specifying maxitems to get_eset, no more than
00419 // that are returned.
00420 DEFINE_TESTCASE(expandmaxitems1, backend) {
00421     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00422     enquire.set_query(Xapian::Query("this"));
00423 
00424     Xapian::MSet mymset = enquire.get_mset(0, 10);
00425     tout << "mymset.size() = " << mymset.size() << endl;
00426     TEST(mymset.size() >= 2);
00427 
00428     Xapian::RSet myrset;
00429     Xapian::MSetIterator i = mymset.begin();
00430     myrset.add_document(*i);
00431     myrset.add_document(*(++i));
00432 
00433     Xapian::ESet myeset = enquire.get_eset(1, myrset);
00434     TEST_EQUAL(myeset.size(), 1);
00435 
00436     return true;
00437 }
00438 
00439 // tests that a pure boolean query has all weights set to 0
00440 DEFINE_TESTCASE(boolquery1, backend) {
00441     Xapian::Query myboolquery(query("this"));
00442 
00443     // open the database (in this case a simple text file
00444     // we prepared earlier)
00445     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00446     enquire.set_query(myboolquery);
00447     enquire.set_weighting_scheme(Xapian::BoolWeight());
00448 
00449     // retrieve the top results
00450     Xapian::MSet mymset = enquire.get_mset(0, 10);
00451 
00452     TEST_NOT_EQUAL(mymset.size(), 0);
00453     TEST_EQUAL(mymset.get_max_possible(), 0);
00454     for (Xapian::MSetIterator i = mymset.begin(); i != mymset.end(); ++i) {
00455         TEST_EQUAL(i.get_weight(), 0);
00456     }
00457     return true;
00458 }
00459 
00460 // tests that get_mset() specifying "this" works as expected
00461 DEFINE_TESTCASE(msetfirst1, backend) {
00462     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00463     enquire.set_query(query("this"));
00464     Xapian::MSet mymset1 = enquire.get_mset(0, 6);
00465     Xapian::MSet mymset2 = enquire.get_mset(3, 3);
00466     TEST(mset_range_is_same(mymset1, 3, mymset2, 0, 3));
00467 
00468     // Regression test - we weren't adjusting the index into items[] by
00469     // firstitem in api/omenquire.cc.
00470     TEST_EQUAL(mymset1[5].get_document().get_data(),
00471                mymset2[2].get_document().get_data());
00472     return true;
00473 }
00474 
00475 // tests the converting-to-percent functions
00476 DEFINE_TESTCASE(topercent1, backend) {
00477     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00478     enquire.set_query(query("this"));
00479     Xapian::MSet mymset = enquire.get_mset(0, 20);
00480 
00481     int last_pct = 100;
00482     Xapian::MSetIterator i = mymset.begin();
00483     for ( ; i != mymset.end(); ++i) {
00484         int pct = mymset.convert_to_percent(i);
00485         TEST_AND_EXPLAIN(pct == i.get_percent(),
00486                          "convert_to_%(msetitor) != convert_to_%(wt)");
00487         TEST_AND_EXPLAIN(pct == mymset.convert_to_percent(i.get_weight()),
00488                          "convert_to_%(msetitor) != convert_to_%(wt)");
00489         TEST_AND_EXPLAIN(pct >= 0 && pct <= 100,
00490                          "percentage out of range: " << pct);
00491         TEST_AND_EXPLAIN(pct <= last_pct, "percentage increased down mset");
00492         last_pct = pct;
00493     }
00494     return true;
00495 }
00496 
00497 // tests the percentage values returned
00498 DEFINE_TESTCASE(topercent2, backend) {
00499     BackendManagerLocal local_manager;
00500     local_manager.set_datadir(test_driver::get_srcdir() + "/testdata/");
00501     Xapian::Enquire localenq(local_manager.get_database("apitest_simpledata"));
00502     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00503 
00504     int pct;
00505 
00506     // First, test a search in which the top document scores 100%.
00507     enquire.set_query(query("this"));
00508     localenq.set_query(query("this"));
00509     Xapian::MSet mymset = enquire.get_mset(0, 20);
00510     Xapian::MSet localmset = localenq.get_mset(0, 20);
00511 
00512     Xapian::MSetIterator i = mymset.begin();
00513     TEST(i != mymset.end());
00514     pct = mymset.convert_to_percent(i);
00515     TEST_EQUAL(pct, 100);
00516 
00517     TEST_EQUAL(mymset.get_matches_lower_bound(), localmset.get_matches_lower_bound());
00518     TEST_EQUAL(mymset.get_matches_upper_bound(), localmset.get_matches_upper_bound());
00519     TEST_EQUAL(mymset.get_matches_estimated(), localmset.get_matches_estimated());
00520     TEST_EQUAL_DOUBLE(mymset.get_max_attained(), localmset.get_max_attained());
00521     TEST_EQUAL_DOUBLE(mymset.get_max_possible(), localmset.get_max_possible());
00522     TEST_EQUAL(mymset.size(), localmset.size());
00523     TEST(mset_range_is_same(mymset, 0, localmset, 0, mymset.size()));
00524 
00525     // A search in which the top document doesn't have 100%
00526     Xapian::Query q = query(Xapian::Query::OP_OR,
00527                             "this", "line", "paragraph", "rubbish");
00528     enquire.set_query(q);
00529     localenq.set_query(q);
00530     mymset = enquire.get_mset(0, 20);
00531     localmset = localenq.get_mset(0, 20);
00532 
00533     i = mymset.begin();
00534     TEST(i != mymset.end());
00535     pct = mymset.convert_to_percent(i);
00536     TEST_GREATER(pct, 65);
00537     TEST_LESSER(pct, 75);
00538 
00539     ++i;
00540 
00541     TEST(i != mymset.end());
00542     pct = mymset.convert_to_percent(i);
00543     TEST_GREATER(pct, 40);
00544     TEST_LESSER(pct, 50);
00545 
00546     TEST_EQUAL(mymset.get_matches_lower_bound(), localmset.get_matches_lower_bound());
00547     TEST_EQUAL(mymset.get_matches_upper_bound(), localmset.get_matches_upper_bound());
00548     TEST_EQUAL(mymset.get_matches_estimated(), localmset.get_matches_estimated());
00549     TEST_EQUAL_DOUBLE(mymset.get_max_attained(), localmset.get_max_attained());
00550     TEST_EQUAL_DOUBLE(mymset.get_max_possible(), localmset.get_max_possible());
00551     TEST_EQUAL(mymset.size(), localmset.size());
00552     TEST(mset_range_is_same(mymset, 0, localmset, 0, mymset.size()));
00553 
00554     return true;
00555 }
00556 
00557 class myExpandFunctor : public Xapian::ExpandDecider {
00558     public:
00559         bool operator()(const string & tname) const {
00560             unsigned long sum = 0;
00561             for (string::const_iterator i=tname.begin(); i!=tname.end(); ++i) {
00562                 sum += *i;
00563             }
00564 //          if (verbose) {
00565 //              tout << tname << "==> " << sum << "\n";
00566 //          }
00567             return (sum % 2) == 0;
00568         }
00569 };
00570 
00571 // tests the expand decision functor
00572 DEFINE_TESTCASE(expandfunctor1, backend) {
00573     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00574     enquire.set_query(Xapian::Query("this"));
00575 
00576     Xapian::MSet mymset = enquire.get_mset(0, 10);
00577     TEST(mymset.size() >= 2);
00578 
00579     Xapian::RSet myrset;
00580     Xapian::MSetIterator i = mymset.begin();
00581     myrset.add_document(*i);
00582     myrset.add_document(*(++i));
00583 
00584     myExpandFunctor myfunctor;
00585 
00586     Xapian::ESet myeset_orig = enquire.get_eset(1000, myrset);
00587     unsigned int neweset_size = 0;
00588     Xapian::ESetIterator j = myeset_orig.begin();
00589     for ( ; j != myeset_orig.end(); ++j) {
00590         if (myfunctor(*j)) neweset_size++;
00591     }
00592     Xapian::ESet myeset = enquire.get_eset(neweset_size, myrset, &myfunctor);
00593 
00594 #if 0
00595     // Compare myeset with the hand-filtered version of myeset_orig.
00596     if (verbose) {
00597         tout << "orig_eset: ";
00598         copy(myeset_orig.begin(), myeset_orig.end(),
00599              ostream_iterator<Xapian::ESetItem>(tout, " "));
00600         tout << "\n";
00601 
00602         tout << "new_eset: ";
00603         copy(myeset.begin(), myeset.end(),
00604              ostream_iterator<Xapian::ESetItem>(tout, " "));
00605         tout << "\n";
00606     }
00607 #endif
00608     Xapian::ESetIterator orig = myeset_orig.begin();
00609     Xapian::ESetIterator filt = myeset.begin();
00610     for (; orig != myeset_orig.end() && filt != myeset.end(); ++orig, ++filt) {
00611         // skip over items that shouldn't be in myeset
00612         while (orig != myeset_orig.end() && !myfunctor(*orig)) {
00613             ++orig;
00614         }
00615 
00616         TEST_AND_EXPLAIN(*orig == *filt &&
00617                          orig.get_weight() == filt.get_weight(),
00618                          "Mismatch in items " << *orig << " vs. " << *filt
00619                          << " after filtering");
00620     }
00621 
00622     while (orig != myeset_orig.end() && !myfunctor(*orig)) {
00623         ++orig;
00624     }
00625 
00626     TEST_EQUAL(orig, myeset_orig.end());
00627     TEST_AND_EXPLAIN(filt == myeset.end(),
00628                      "Extra items in the filtered eset.");
00629     return true;
00630 }
00631 
00632 // tests the percent cutoff option
00633 DEFINE_TESTCASE(pctcutoff1, backend) {
00634     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00635     enquire.set_query(query(Xapian::Query::OP_OR,
00636                             "this", "line", "paragraph", "rubbish"));
00637     Xapian::MSet mymset1 = enquire.get_mset(0, 100);
00638 
00639     if (verbose) {
00640         tout << "Original mset pcts:";
00641         print_mset_percentages(mymset1);
00642         tout << "\n";
00643     }
00644 
00645     unsigned int num_items = 0;
00646     int my_pct = 100;
00647     int changes = 0;
00648     Xapian::MSetIterator i = mymset1.begin();
00649     int c = 0;
00650     for ( ; i != mymset1.end(); ++i, ++c) {
00651         int new_pct = mymset1.convert_to_percent(i);
00652         if (new_pct != my_pct) {
00653             changes++;
00654             if (changes > 3) break;
00655             num_items = c;
00656             my_pct = new_pct;
00657         }
00658     }
00659 
00660     TEST_AND_EXPLAIN(changes > 3, "MSet not varied enough to test");
00661     if (verbose) {
00662         tout << "Cutoff percent: " << my_pct << "\n";
00663     }
00664 
00665     enquire.set_cutoff(my_pct);
00666     Xapian::MSet mymset2 = enquire.get_mset(0, 100);
00667 
00668     if (verbose) {
00669         tout << "Percentages after cutoff:";
00670         print_mset_percentages(mymset2);
00671         tout << "\n";
00672     }
00673 
00674     TEST_AND_EXPLAIN(mymset2.size() >= num_items,
00675                      "Match with % cutoff lost too many items");
00676 
00677     TEST_AND_EXPLAIN(mymset2.size() == num_items ||
00678                      (mymset2.convert_to_percent(mymset2[num_items]) == my_pct &&
00679                       mymset2.convert_to_percent(mymset2.back()) == my_pct),
00680                      "Match with % cutoff returned too many items");
00681 
00682     return true;
00683 }
00684 
00685 // Tests the percent cutoff option combined with collapsing
00686 DEFINE_TESTCASE(pctcutoff2, backend) {
00687     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00688     enquire.set_query(Xapian::Query("this"));
00689     enquire.set_query(Xapian::Query(Xapian::Query::OP_AND_NOT, Xapian::Query("this"), Xapian::Query("banana")));
00690     Xapian::MSet mset = enquire.get_mset(0, 100);
00691 
00692     if (verbose) {
00693         tout << "Original mset pcts:";
00694         print_mset_percentages(mset);
00695         tout << "\n";
00696     }
00697 
00698     TEST(mset.size() >= 2);
00699     TEST(mset[0].get_percent() - mset[1].get_percent() >= 2);
00700 
00701     Xapian::percent cutoff = mset[0].get_percent() + mset[1].get_percent();
00702     cutoff /= 2;
00703 
00704     enquire.set_cutoff(cutoff);
00705     enquire.set_collapse_key(1234); // Value which is always empty.
00706 
00707     mset = enquire.get_mset(0, 1);
00708     TEST_EQUAL(mset.size(), 1);
00709     TEST_EQUAL(mset.get_matches_lower_bound(), 1);
00710 
00711     return true;
00712 }
00713 
00714 // tests the cutoff option
00715 DEFINE_TESTCASE(cutoff1, backend) {
00716     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00717     enquire.set_query(query(Xapian::Query::OP_OR,
00718                             "this", "line", "paragraph", "rubbish"));
00719     Xapian::MSet mymset1 = enquire.get_mset(0, 100);
00720 
00721     if (verbose) {
00722         tout << "Original mset weights:";
00723         print_mset_weights(mymset1);
00724         tout << "\n";
00725     }
00726 
00727     unsigned int num_items = 0;
00728     Xapian::weight my_wt = -100;
00729     int changes = 0;
00730     Xapian::MSetIterator i = mymset1.begin();
00731     int c = 0;
00732     for ( ; i != mymset1.end(); ++i, ++c) {
00733         Xapian::weight new_wt = i.get_weight();
00734         if (new_wt != my_wt) {
00735             changes++;
00736             if (changes > 3) break;
00737             num_items = c;
00738             my_wt = new_wt;
00739         }
00740     }
00741 
00742     TEST_AND_EXPLAIN(changes > 3, "MSet not varied enough to test");
00743     if (verbose) {
00744         tout << "Cutoff weight: " << my_wt << "\n";
00745     }
00746 
00747     enquire.set_cutoff(0, my_wt);
00748     Xapian::MSet mymset2 = enquire.get_mset(0, 100);
00749 
00750     if (verbose) {
00751         tout << "Weights after cutoff:";
00752         print_mset_weights(mymset2);
00753         tout << "\n";
00754     }
00755 
00756     TEST_AND_EXPLAIN(mymset2.size() >= num_items,
00757                      "Match with cutoff lost too many items");
00758 
00759     TEST_AND_EXPLAIN(mymset2.size() == num_items ||
00760                      (mymset2[num_items].get_weight() == my_wt &&
00761                       mymset2.back().get_weight() == my_wt),
00762                      "Match with cutoff returned too many items");
00763 
00764     return true;
00765 }
00766 
00767 // tests the allow query terms expand option
00768 DEFINE_TESTCASE(allowqterms1, backend) {
00769     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00770     enquire.set_query(Xapian::Query("this"));
00771 
00772     Xapian::MSet mymset = enquire.get_mset(0, 10);
00773     TEST(mymset.size() >= 2);
00774 
00775     Xapian::RSet myrset;
00776     Xapian::MSetIterator i = mymset.begin();
00777     myrset.add_document(*i);
00778     myrset.add_document(*(++i));
00779 
00780     Xapian::ESet myeset = enquire.get_eset(1000, myrset);
00781     Xapian::ESetIterator j = myeset.begin();
00782     for ( ; j != myeset.end(); ++j) {
00783         TEST_NOT_EQUAL(*j, "this");
00784     }
00785 
00786     Xapian::ESet myeset2 = enquire.get_eset(1000, myrset, Xapian::Enquire::INCLUDE_QUERY_TERMS);
00787     j = myeset2.begin();
00788     for ( ; j != myeset2.end(); ++j) {
00789         if (*j == "this") break;
00790     }
00791     TEST(j != myeset2.end());
00792     return true;
00793 }
00794 
00795 // tests that the MSet max_attained works
00796 DEFINE_TESTCASE(maxattain1, backend) {
00797     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00798     enquire.set_query(query("this"));
00799     Xapian::MSet mymset = enquire.get_mset(0, 100);
00800 
00801     Xapian::weight mymax = 0;
00802     Xapian::MSetIterator i = mymset.begin();
00803     for ( ; i != mymset.end(); ++i) {
00804         if (i.get_weight() > mymax) mymax = i.get_weight();
00805     }
00806     TEST_EQUAL(mymax, mymset.get_max_attained());
00807 
00808     return true;
00809 }
00810 
00811 // tests a reversed boolean query
00812 DEFINE_TESTCASE(reversebool1, backend) {
00813     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00814     enquire.set_query(Xapian::Query("this"));
00815     enquire.set_weighting_scheme(Xapian::BoolWeight());
00816 
00817     Xapian::MSet mymset1 = enquire.get_mset(0, 100);
00818     TEST_AND_EXPLAIN(mymset1.size() > 1,
00819                      "Mset was too small to test properly");
00820 
00821     enquire.set_docid_order(Xapian::Enquire::ASCENDING);
00822     Xapian::MSet mymset2 = enquire.get_mset(0, 100);
00823     enquire.set_docid_order(Xapian::Enquire::DESCENDING);
00824     Xapian::MSet mymset3 = enquire.get_mset(0, 100);
00825 
00826     // mymset1 and mymset2 should be identical
00827     TEST_EQUAL(mymset1.size(), mymset2.size());
00828 
00829     {
00830         Xapian::MSetIterator i = mymset1.begin();
00831         Xapian::MSetIterator j = mymset2.begin();
00832         for ( ; i != mymset1.end(), j != mymset2.end(); ++i, j++) {
00833             // if this fails, then setting match_sort_forward=true was not
00834             // the same as the default.
00835             TEST_EQUAL(*i, *j);
00836         }
00837     }
00838 
00839     // mymset1 and mymset3 should be same but reversed
00840     TEST_EQUAL(mymset1.size(), mymset3.size());
00841 
00842     {
00843         Xapian::MSetIterator i = mymset1.begin();
00844         vector<Xapian::docid> rev(mymset3.begin(), mymset3.end());
00845         // Next iterator not const because of compiler brokenness (egcs 1.1.2)
00846         vector<Xapian::docid>::reverse_iterator j = rev.rbegin();
00847         for ( ; i != mymset1.end(); ++i, j++) {
00848             // if this fails, then setting match_sort_forward=false didn't
00849             // reverse the results.
00850             TEST_EQUAL(*i, *j);
00851         }
00852     }
00853 
00854     return true;
00855 }
00856 
00857 // tests a reversed boolean query, where the full mset isn't returned
00858 DEFINE_TESTCASE(reversebool2, backend) {
00859     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00860     enquire.set_query(Xapian::Query("this"));
00861     enquire.set_weighting_scheme(Xapian::BoolWeight());
00862 
00863     Xapian::MSet mymset1 = enquire.get_mset(0, 100);
00864 
00865     TEST_AND_EXPLAIN(mymset1.size() > 1,
00866                      "Mset was too small to test properly");
00867 
00868     enquire.set_docid_order(Xapian::Enquire::ASCENDING);
00869     Xapian::doccount msize = mymset1.size() / 2;
00870     Xapian::MSet mymset2 = enquire.get_mset(0, msize);
00871     enquire.set_docid_order(Xapian::Enquire::DESCENDING);
00872     Xapian::MSet mymset3 = enquire.get_mset(0, msize);
00873 
00874     // mymset2 should be first msize items of mymset1
00875     TEST_EQUAL(msize, mymset2.size());
00876     {
00877         Xapian::MSetIterator i = mymset1.begin();
00878         Xapian::MSetIterator j = mymset2.begin();
00879         for ( ; i != mymset1.end(), j != mymset2.end(); ++i, j++) {
00880             // if this fails, then setting match_sort_forward=true was not
00881             // the same as the default.
00882             TEST_EQUAL(*i, *j);
00883         }
00884     }
00885 
00886     // mymset3 should be last msize items of mymset1, in reverse order
00887     TEST_EQUAL(msize, mymset3.size());
00888     {
00889         vector<Xapian::docid> rev(mymset1.begin(), mymset1.end());
00890         // Next iterator not const because of compiler brokenness (egcs 1.1.2)
00891         vector<Xapian::docid>::reverse_iterator i = rev.rbegin();
00892         Xapian::MSetIterator j = mymset3.begin();
00893         for ( ; j != mymset3.end(); ++i, j++) {
00894             // if this fails, then setting match_sort_forward=false didn't
00895             // reverse the results.
00896             TEST_EQUAL(*i, *j);
00897         }
00898     }
00899 
00900     return true;
00901 }
00902 
00903 // tests that get_matching_terms() returns the terms in the right order
00904 DEFINE_TESTCASE(getmterms1, backend) {
00905     list<string> answers_list;
00906     answers_list.push_back("one");
00907     answers_list.push_back("two");
00908     answers_list.push_back("three");
00909     answers_list.push_back("four");
00910 
00911     Xapian::Database mydb(get_database("apitest_termorder"));
00912     Xapian::Enquire enquire(mydb);
00913 
00914     Xapian::Query myquery(Xapian::Query::OP_OR,
00915             Xapian::Query(Xapian::Query::OP_AND,
00916                     Xapian::Query("one", 1, 1),
00917                     Xapian::Query("three", 1, 3)),
00918             Xapian::Query(Xapian::Query::OP_OR,
00919                     Xapian::Query("four", 1, 4),
00920                     Xapian::Query("two", 1, 2)));
00921 
00922     enquire.set_query(myquery);
00923 
00924     Xapian::MSet mymset = enquire.get_mset(0, 10);
00925 
00926     TEST_MSET_SIZE(mymset, 1);
00927     list<string> list(enquire.get_matching_terms_begin(mymset.begin()),
00928                           enquire.get_matching_terms_end(mymset.begin()));
00929     TEST(list == answers_list);
00930 
00931     return true;
00932 }
00933 
00934 // tests that get_matching_terms() returns the terms only once
00935 DEFINE_TESTCASE(getmterms2, backend) {
00936     list<string> answers_list;
00937     answers_list.push_back("one");
00938     answers_list.push_back("two");
00939     answers_list.push_back("three");
00940 
00941     Xapian::Database mydb(get_database("apitest_termorder"));
00942     Xapian::Enquire enquire(mydb);
00943 
00944     Xapian::Query myquery(Xapian::Query::OP_OR,
00945             Xapian::Query(Xapian::Query::OP_AND,
00946                     Xapian::Query("one", 1, 1),
00947                     Xapian::Query("three", 1, 3)),
00948             Xapian::Query(Xapian::Query::OP_OR,
00949                     Xapian::Query("one", 1, 4),
00950                     Xapian::Query("two", 1, 2)));
00951 
00952     enquire.set_query(myquery);
00953 
00954     Xapian::MSet mymset = enquire.get_mset(0, 10);
00955 
00956     TEST_MSET_SIZE(mymset, 1);
00957     list<string> list(enquire.get_matching_terms_begin(mymset.begin()),
00958                           enquire.get_matching_terms_end(mymset.begin()));
00959     TEST(list == answers_list);
00960 
00961     return true;
00962 }
00963 
00964 // tests that the collapsing on termpos optimisation works
00965 DEFINE_TESTCASE(poscollapse1, backend) {
00966     Xapian::Query myquery1(Xapian::Query::OP_OR,
00967                      Xapian::Query("this", 1, 1),
00968                      Xapian::Query("this", 1, 1));
00969     Xapian::Query myquery2("this", 2, 1);
00970 
00971     if (verbose) {
00972         tout << myquery1.get_description() << "\n";
00973         tout << myquery2.get_description() << "\n";
00974     }
00975 
00976     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00977     enquire.set_query(myquery1);
00978     Xapian::MSet mymset1 = enquire.get_mset(0, 10);
00979 
00980     enquire.set_query(myquery2);
00981     Xapian::MSet mymset2 = enquire.get_mset(0, 10);
00982 
00983     TEST_EQUAL(mymset1, mymset2);
00984 
00985     return true;
00986 }
00987 
00988 // test that running a query twice returns the same results
00989 DEFINE_TESTCASE(repeatquery1, backend) {
00990     Xapian::Enquire enquire(get_database("apitest_simpledata"));
00991     enquire.set_query(Xapian::Query("this"));
00992 
00993     enquire.set_query(query(Xapian::Query::OP_OR, "this", "word"));
00994 
00995     Xapian::MSet mymset1 = enquire.get_mset(0, 10);
00996     Xapian::MSet mymset2 = enquire.get_mset(0, 10);
00997     TEST_EQUAL(mymset1, mymset2);
00998 
00999     return true;
01000 }
01001 
01002 // test that prefetching documents works (at least, gives same results)
01003 DEFINE_TESTCASE(fetchdocs1, backend) {
01004     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01005     enquire.set_query(Xapian::Query("this"));
01006 
01007     enquire.set_query(query(Xapian::Query::OP_OR, "this", "word"));
01008 
01009     Xapian::MSet mymset1 = enquire.get_mset(0, 10);
01010     Xapian::MSet mymset2 = enquire.get_mset(0, 10);
01011     TEST_EQUAL(mymset1, mymset2);
01012     mymset2.fetch(mymset2[0], mymset2[mymset2.size() - 1]);
01013     mymset2.fetch(mymset2.begin(), mymset2.end());
01014     mymset2.fetch(mymset2.begin());
01015     mymset2.fetch();
01016 
01017     Xapian::MSetIterator it1 = mymset1.begin();
01018     Xapian::MSetIterator it2 = mymset2.begin();
01019 
01020     while (it1 != mymset1.end() && it2 != mymset2.end()) {
01021         TEST_EQUAL(it1.get_document().get_data(),
01022                    it2.get_document().get_data());
01023         TEST_NOT_EQUAL(it1.get_document().get_data(), "");
01024         TEST_NOT_EQUAL(it2.get_document().get_data(), "");
01025         it1++;
01026         it2++;
01027     }
01028     TEST_EQUAL(it1, mymset1.end());
01029     TEST_EQUAL(it1, mymset2.end());
01030 
01031     return true;
01032 }
01033 
01034 // test that searching for a term not in the database fails nicely
01035 DEFINE_TESTCASE(absentterm1, backend) {
01036     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01037     enquire.set_weighting_scheme(Xapian::BoolWeight());
01038     enquire.set_query(Xapian::Query("frink"));
01039 
01040     Xapian::MSet mymset = enquire.get_mset(0, 10);
01041     mset_expect_order(mymset);
01042 
01043     return true;
01044 }
01045 
01046 // as absentterm1, but setting query from a vector of terms
01047 DEFINE_TESTCASE(absentterm2, backend) {
01048     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01049     vector<string> terms;
01050     terms.push_back("frink");
01051 
01052     Xapian::Query query(Xapian::Query::OP_OR, terms.begin(), terms.end());
01053     enquire.set_query(query);
01054 
01055     Xapian::MSet mymset = enquire.get_mset(0, 10);
01056     mset_expect_order(mymset);
01057 
01058     return true;
01059 }
01060 
01061 // test that rsets do sensible things
01062 DEFINE_TESTCASE(rset1, backend) {
01063     Xapian::Database mydb(get_database("apitest_rset"));
01064     Xapian::Enquire enquire(mydb);
01065     Xapian::Query myquery = query(Xapian::Query::OP_OR, "giraffe", "tiger");
01066     enquire.set_query(myquery);
01067 
01068     Xapian::MSet mymset1 = enquire.get_mset(0, 10);
01069 
01070     Xapian::RSet myrset;
01071     myrset.add_document(1);
01072 
01073     Xapian::MSet mymset2 = enquire.get_mset(0, 10, &myrset);
01074 
01075     // We should have the same documents turn up, but 1 and 3 should
01076     // have higher weights with the RSet.
01077     TEST_MSET_SIZE(mymset1, 3);
01078     TEST_MSET_SIZE(mymset2, 3);
01079 
01080     return true;
01081 }
01082 
01083 // test that rsets do more sensible things
01084 DEFINE_TESTCASE(rset2, backend) {
01085     Xapian::Database mydb(get_database("apitest_rset"));
01086     Xapian::Enquire enquire(mydb);
01087     Xapian::Query myquery = query(Xapian::Query::OP_OR, "cuddly", "people");
01088     enquire.set_query(myquery);
01089 
01090     Xapian::MSet mymset1 = enquire.get_mset(0, 10);
01091 
01092     Xapian::RSet myrset;
01093     myrset.add_document(2);
01094 
01095     Xapian::MSet mymset2 = enquire.get_mset(0, 10, &myrset);
01096 
01097     mset_expect_order(mymset1, 1, 2);
01098     mset_expect_order(mymset2, 2, 1);
01099 
01100     return true;
01101 }
01102 
01103 // test that rsets behave correctly with multiDBs
01104 DEFINE_TESTCASE(rsetmultidb1, backend && !multi) {
01105     Xapian::Database mydb1(get_database("apitest_rset", "apitest_simpledata2"));
01106     Xapian::Database mydb2(get_database("apitest_rset"));
01107     mydb2.add_database(get_database("apitest_simpledata2"));
01108 
01109     Xapian::Enquire enquire1(mydb1);
01110     Xapian::Enquire enquire2(mydb2);
01111 
01112     Xapian::Query myquery = query(Xapian::Query::OP_OR, "cuddly", "multiple");
01113 
01114     enquire1.set_query(myquery);
01115     enquire2.set_query(myquery);
01116 
01117     Xapian::RSet myrset1;
01118     Xapian::RSet myrset2;
01119     myrset1.add_document(4);
01120     myrset2.add_document(2);
01121 
01122     Xapian::MSet mymset1a = enquire1.get_mset(0, 10);
01123     Xapian::MSet mymset1b = enquire1.get_mset(0, 10, &myrset1);
01124     Xapian::MSet mymset2a = enquire2.get_mset(0, 10);
01125     Xapian::MSet mymset2b = enquire2.get_mset(0, 10, &myrset2);
01126 
01127     mset_expect_order(mymset1a, 1, 4);
01128     mset_expect_order(mymset1b, 4, 1);
01129     mset_expect_order(mymset2a, 1, 2);
01130     mset_expect_order(mymset2b, 2, 1);
01131 
01132     TEST(mset_range_is_same_weights(mymset1a, 0, mymset2a, 0, 2));
01133     TEST(mset_range_is_same_weights(mymset1b, 0, mymset2b, 0, 2));
01134     TEST_NOT_EQUAL(mymset1a, mymset1b);
01135     TEST_NOT_EQUAL(mymset2a, mymset2b);
01136 
01137     return true;
01138 }
01139 
01140 // regression tests - used to cause assertion in stats.h to fail
01141 // Doesn't actually fail for multi but it doesn't make sense to run there.
01142 DEFINE_TESTCASE(rsetmultidb3, backend && !multi) {
01143     Xapian::Enquire enquire(get_database("apitest_simpledata2"));
01144     enquire.set_query(query(Xapian::Query::OP_OR, "cuddly", "people"));
01145     Xapian::MSet mset = enquire.get_mset(0, 10); // used to fail assertion
01146     return true;
01147 }
01148 
01150 DEFINE_TESTCASE(eliteset1, backend) {
01151     // FIXME: OP_ELITE_SET erroneously picks the best N terms separately in
01152     // each sub-database!
01153     SKIP_TEST_FOR_BACKEND("multi");
01154 
01155     Xapian::Database mydb(get_database("apitest_simpledata"));
01156     Xapian::Enquire enquire(mydb);
01157 
01158     Xapian::Query myquery1 = query(Xapian::Query::OP_OR, "word");
01159 
01160     Xapian::Query myquery2 = query(Xapian::Query::OP_ELITE_SET, 1,
01161                                    "simple", "word");
01162 
01163     enquire.set_query(myquery1, 2); // So the query lengths are the same.
01164     Xapian::MSet mymset1 = enquire.get_mset(0, 10);
01165 
01166     enquire.set_query(myquery2);
01167     Xapian::MSet mymset2 = enquire.get_mset(0, 10);
01168 
01169     TEST_EQUAL(mymset1, mymset2);
01170     return true;
01171 }
01172 
01175 DEFINE_TESTCASE(eliteset2, backend) {
01176     // FIXME: OP_ELITE_SET erroneously picks the best N terms separately in
01177     // each sub-database!
01178     SKIP_TEST_FOR_BACKEND("multi");
01179 
01180     Xapian::Database mydb(get_database("apitest_simpledata"));
01181     Xapian::Enquire enquire(mydb);
01182 
01183     Xapian::Query myquery1 = query(Xapian::Query::OP_AND, "word", "search");
01184 
01185     vector<Xapian::Query> qs;
01186     qs.push_back(query("this"));
01187     qs.push_back(query(Xapian::Query::OP_AND, "word", "search"));
01188     Xapian::Query myquery2(Xapian::Query::OP_ELITE_SET,
01189                            qs.begin(), qs.end(), 1);
01190 
01191     enquire.set_query(myquery1);
01192     Xapian::MSet mymset1 = enquire.get_mset(0, 10);
01193 
01194     enquire.set_query(myquery2);
01195     Xapian::MSet mymset2 = enquire.get_mset(0, 10);
01196 
01197     TEST_EQUAL(mymset1, mymset2);
01198     // query lengths differ so mset weights not the same (with some weighting
01199     // parameters)
01200     //test_mset_order_equal(mymset1, mymset2);
01201 
01202     return true;
01203 }
01204 
01207 DEFINE_TESTCASE(eliteset3, backend) {
01208     Xapian::Database mydb1(get_database("apitest_simpledata"));
01209     Xapian::Enquire enquire1(mydb1);
01210 
01211     Xapian::Database mydb2(get_database("apitest_simpledata"));
01212     Xapian::Enquire enquire2(mydb2);
01213 
01214     // make a query
01215     Xapian::Stem stemmer("english");
01216 
01217     string term1 = stemmer("word");
01218     string term2 = stemmer("rubbish");
01219     string term3 = stemmer("banana");
01220 
01221     vector<string> terms;
01222     terms.push_back(term1);
01223     terms.push_back(term2);
01224     terms.push_back(term3);
01225 
01226     Xapian::Query myquery1(Xapian::Query::OP_OR, terms.begin(), terms.end());
01227     enquire1.set_query(myquery1);
01228 
01229     Xapian::Query myquery2(Xapian::Query::OP_ELITE_SET, terms.begin(), terms.end(), 3);
01230     enquire2.set_query(myquery2);
01231 
01232     // retrieve the results
01233     Xapian::MSet mymset1 = enquire1.get_mset(0, 10);
01234     Xapian::MSet mymset2 = enquire2.get_mset(0, 10);
01235 
01236     TEST_EQUAL(mymset1.get_termfreq(term1),
01237                mymset2.get_termfreq(term1));
01238     TEST_EQUAL(mymset1.get_termweight(term1),
01239                mymset2.get_termweight(term1));
01240     TEST_EQUAL(mymset1.get_termfreq(term2),
01241                mymset2.get_termfreq(term2));
01242     TEST_EQUAL(mymset1.get_termweight(term2),
01243                mymset2.get_termweight(term2));
01244     TEST_EQUAL(mymset1.get_termfreq(term3),
01245                mymset2.get_termfreq(term3));
01246     TEST_EQUAL(mymset1.get_termweight(term3),
01247                mymset2.get_termweight(term3));
01248 //    TEST_EQUAL(mymset1, mymset2);
01249 
01250     return true;
01251 }
01252 
01254 DEFINE_TESTCASE(eliteset4, backend) {
01255     // FIXME: OP_ELITE_SET erroneously picks the best N terms separately in
01256     // each sub-database!
01257     SKIP_TEST_FOR_BACKEND("multi");
01258 
01259     Xapian::Database mydb1(get_database("apitest_simpledata"));
01260     Xapian::Enquire enquire1(mydb1);
01261 
01262     Xapian::Database mydb2(get_database("apitest_simpledata"));
01263     Xapian::Enquire enquire2(mydb2);
01264 
01265     Xapian::Query myquery1 = query("rubbish");
01266     Xapian::Query myquery2 = query(Xapian::Query::OP_ELITE_SET, 1,
01267                                    "word", "rubbish", "fibble");
01268     enquire1.set_query(myquery1);
01269     enquire2.set_query(myquery2);
01270 
01271     // retrieve the results
01272     Xapian::MSet mymset1 = enquire1.get_mset(0, 10);
01273     Xapian::MSet mymset2 = enquire2.get_mset(0, 10);
01274 
01275     TEST_NOT_EQUAL(mymset2.size(), 0);
01276     TEST_EQUAL(mymset1, mymset2);
01277 //    TEST_EQUAL(mymset1, mymset2);
01278 
01279     return true;
01280 }
01281 
01283 DEFINE_TESTCASE(eliteset5, backend) {
01284     SKIP_TEST_FOR_BACKEND("multi");
01285 
01286     Xapian::Database mydb1(get_database("apitest_simpledata"));
01287     Xapian::Enquire enquire1(mydb1);
01288 
01289     vector<string> v;
01290     for (int i = 0; i != 3; ++i) {
01291         v.push_back("simpl");
01292         v.push_back("queri");
01293 
01294         v.push_back("rubbish");
01295         v.push_back("rubbish");
01296         v.push_back("rubbish");
01297         v.push_back("word");
01298         v.push_back("word");
01299         v.push_back("word");
01300     }
01301 
01302     Xapian::Query myquery1 = Xapian::Query(Xapian::Query::OP_ELITE_SET,
01303                                            v.begin(), v.end(), 1);
01304     myquery1 = Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT,
01305                              myquery1,
01306                              0.004);
01307 
01308     enquire1.set_query(myquery1);
01309     // On architectures with excess precision (or, at least, on x86), the
01310     // following call used to result in a segfault.
01311     enquire1.get_mset(0, 10);
01312 
01313     return true;
01314 }
01315 
01317 DEFINE_TESTCASE(termlisttermfreq1, backend) {
01318     Xapian::Database mydb(get_database("apitest_simpledata"));
01319     Xapian::Enquire enquire(mydb);
01320     Xapian::Stem stemmer("english");
01321     Xapian::RSet rset1;
01322     Xapian::RSet rset2;
01323     rset1.add_document(5);
01324     rset2.add_document(6);
01325 
01326     Xapian::ESet eset1 = enquire.get_eset(1000, rset1);
01327     Xapian::ESet eset2 = enquire.get_eset(1000, rset2);
01328 
01329     // search for weight of term 'another'
01330     string theterm = stemmer("another");
01331 
01332     Xapian::weight wt1 = 0;
01333     Xapian::weight wt2 = 0;
01334     {
01335         Xapian::ESetIterator i = eset1.begin();
01336         for ( ; i != eset1.end(); i++) {
01337             if (*i == theterm) {
01338                 wt1 = i.get_weight();
01339                 break;
01340             }
01341         }
01342     }
01343     {
01344         Xapian::ESetIterator i = eset2.begin();
01345         for ( ; i != eset2.end(); i++) {
01346             if (*i == theterm) {
01347                 wt2 = i.get_weight();
01348                 break;
01349             }
01350         }
01351     }
01352 
01353     TEST_NOT_EQUAL(wt1, 0);
01354     TEST_NOT_EQUAL(wt2, 0);
01355     TEST_EQUAL(wt1, wt2);
01356 
01357     return true;
01358 }
01359 
01361 DEFINE_TESTCASE(qterminfo1, backend) {
01362     Xapian::Database mydb1(get_database("apitest_simpledata", "apitest_simpledata2"));
01363     Xapian::Enquire enquire1(mydb1);
01364 
01365     Xapian::Database mydb2(get_database("apitest_simpledata"));
01366     mydb2.add_database(get_database("apitest_simpledata2"));
01367     Xapian::Enquire enquire2(mydb2);
01368 
01369     // make a query
01370     Xapian::Stem stemmer("english");
01371 
01372     string term1 = stemmer("word");
01373     string term2 = stemmer("inmemory");
01374     string term3 = stemmer("flibble");
01375 
01376     Xapian::Query myquery(Xapian::Query::OP_OR,
01377                     Xapian::Query(term1),
01378                     Xapian::Query(Xapian::Query::OP_OR,
01379                             Xapian::Query(term2),
01380                             Xapian::Query(term3)));
01381     enquire1.set_query(myquery);
01382     enquire2.set_query(myquery);
01383 
01384     // retrieve the results
01385     Xapian::MSet mymset1a = enquire1.get_mset(0, 0);
01386     Xapian::MSet mymset2a = enquire2.get_mset(0, 0);
01387 
01388     TEST_EQUAL(mymset1a.get_termfreq(term1),
01389                mymset2a.get_termfreq(term1));
01390     TEST_EQUAL(mymset1a.get_termweight(term1),
01391                mymset2a.get_termweight(term1));
01392     TEST_EQUAL(mymset1a.get_termfreq(term2),
01393                mymset2a.get_termfreq(term2));
01394     TEST_EQUAL(mymset1a.get_termweight(term2),
01395                mymset2a.get_termweight(term2));
01396     TEST_EQUAL(mymset1a.get_termfreq(term3),
01397                mymset2a.get_termfreq(term3));
01398     TEST_EQUAL(mymset1a.get_termweight(term3),
01399                mymset2a.get_termweight(term3));
01400 
01401     TEST_EQUAL(mymset1a.get_termfreq(term1), 3);
01402     TEST_EQUAL(mymset1a.get_termfreq(term2), 1);
01403     TEST_EQUAL(mymset1a.get_termfreq(term3), 0);
01404 
01405     TEST_NOT_EQUAL(mymset1a.get_termweight(term1), 0);
01406     TEST_NOT_EQUAL(mymset1a.get_termweight(term2), 0);
01407     // non-existent terms still have weight
01408     TEST_NOT_EQUAL(mymset1a.get_termweight(term3), 0);
01409 
01410     TEST_EXCEPTION(Xapian::InvalidArgumentError,
01411                    mymset1a.get_termfreq("sponge"));
01412 
01413     return true;
01414 }
01415 
01417 DEFINE_TESTCASE(qterminfo2, backend) {
01418     Xapian::Database db(get_database("apitest_simpledata"));
01419     Xapian::Enquire enquire(db);
01420 
01421     // make a query
01422     Xapian::Stem stemmer("english");
01423 
01424     string term1 = stemmer("paragraph");
01425     string term2 = stemmer("another");
01426 
01427     Xapian::Query query(Xapian::Query::OP_AND_NOT, term1,
01428             Xapian::Query(Xapian::Query::OP_AND, term1, term2));
01429     enquire.set_query(query);
01430 
01431     // retrieve the results
01432     // Note: get_mset() used to throw "AssertionError" in debug builds
01433     Xapian::MSet mset = enquire.get_mset(0, 10);
01434 
01435     TEST_NOT_EQUAL(mset.get_termweight("paragraph"), 0);
01436 
01437     return true;
01438 }
01439 
01440 // tests that when specifying that no items are to be returned, those
01441 // statistics which should be the same are.
01442 DEFINE_TESTCASE(msetzeroitems1, backend) {
01443     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01444     enquire.set_query(query("this"));
01445     Xapian::MSet mymset1 = enquire.get_mset(0, 0);
01446 
01447     Xapian::MSet mymset2 = enquire.get_mset(0, 1);
01448 
01449     TEST_EQUAL(mymset1.get_max_possible(), mymset2.get_max_possible());
01450 
01451     return true;
01452 }
01453 
01454 // test that the matches_* of a simple query are as expected
01455 DEFINE_TESTCASE(matches1, backend) {
01456     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01457     Xapian::Query myquery;
01458     Xapian::MSet mymset;
01459 
01460     myquery = query("word");
01461     enquire.set_query(myquery);
01462     mymset = enquire.get_mset(0, 10);
01463     TEST_EQUAL(mymset.get_matches_lower_bound(), 2);
01464     TEST_EQUAL(mymset.get_matches_estimated(), 2);
01465     TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
01466 
01467     myquery = query(Xapian::Query::OP_OR, "inmemory", "word");
01468     enquire.set_query(myquery);
01469     mymset = enquire.get_mset(0, 10);
01470     TEST_EQUAL(mymset.get_matches_lower_bound(), 2);
01471     TEST_EQUAL(mymset.get_matches_estimated(), 2);
01472     TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
01473 
01474     myquery = query(Xapian::Query::OP_AND, "inmemory", "word");
01475     enquire.set_query(myquery);
01476     mymset = enquire.get_mset(0, 10);
01477     TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
01478     TEST_EQUAL(mymset.get_matches_estimated(), 0);
01479     TEST_EQUAL(mymset.get_matches_upper_bound(), 0);
01480 
01481     myquery = query(Xapian::Query::OP_AND, "simple", "word");
01482     enquire.set_query(myquery);
01483     mymset = enquire.get_mset(0, 10);
01484     TEST_EQUAL(mymset.get_matches_lower_bound(), 2);
01485     TEST_EQUAL(mymset.get_matches_estimated(), 2);
01486     TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
01487 
01488     myquery = query(Xapian::Query::OP_AND, "simple", "word");
01489     enquire.set_query(myquery);
01490     mymset = enquire.get_mset(0, 0);
01491     // For a single database, this is true, but not for "multi" (since there
01492     // one sub-database has 3 documents and simple and word both have termfreq
01493     // of 2, so the matcher can tell at least one document must match!)
01494     // TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
01495     TEST(mymset.get_matches_lower_bound() <= mymset.get_matches_estimated());
01496     TEST_EQUAL(mymset.get_matches_estimated(), 1);
01497     TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
01498 
01499     mymset = enquire.get_mset(0, 1);
01500     TEST_EQUAL(mymset.get_matches_lower_bound(), 2);
01501     TEST_EQUAL(mymset.get_matches_estimated(), 2);
01502     TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
01503 
01504     mymset = enquire.get_mset(0, 2);
01505     TEST_EQUAL(mymset.get_matches_lower_bound(), 2);
01506     TEST_EQUAL(mymset.get_matches_estimated(), 2);
01507     TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
01508 
01509     myquery = query(Xapian::Query::OP_AND, "paragraph", "another");
01510     enquire.set_query(myquery);
01511     mymset = enquire.get_mset(0, 0);
01512     TEST_EQUAL(mymset.get_matches_lower_bound(), 1);
01513     TEST_EQUAL(mymset.get_matches_estimated(), 2);
01514     TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
01515 
01516     mymset = enquire.get_mset(0, 1);
01517     TEST_EQUAL(mymset.get_matches_lower_bound(), 1);
01518     TEST_EQUAL(mymset.get_matches_estimated(), 2);
01519     TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
01520 
01521     mymset = enquire.get_mset(0, 2);
01522     TEST_EQUAL(mymset.get_matches_lower_bound(), 1);
01523     TEST_EQUAL(mymset.get_matches_estimated(), 1);
01524     TEST_EQUAL(mymset.get_matches_upper_bound(), 1);
01525 
01526     mymset = enquire.get_mset(1, 20);
01527     TEST_EQUAL(mymset.get_matches_lower_bound(), 1);
01528     TEST_EQUAL(mymset.get_matches_estimated(), 1);
01529     TEST_EQUAL(mymset.get_matches_upper_bound(), 1);
01530 
01531     return true;
01532 }
01533 
01534 // tests that wqf affects the document weights
01535 DEFINE_TESTCASE(wqf1, backend) {
01536     // Both queries have length 2; in q1 word has wqf=2, in q2 word has wqf=1
01537     Xapian::Query q1("word", 2);
01538     Xapian::Query q2("word");
01539     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01540     enquire.set_query(q1);
01541     Xapian::MSet mset1 = enquire.get_mset(0, 10);
01542     enquire.set_query(q2);
01543     Xapian::MSet mset2 = enquire.get_mset(0, 2);
01544     // Check the weights
01545     TEST(mset1.begin().get_weight() > mset2.begin().get_weight());
01546     return true;
01547 }
01548 
01549 // tests that query length affects the document weights
01550 DEFINE_TESTCASE(qlen1, backend) {
01551     Xapian::Query q1("word");
01552     Xapian::Query q2("word");
01553     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01554     enquire.set_query(q1);
01555     Xapian::MSet mset1 = enquire.get_mset(0, 10);
01556     enquire.set_query(q2);
01557     Xapian::MSet mset2 = enquire.get_mset(0, 2);
01558     // Check the weights
01559     //TEST(mset1.begin().get_weight() < mset2.begin().get_weight());
01560     TEST(mset1.begin().get_weight() == mset2.begin().get_weight());
01561     return true;
01562 }
01563 
01564 // tests that opening a non-existent termlist throws the correct exception
01565 DEFINE_TESTCASE(termlist1, backend) {
01566     Xapian::Database db(get_database("apitest_onedoc"));
01567     TEST_EXCEPTION(Xapian::InvalidArgumentError,
01568                    Xapian::TermIterator t = db.termlist_begin(0));
01569     TEST_EXCEPTION(Xapian::DocNotFoundError,
01570                    Xapian::TermIterator t = db.termlist_begin(2));
01571     /* Cause the database to be used properly, showing up problems
01572      * with the link being in a bad state.  CME */
01573     Xapian::TermIterator temp = db.termlist_begin(1);
01574     TEST_EXCEPTION(Xapian::DocNotFoundError,
01575                    Xapian::TermIterator t = db.termlist_begin(999999999));
01576     return true;
01577 }
01578 
01579 // tests that a Xapian::TermIterator works as an STL iterator
01580 DEFINE_TESTCASE(termlist2, backend) {
01581     Xapian::Database db(get_database("apitest_onedoc"));
01582     Xapian::TermIterator t = db.termlist_begin(1);
01583     Xapian::TermIterator tend = db.termlist_end(1);
01584 
01585     // test operator= creates a copy which compares equal
01586     Xapian::TermIterator t_copy = t;
01587     TEST_EQUAL(t, t_copy);
01588 
01589     // test copy constructor creates a copy which compares equal
01590     Xapian::TermIterator t_clone(t);
01591     TEST_EQUAL(t, t_clone);
01592 
01593     vector<string> v(t, tend);
01594 
01595     t = db.termlist_begin(1);
01596     tend = db.termlist_end(1);
01597     vector<string>::const_iterator i;
01598     for (i = v.begin(); i != v.end(); i++) {
01599         TEST_NOT_EQUAL(t, tend);
01600         TEST_EQUAL(*i, *t);
01601         t++;
01602     }
01603     TEST_EQUAL(t, tend);
01604     return true;
01605 }
01606 
01607 static Xapian::TermIterator
01608 test_termlist3_helper()
01609 {
01610     Xapian::Database db(get_database("apitest_onedoc"));
01611     return db.termlist_begin(1);
01612 }
01613 
01614 // tests that a Xapian::TermIterator still works when the DB is deleted
01615 DEFINE_TESTCASE(termlist3, backend) {
01616     Xapian::TermIterator u = test_termlist3_helper();
01617     Xapian::Database db(get_database("apitest_onedoc"));
01618     Xapian::TermIterator t = db.termlist_begin(1);
01619     Xapian::TermIterator tend = db.termlist_end(1);
01620 
01621     while (t != tend) {
01622         TEST_EQUAL(*t, *u);
01623         t++;
01624         u++;
01625     }
01626     return true;
01627 }
01628 
01629 // tests skip_to
01630 DEFINE_TESTCASE(termlist4, backend) {
01631     Xapian::Database db(get_database("apitest_onedoc"));
01632     Xapian::TermIterator i = db.termlist_begin(1);
01633     i.skip_to("");
01634     i.skip_to("\xff");
01635     return true;
01636 }
01637 
01638 // tests punctuation is OK in terms (particularly in remote queries)
01639 DEFINE_TESTCASE(puncterms1, backend) {
01640     Xapian::Database db(get_database("apitest_punc"));
01641     Xapian::Enquire enquire(db);
01642 
01643     Xapian::Query q1("semi;colon");
01644     enquire.set_query(q1);
01645     Xapian::MSet m1 = enquire.get_mset(0, 10);
01646 
01647     Xapian::Query q2("col:on");
01648     enquire.set_query(q2);
01649     Xapian::MSet m2 = enquire.get_mset(0, 10);
01650 
01651     Xapian::Query q3("com,ma");
01652     enquire.set_query(q3);
01653     Xapian::MSet m3 = enquire.get_mset(0, 10);
01654 
01655     return true;
01656 }
01657 
01658 // test that searching for a term with a space or backslash in it works
01659 DEFINE_TESTCASE(spaceterms1, backend) {
01660     Xapian::Enquire enquire(get_database("apitest_space"));
01661     Xapian::MSet mymset;
01662     Xapian::doccount count;
01663     Xapian::MSetIterator m;
01664     Xapian::Stem stemmer("english");
01665 
01666     enquire.set_query(stemmer("space man"));
01667     mymset = enquire.get_mset(0, 10);
01668     TEST_MSET_SIZE(mymset, 1);
01669     count = 0;
01670     for (m = mymset.begin(); m != mymset.end(); ++m) ++count;
01671     TEST_EQUAL(count, 1);
01672 
01673     for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
01674         TEST_NOT_EQUAL(mymset.begin().get_document().get_data(), "");
01675         TEST_NOT_EQUAL(mymset.begin().get_document().get_value(value_no), "");
01676     }
01677 
01678     enquire.set_query(stemmer("tab\tby"));
01679     mymset = enquire.get_mset(0, 10);
01680     TEST_MSET_SIZE(mymset, 1);
01681     count = 0;
01682     for (m = mymset.begin(); m != mymset.end(); ++m) ++count;
01683     TEST_EQUAL(count, 1);
01684 
01685     for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
01686         string value = mymset.begin().get_document().get_value(value_no);
01687         TEST_NOT_EQUAL(value, "");
01688         if (value_no == 0) {
01689             TEST(value.size() > 262);
01690             TEST_EQUAL(static_cast<unsigned char>(value[261]), 255);
01691         }
01692     }
01693 
01694     enquire.set_query(stemmer("back\\slash"));
01695     mymset = enquire.get_mset(0, 10);
01696     TEST_MSET_SIZE(mymset, 1);
01697     count = 0;
01698     for (m = mymset.begin(); m != mymset.end(); ++m) ++count;
01699     TEST_EQUAL(count, 1);
01700 
01701     return true;
01702 }
01703 
01704 // test that XOR queries work
01705 DEFINE_TESTCASE(xor1, backend) {
01706     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01707     enquire.set_query(Xapian::Query("this"));
01708     Xapian::Stem stemmer("english");
01709 
01710     vector<string> terms;
01711     terms.push_back(stemmer("this"));
01712     terms.push_back(stemmer("word"));
01713     terms.push_back(stemmer("of"));
01714 
01715     Xapian::Query query(Xapian::Query::OP_XOR, terms.begin(), terms.end());
01716     enquire.set_weighting_scheme(Xapian::BoolWeight());
01717     enquire.set_query(query);
01718 
01719     Xapian::MSet mymset = enquire.get_mset(0, 10);
01720     mset_expect_order(mymset, 1, 2, 5, 6);
01721 
01722     return true;
01723 }
01724 
01725 // test Xapian::Database::get_document()
01726 DEFINE_TESTCASE(getdoc1, backend) {
01727     Xapian::Database db(get_database("apitest_onedoc"));
01728     Xapian::Document doc(db.get_document(1));
01729     TEST_EXCEPTION(Xapian::InvalidArgumentError, db.get_document(0));
01730     TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(999999999));
01731     TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(123456789));
01732     TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(3));
01733     TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(2));
01734     // Check that Document works as a handle on modification
01735     // (this was broken for the first try at Xapian::Document prior to 0.7).
01736     Xapian::Document doc2 = doc;
01737     doc.set_data("modified!");
01738     TEST_EQUAL(doc.get_data(), "modified!");
01739     TEST_EQUAL(doc.get_data(), doc2.get_data());
01740     return true;
01741 }
01742 
01743 // test whether operators with no elements work as a null query
01744 DEFINE_TESTCASE(emptyop1, backend) {
01745     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01746     vector<Xapian::Query> nullvec;
01747 
01748     Xapian::Query query1(Xapian::Query::OP_XOR, nullvec.begin(), nullvec.end());
01749 
01750     enquire.set_query(query1);
01751     Xapian::MSet mymset = enquire.get_mset(0, 10);
01752     TEST_MSET_SIZE(mymset, 0);
01753     TEST_EXCEPTION(Xapian::InvalidArgumentError, enquire.get_matching_terms_begin(1));
01754 
01755     return true;
01756 }
01757 
01758 // Regression test for check_at_least SEGV when there are no matches.
01759 DEFINE_TESTCASE(checkatleast1, backend) {
01760     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01761     enquire.set_query(Xapian::Query("thom"));
01762     Xapian::MSet mymset = enquire.get_mset(0, 10, 11);
01763     TEST_EQUAL(0, mymset.size());
01764 
01765     return true;
01766 }
01767 
01768 // Regression test - if check_at_least was set we returned (check_at_least - 1)
01769 // results, rather than the requested msize.  Fixed in 1.0.2.
01770 DEFINE_TESTCASE(checkatleast2, backend) {
01771     Xapian::Enquire enquire(get_database("apitest_simpledata"));
01772     enquire.set_query(Xapian::Query("paragraph"));
01773 
01774     Xapian::MSet mymset = enquire.get_mset(0, 3, 10);
01775     TEST_MSET_SIZE(mymset, 3);
01776     TEST_EQUAL(mymset.get_matches_lower_bound(), 5);
01777 
01778     mymset = enquire.get_mset(0, 2, 4);
01779     TEST_MSET_SIZE(mymset, 2);
01780     TEST_GREATER_OR_EQUAL(mymset.get_matches_lower_bound(), 4);
01781 
01782     return true;
01783 }
01784 
01785 // Feature tests - check_at_least with various sorting options.
01786 DEFINE_TESTCASE(checkatleast3, backend) {
01787     Xapian::Enquire enquire(get_database("etext"));
01788     enquire.set_query(Xapian::Query("prussian")); // 60 matches.
01789 
01790     for (int order = 0; order < 3; ++order) {
01791         switch (order) {
01792             case 0:
01793                 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
01794                 break;
01795             case 1:
01796                 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
01797                 break;
01798             case 2:
01799                 enquire.set_docid_order(Xapian::Enquire::DONT_CARE);
01800                 break;
01801         }
01802 
01803         for (int sort = 0; sort < 7; ++sort) {
01804             bool reverse = (sort & 1);
01805             switch (sort) {
01806                 case 0:
01807                     enquire.set_sort_by_relevance();
01808                     break;
01809                 case 1: case 2:
01810                     enquire.set_sort_by_value(0, reverse);
01811                     break;
01812                 case 3: case 4:
01813                     enquire.set_sort_by_value_then_relevance(0, reverse);
01814                     break;
01815                 case 5: case 6:
01816                     enquire.set_sort_by_relevance_then_value(0, reverse);
01817                     break;
01818             }
01819 
01820             Xapian::MSet mset = enquire.get_mset(0, 100, 500);
01821             TEST_MSET_SIZE(mset, 60);
01822             TEST_EQUAL(mset.get_matches_lower_bound(), 60);
01823             TEST_EQUAL(mset.get_matches_estimated(), 60);
01824             TEST_EQUAL(mset.get_matches_upper_bound(), 60);
01825 
01826             mset = enquire.get_mset(0, 50, 100);
01827             TEST_MSET_SIZE(mset, 50);
01828             TEST_EQUAL(mset.get_matches_lower_bound(), 60);
01829             TEST_EQUAL(mset.get_matches_estimated(), 60);
01830             TEST_EQUAL(mset.get_matches_upper_bound(), 60);
01831 
01832             mset = enquire.get_mset(0, 10, 50);
01833             TEST_MSET_SIZE(mset, 10);
01834             TEST(mset.get_matches_lower_bound() >= 50);
01835         }
01836     }
01837 
01838     return true;
01839 }
01840 
01841 // tests all document postlists
01842 DEFINE_TESTCASE(allpostlist1, backend) {
01843     Xapian::Database db(get_database("apitest_manydocs"));
01844     Xapian::PostingIterator i = db.postlist_begin("");
01845     unsigned int j = 1;
01846     while (i != db.postlist_end("")) {
01847         TEST_EQUAL(*i, j);
01848         i++;
01849         j++;
01850     }
01851     TEST_EQUAL(j, 513);
01852 
01853     i = db.postlist_begin("");
01854     j = 1;
01855     while (i != db.postlist_end("")) {
01856         TEST_EQUAL(*i, j);
01857         i++;
01858         j++;
01859         if (j == 50) {
01860             j += 10;
01861             i.skip_to(j);
01862         }
01863     }
01864     TEST_EQUAL(j, 513);
01865 
01866     return true;
01867 }
01868 
01869 static void test_emptyterm1_helper(Xapian::Database & db)
01870 {
01871     // Don't bother with postlist_begin() because allpostlist tests cover that.
01872     TEST_EXCEPTION(Xapian::InvalidArgumentError, db.positionlist_begin(1, ""));
01873     TEST_EQUAL(db.get_doccount(), db.get_termfreq(""));
01874     TEST_EQUAL(db.get_doccount() != 0, db.term_exists(""));
01875     TEST_EQUAL(db.get_doccount(), db.get_collection_freq(""));
01876 }
01877 
01878 // tests results of passing an empty term to various methods
01879 DEFINE_TESTCASE(emptyterm1, backend) {
01880     Xapian::Database db(get_database("apitest_manydocs"));
01881     TEST_EQUAL(db.get_doccount(), 512);
01882     test_emptyterm1_helper(db);
01883 
01884     db = get_database("apitest_onedoc");
01885     TEST_EQUAL(db.get_doccount(), 1);
01886     test_emptyterm1_helper(db);
01887 
01888     db = get_database("");
01889     TEST_EQUAL(db.get_doccount(), 0);
01890     test_emptyterm1_helper(db);
01891 
01892     return true;
01893 }
01894 
01895 // Feature test for Query::OP_VALUE_RANGE.
01896 DEFINE_TESTCASE(valuerange1, backend) {
01897     Xapian::Database db(get_database("apitest_phrase"));
01898     Xapian::Enquire enq(db);
01899     static const char * vals[] = {
01900         "", " ", "a", "aa", "abcd", "e", "g", "h", "hzz", "i", "l", "z", NULL
01901     };
01902     for (const char **start = vals; *start; ++start) {
01903         for (const char **end = vals; *end; ++end) {
01904             Xapian::Query query(Xapian::Query::OP_VALUE_RANGE, 1, *start, *end);
01905             enq.set_query(query);
01906             Xapian::MSet mset = enq.get_mset(0, 20);
01907             // Check that documents in the MSet match the value range filter.
01908             set<Xapian::docid> matched;
01909             Xapian::MSetIterator i;
01910             for (i = mset.begin(); i != mset.end(); ++i) {
01911                 matched.insert(*i);
01912                 string value = db.get_document(*i).get_value(1);
01913                 tout << "'" << *start << "' <= '" << value << "' <= '" << *end << "'" << endl;
01914                 TEST(value >= *start);
01915                 TEST(value <= *end);
01916             }
01917             // Check that documents not in the MSet don't match the value range filter.
01918             for (Xapian::docid j = db.get_lastdocid(); j != 0; --j) {
01919                 if (matched.find(j) == matched.end()) {
01920                     string value = db.get_document(j).get_value(1);
01921                     tout << value << " < '" << *start << "' or > '" << *end << "'" << endl;
01922                     TEST(value < *start || value > *end);
01923                 }
01924             }
01925         }
01926     }
01927     return true;
01928 }
01929 
01930 // Regression test for Query::OP_VALUE_LE - used to return document IDs for
01931 // non-existent documents.
01932 DEFINE_TESTCASE(valuerange2, writable) {
01933     Xapian::WritableDatabase db = get_writable_database();
01934     Xapian::Document doc;
01935     doc.set_data("5");
01936     doc.add_value(0, "5");
01937     db.replace_document(5, doc);
01938     Xapian::Enquire enq(db);
01939 
01940     Xapian::Query query(Xapian::Query::OP_VALUE_LE, 0, "6");
01941     enq.set_query(query);
01942     Xapian::MSet mset = enq.get_mset(0, 20);
01943 
01944     TEST_EQUAL(mset.size(), 1);
01945     TEST_EQUAL(*(mset[0]), 5);
01946     return true;
01947 }
01948 
01949 // Test for alldocs postlist with a sparse database.
01950 DEFINE_TESTCASE(alldocspl1, writable) {
01951     Xapian::WritableDatabase db = get_writable_database();
01952     Xapian::Document doc;
01953     doc.set_data("5");
01954     doc.add_value(0, "5");
01955     db.replace_document(5, doc);
01956 
01957     Xapian::PostingIterator i = db.postlist_begin("");
01958     TEST(i != db.postlist_end(""));
01959     TEST_EQUAL(*i, 5);
01960     TEST_EQUAL(i.get_doclength(), 0);
01961     TEST_EQUAL(i.get_wdf(), 1);
01962     ++i;
01963     TEST(i == db.postlist_end(""));
01964 
01965     return true;
01966 }
01967 
01968 // Test reading and writing a modified alldocspostlist.
01969 DEFINE_TESTCASE(alldocspl2, writable) {
01970     Xapian::PostingIterator i, end;
01971     {
01972         Xapian::WritableDatabase db = get_writable_database();
01973         Xapian::Document doc;
01974         doc.set_data("5");
01975         doc.add_value(0, "5");
01976         db.replace_document(5, doc);
01977 
01978         // Test iterating before flushing the changes.
01979         i = db.postlist_begin("");
01980         end = db.postlist_end("");
01981         TEST(i != end);
01982         TEST_EQUAL(*i, 5);
01983         TEST_EQUAL(i.get_doclength(), 0);
01984         TEST_EQUAL(i.get_wdf(), 1);
01985         ++i;
01986         TEST(i == end);
01987 
01988         db.flush();
01989 
01990         // Test iterating after flushing the changes.
01991         i = db.postlist_begin("");
01992         end = db.postlist_end("");
01993         TEST(i != end);
01994         TEST_EQUAL(*i, 5);
01995         TEST_EQUAL(i.get_doclength(), 0);
01996         TEST_EQUAL(i.get_wdf(), 1);
01997         ++i;
01998         TEST(i == end);
01999 
02000         // Add another document.
02001         doc = Xapian::Document();
02002         doc.set_data("5");
02003         doc.add_value(0, "7");
02004         db.replace_document(7, doc);
02005 
02006         // Test iterating through before flushing the changes.
02007         i = db.postlist_begin("");
02008         end = db.postlist_end("");
02009         TEST(i != end);
02010         TEST_EQUAL(*i, 5);
02011         TEST_EQUAL(i.get_doclength(), 0);
02012         TEST_EQUAL(i.get_wdf(), 1);
02013         ++i;
02014         TEST(i != end);
02015         TEST_EQUAL(*i, 7);
02016         TEST_EQUAL(i.get_doclength(), 0);
02017         TEST_EQUAL(i.get_wdf(), 1);
02018         ++i;
02019         TEST(i == end);
02020 
02021         // Delete the first document.
02022         db.delete_document(5);
02023 
02024         // Test iterating through before flushing the changes.
02025         i = db.postlist_begin("");
02026         end = db.postlist_end("");
02027         TEST(i != end);
02028         TEST_EQUAL(*i, 7);
02029         TEST_EQUAL(i.get_doclength(), 0);
02030         TEST_EQUAL(i.get_wdf(), 1);
02031         ++i;
02032         TEST(i == end);
02033 
02034         // Test iterating through after flushing the changes, and dropping the reference to the main DB.
02035         db.flush();
02036         i = db.postlist_begin("");
02037         end = db.postlist_end("");
02038     }
02039 
02040     TEST(i != end);
02041     TEST_EQUAL(*i, 7);
02042     TEST_EQUAL(i.get_doclength(), 0);
02043     TEST_EQUAL(i.get_wdf(), 1);
02044     ++i;
02045     TEST(i == end);
02046 
02047     return true;
02048 }
02049 
02050 // Feature test for Query::OP_VALUE_GE.
02051 DEFINE_TESTCASE(valuege1, backend) {
02052     Xapian::Database db(get_database("apitest_phrase"));
02053     Xapian::Enquire enq(db);
02054     static const char * vals[] = {
02055         "", " ", "a", "aa", "abcd", "e", "g", "h", "hzz", "i", "l", "z", NULL
02056     };
02057     for (const char **start = vals; *start; ++start) {
02058         Xapian::Query query(Xapian::Query::OP_VALUE_GE, 1, *start);
02059         enq.set_query(query);
02060         Xapian::MSet mset = enq.get_mset(0, 20);
02061         // Check that documents in the MSet match the value range filter.
02062         set<Xapian::docid> matched;
02063         Xapian::MSetIterator i;
02064         for (i = mset.begin(); i != mset.end(); ++i) {
02065             matched.insert(*i);
02066             string value = db.get_document(*i).get_value(1);
02067             tout << "'" << *start << "' <= '" << value << "'" << endl;
02068             TEST(value >= *start);
02069         }
02070         // Check that documents not in the MSet don't match the value range filter.
02071         for (Xapian::docid j = db.get_lastdocid(); j != 0; --j) {
02072             if (matched.find(j) == matched.end()) {
02073                 string value = db.get_document(j).get_value(1);
02074                 tout << value << " < '" << *start << "'" << endl;
02075                 TEST(value < *start);
02076             }
02077         }
02078     }
02079     return true;
02080 }
02081 
02082 // Feature test for Query::OP_VALUE_LE.
02083 DEFINE_TESTCASE(valuele1, backend) {
02084     Xapian::Database db(get_database("apitest_phrase"));
02085     Xapian::Enquire enq(db);
02086     static const char * vals[] = {
02087         "", " ", "a", "aa", "abcd", "e", "g", "h", "hzz", "i", "l", "z", NULL
02088     };
02089     for (const char **end = vals; *end; ++end) {
02090         Xapian::Query query(Xapian::Query::OP_VALUE_LE, 1, *end);
02091         enq.set_query(query);
02092         Xapian::MSet mset = enq.get_mset(0, 20);
02093         // Check that documents in the MSet match the value range filter.
02094         set<Xapian::docid> matched;
02095         Xapian::MSetIterator i;
02096         for (i = mset.begin(); i != mset.end(); ++i) {
02097             matched.insert(*i);
02098             string value = db.get_document(*i).get_value(1);
02099             tout << "'" << *end << "' <= '" << value << "'" << endl;
02100             TEST(value <= *end);
02101         }
02102         // Check that documents not in the MSet don't match the value range filter.
02103         for (Xapian::docid j = db.get_lastdocid(); j != 0; --j) {
02104             if (matched.find(j) == matched.end()) {
02105                 string value = db.get_document(j).get_value(1);
02106                 tout << value << " < '" << *end << "'" << endl;
02107                 TEST(value > *end);
02108             }
02109         }
02110     }
02111     return true;
02112 }
02113 
02114 // Feature test for Query::OP_SCALE_WEIGHT.
02115 DEFINE_TESTCASE(scaleweight1, backend) {
02116     Xapian::Database db(get_database("apitest_phrase"));
02117     Xapian::Enquire enq(db);
02118     Xapian::QueryParser qp;
02119 
02120     static const char * queries[] = {
02121         "pad",
02122         "milk fridge",
02123         "leave milk on fridge",
02124         "ordered milk operator",
02125         "ordered phrase operator",
02126         "leave \"milk on fridge\"",
02127         "notpresent",
02128         "leave \"milk notpresent\"",
02129         NULL
02130     };
02131     static double multipliers[] = {
02132         -1000000, -2.5, -1, -0.5, 0, 0.5, 1, 2.5, 1000000,
02133         0, 0
02134     };
02135 
02136     for (const char **qstr = queries; *qstr; ++qstr) {
02137         Xapian::Query query1 = qp.parse_query(*qstr);
02138         tout << "query1: " << query1.get_description() << endl;
02139         for (double *multp = multipliers; multp[0] != multp[1]; ++multp) {
02140             double mult = *multp;
02141             if (mult < 0) {
02142                 TEST_EXCEPTION(Xapian::InvalidArgumentError,
02143                                Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT,
02144                                              query1, mult));
02145                 continue;
02146             }
02147             Xapian::Query query2(Xapian::Query::OP_SCALE_WEIGHT, query1, mult);
02148             tout << "query2: " << query2.get_description() << endl;
02149 
02150             enq.set_query(query1);
02151             Xapian::MSet mset1 = enq.get_mset(0, 20);
02152             enq.set_query(query2);
02153             Xapian::MSet mset2 = enq.get_mset(0, 20);
02154 
02155             TEST_EQUAL(mset1.size(), mset2.size());
02156 
02157             Xapian::MSetIterator i1, i2;
02158             if (mult > 0) {
02159                 for (i1 = mset1.begin(), i2 = mset2.begin();
02160                      i1 != mset1.end() && i2 != mset2.end(); ++i1, ++i2) {
02161                     TEST_EQUAL_DOUBLE(i1.get_weight() * mult, i2.get_weight());
02162                     TEST_EQUAL(*i1, *i2);
02163                 }
02164             } else {
02165                 // Weights in mset2 are 0; so it should be sorted by docid.
02166                 vector<Xapian::docid> ids1;
02167                 vector<Xapian::docid> ids2;
02168                 for (i1 = mset1.begin(), i2 = mset2.begin();
02169                      i1 != mset1.end() && i2 != mset2.end(); ++i1, ++i2) {
02170                     TEST_NOT_EQUAL_DOUBLE(i1.get_weight(), 0);
02171                     TEST_EQUAL_DOUBLE(i2.get_weight(), 0);
02172                     ids1.push_back(*i1);
02173                     ids2.push_back(*i2);
02174                 }
02175                 sort(ids1.begin(), ids1.end());
02176                 TEST_EQUAL(ids1, ids2);
02177             }
02178         }
02179     }
02180     return true;
02181 }
02182 
02183 // Test Query::OP_SCALE_WEIGHT being used to multiply some of the weights of a
02184 // search by zero.
02185 DEFINE_TESTCASE(scaleweight2, backend) {
02186     Xapian::Database db(get_database("apitest_phrase"));
02187     Xapian::Enquire enq(db);
02188     Xapian::MSetIterator i;
02189 
02190     Xapian::Query query1("fridg");
02191     Xapian::Query query2(Xapian::Query::OP_SCALE_WEIGHT, query1, 2.5);
02192     Xapian::Query query3("milk");
02193     Xapian::Query query4(Xapian::Query::OP_SCALE_WEIGHT, query3, 0);
02194     Xapian::Query query5(Xapian::Query::OP_OR, query2, query4);
02195 
02196     // query5 should first return the same results as query1, in the same
02197     // order, and then return the results of query3 which aren't also results
02198     // of query1, in ascending docid order.  We test that this happens.
02199 
02200     // First, build a vector of docids matching the first part of the query,
02201     // and append the non-duplicate docids matching the second part of the
02202     // query.
02203     vector<Xapian::docid> ids1;
02204     set<Xapian::docid> idsin1;
02205     vector<Xapian::docid> ids3;
02206 
02207     enq.set_query(query1);
02208     Xapian::MSet mset1 = enq.get_mset(0, 20);
02209     enq.set_query(query3);
02210     Xapian::MSet mset3 = enq.get_mset(0, 20);
02211     TEST_NOT_EQUAL(mset1.size(), 0);
02212     for (i = mset1.begin(); i != mset1.end(); ++i) {
02213         ids1.push_back(*i);
02214         idsin1.insert(*i);
02215     }
02216     TEST_NOT_EQUAL(mset3.size(), 0);
02217     for (i = mset3.begin(); i != mset3.end(); ++i) {
02218         if (idsin1.find(*i) != idsin1.end())
02219             continue;
02220         ids3.push_back(*i);
02221     }
02222     sort(ids3.begin(), ids3.end());
02223     ids1.insert(ids1.end(), ids3.begin(), ids3.end());
02224 
02225     // Now, run the combined query and build a vector of the matching docids.
02226     vector<Xapian::docid> ids5;
02227     enq.set_query(query5);
02228     Xapian::MSet mset5 = enq.get_mset(0, 20);
02229     for (i = mset5.begin(); i != mset5.end(); ++i) {
02230         ids5.push_back(*i);
02231     }
02232 
02233     TEST_EQUAL(ids1, ids5);
02234     return true;
02235 }
02236 
02237 // Regression test for bug fixed in 1.0.5 - this test would failed under
02238 // valgrind because it used an uninitialised value.
02239 DEFINE_TESTCASE(bm25weight1, backend) {
02240     Xapian::Enquire enquire(get_database("apitest_simpledata"));
02241     enquire.set_weighting_scheme(Xapian::BM25Weight(1, 25, 1, 0.01, 0.5));
02242     enquire.set_query(Xapian::Query("word") );
02243 
02244     Xapian::MSet mset = enquire.get_mset(0, 25);
02245 
02246     return true;
02247 }
02248 
02249 // Feature test for TradWeight.
02250 DEFINE_TESTCASE(tradweight1, backend) {
02251     Xapian::Enquire enquire(get_database("apitest_simpledata"));
02252     enquire.set_weighting_scheme(Xapian::TradWeight());
02253     enquire.set_query(Xapian::Query("word") );
02254 
02255     Xapian::MSet mset = enquire.get_mset(0, 25);
02256 
02257     enquire.set_weighting_scheme(Xapian::TradWeight(0));
02258     enquire.set_query(Xapian::Query("word") );
02259 
02260     mset = enquire.get_mset(0, 25);
02261     // FIXME: should check that TradWeight(0) means wdf and doc length really
02262     // don't affect the weights as stated in the documentation.
02263 
02264     return true;
02265 }

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