xapian-core  1.4.26
api_nodb.cc
Go to the documentation of this file.
1 
4 /* Copyright 1999,2000,2001 BrightStation PLC
5  * Copyright 2002 Ananova Ltd
6  * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2015,2016,2017,2019 Olly Betts
7  * Copyright 2006 Lemur Consulting Ltd
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22  * USA
23  */
24 
25 #include <config.h>
26 
27 #include "api_nodb.h"
28 
29 #include <xapian.h>
30 
31 #include "apitest.h"
32 #include "testsuite.h"
33 #include "testutils.h"
34 
35 #include <list>
36 #include <string>
37 #include <vector>
38 
39 using namespace std;
40 
41 // tests that get_query_terms() returns the terms in the right order
42 DEFINE_TESTCASE(getqterms1, !backend) {
43  list<string> answers_list;
44  answers_list.push_back("one");
45  answers_list.push_back("two");
46  answers_list.push_back("three");
47  answers_list.push_back("four");
48 
51  Xapian::Query("one", 1, 1),
52  Xapian::Query("three", 1, 3)),
54  Xapian::Query("four", 1, 4),
55  Xapian::Query("two", 1, 2)));
56 
57  list<string> list1;
58  {
60  for (t = myquery.get_terms_begin(); t != myquery.get_terms_end(); ++t)
61  list1.push_back(*t);
62  }
63  TEST(list1 == answers_list);
64  list<string> list2(myquery.get_terms_begin(), myquery.get_terms_end());
65  TEST(list2 == answers_list);
66 }
67 
68 // tests that get_query_terms() doesn't SEGV on an empty query
69 // (regression test for bug in 0.9.0)
70 DEFINE_TESTCASE(getqterms2, !backend) {
71  Xapian::Query empty_query;
72  TEST_EQUAL(empty_query.get_terms_begin(), empty_query.get_terms_end());
73  TEST_EQUAL(empty_query.get_unique_terms_begin(),
74  empty_query.get_unique_terms_end());
75 }
76 
77 // tests that empty queries work correctly
78 DEFINE_TESTCASE(emptyquery2, !backend) {
79  // test that Query::empty() is true for an empty query.
80  TEST(Xapian::Query().empty());
81  // test that an empty query has length 0
82  TEST(Xapian::Query().get_length() == 0);
83  vector<Xapian::Query> v;
84  TEST(Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end()).empty());
85  TEST(Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end()).get_length() == 0);
86 }
87 
89 DEFINE_TESTCASE(emptyquery3, !backend) {
90  static const Xapian::Query::op ops[] = {
96  };
97 
98  for (size_t i = 0; i < sizeof(ops) / sizeof(ops[0]); ++i) {
99  tout << "Testing op #" << i << '\n';
100  Xapian::Query empty;
101  Xapian::Query q("test");
102  Xapian::Query qcombine(ops[i], empty, q);
103  tout << qcombine.get_description() << '\n';
104  Xapian::Query qcombine2(ops[i], q, empty);
105  tout << qcombine2.get_description() << '\n';
106  Xapian::Query qcombine3(ops[i], empty, empty);
107  tout << qcombine3.get_description() << '\n';
108  }
109 }
110 
111 // tests that query lengths are calculated correctly
112 DEFINE_TESTCASE(querylen1, !backend) {
113  // test that a simple query has the right length
114  Xapian::Query myquery;
116  Xapian::Query("foo"),
117  Xapian::Query("bar"));
119  myquery,
121  Xapian::Query("wibble"),
122  Xapian::Query("spoon")));
123 
124  TEST_EQUAL(myquery.get_length(), 4);
125  TEST(!myquery.empty());
126 }
127 
128 // tests that query lengths are calculated correctly
129 DEFINE_TESTCASE(querylen2, !backend) {
130  // test with an even bigger and strange query
131  string terms[3] = {
132  "foo",
133  "bar",
134  "baz"
135  };
136  Xapian::Query queries[3] = {
137  Xapian::Query("wibble"),
138  Xapian::Query("wobble"),
139  Xapian::Query(Xapian::Query::OP_OR, string("jelly"), string("belly"))
140  };
141 
142  Xapian::Query myquery;
143  vector<string> v1(terms, terms + 3);
144  vector<Xapian::Query> v2(queries, queries + 3);
145  vector<Xapian::Query *> v3;
146  Xapian::Query query1(Xapian::Query::OP_AND, string("ball"), string("club"));
147  Xapian::Query query2("ring");
148  v3.push_back(&query1);
149  v3.push_back(&query2);
150 
151  Xapian::Query myq1 = Xapian::Query(Xapian::Query::OP_AND, v1.begin(), v1.end());
152  tout << "myq1=" << myq1 << "\n";
153  TEST_EQUAL(myq1.get_length(), 3);
154 
155  Xapian::Query myq2_1 = Xapian::Query(Xapian::Query::OP_OR, v2.begin(), v2.end());
156  tout << "myq2_1=" << myq2_1 << "\n";
157  TEST_EQUAL(myq2_1.get_length(), 4);
158 
159  Xapian::Query myq2_2 = Xapian::Query(Xapian::Query::OP_AND, v3.begin(), v3.end());
160  tout << "myq2_2=" << myq2_2 << "\n";
161  TEST_EQUAL(myq2_2.get_length(), 3);
162 
163  Xapian::Query myq2 = Xapian::Query(Xapian::Query::OP_OR, myq2_1, myq2_2);
164  tout << "myq2=" << myq2 << "\n";
165  TEST_EQUAL(myq2.get_length(), 7);
166 
167  myquery = Xapian::Query(Xapian::Query::OP_OR, myq1, myq2);
168  tout << "myquery=" << myquery << "\n";
169  TEST_EQUAL(myquery.get_length(), 10);
170 }
171 
178 DEFINE_TESTCASE(dontflattensubqueries1, !backend) {
179  Xapian::Query queries1[3] = {
180  Xapian::Query("wibble"),
181  Xapian::Query("wobble"),
182  Xapian::Query(Xapian::Query::OP_OR, string("jelly"), string("belly"))
183  };
184 
185  Xapian::Query queries2[3] = {
186  Xapian::Query(Xapian::Query::OP_AND, string("jelly"), string("belly")),
187  Xapian::Query("wibble"),
188  Xapian::Query("wobble")
189  };
190 
191  vector<Xapian::Query> vec1(queries1, queries1 + 3);
192  Xapian::Query myquery1(Xapian::Query::OP_OR, vec1.begin(), vec1.end());
193  TEST_EQUAL(myquery1.get_description(),
194  "Query((wibble OR wobble OR (jelly OR belly)))");
195 
196  vector<Xapian::Query> vec2(queries2, queries2 + 3);
197  Xapian::Query myquery2(Xapian::Query::OP_AND, vec2.begin(), vec2.end());
198  TEST_EQUAL(myquery2.get_description(),
199  "Query(((jelly AND belly) AND wibble AND wobble))");
200 }
201 
202 // test behaviour when creating a query from an empty vector
203 DEFINE_TESTCASE(emptyquerypart1, !backend) {
204  vector<string> emptyterms;
205  Xapian::Query query(Xapian::Query::OP_OR, emptyterms.begin(), emptyterms.end());
207  TEST(Xapian::Query(Xapian::Query::OP_AND, query, Xapian::Query("x")).get_length() == 0);
209  TEST(Xapian::Query(Xapian::Query::OP_OR, query, Xapian::Query("x")).get_length() == 1);
210 }
211 
212 DEFINE_TESTCASE(stemlangs1, !backend) {
213  string langs = Xapian::Stem::get_available_languages();
214  tout << "available languages '" << langs << "'\n";
215  TEST(!langs.empty());
216 
217  // Also test the language codes.
218  langs += " ar hy eu ca da nl en fi fr de hu id ga it lt ne nb nn no pt ro"
219  " ru es sv ta tr";
220 
221  string::size_type i = 0;
222  while (true) {
223  string::size_type spc = langs.find(' ', i);
224  // The only spaces in langs should be a single one between each pair
225  // of language names.
226  TEST_NOT_EQUAL(i, spc);
227 
228  // Try making a stemmer for this language. We should be able to create
229  // it without an exception being thrown.
230  string language(langs, i, spc - i);
231  tout << "checking language code '" << language << "' works\n";
232  Xapian::Stem stemmer(language);
233  TEST(!stemmer.is_none());
234  if (language.size() > 2) {
235  string expected("Xapian::Stem(");
236  expected += language;
237  expected += ')';
238  TEST_EQUAL(stemmer.get_description(), expected);
239  }
240 
241  if (spc == string::npos) break;
242  i = spc + 1;
243  }
244 
245  {
246  // Stem("none") should give a no-op stemmer.
247  Xapian::Stem stem_nothing = Xapian::Stem("none");
248  TEST(stem_nothing.is_none());
249  TEST_EQUAL(stem_nothing.get_description(), "Xapian::Stem(none)");
250  }
251 
252  {
253  // Stem("") should be equivalent.
254  Xapian::Stem stem_nothing = Xapian::Stem("");
255  TEST(stem_nothing.is_none());
256  TEST_EQUAL(stem_nothing.get_description(), "Xapian::Stem(none)");
257  }
258 }
259 
260 // Regression test.
261 DEFINE_TESTCASE(nosuchdb1, !backend) {
262  // This is a "nodb" test because it doesn't test a particular backend.
263  try {
264  Xapian::Database db("NOsuChdaTabASe");
265  FAIL_TEST("Managed to open 'NOsuChdaTabASe'");
266  } catch (const Xapian::DatabaseOpeningError & e) {
267  // We don't really require this exact message, but in Xapian <= 1.1.0
268  // this gave "Couldn't detect type of database".
269  TEST_STRINGS_EQUAL(e.get_msg(), "Couldn't stat 'NOsuChdaTabASe'");
270  }
271 
272  try {
273  Xapian::Database::check("NOsuChdaTabASe");
274  FAIL_TEST("Managed to check 'NOsuChdaTabASe'");
275  } catch (const Xapian::DatabaseOpeningError & e) {
276  // In 1.4.3 and earlier, this threw DatabaseError with the message:
277  // "File is not a Xapian database or database table" (confusing as
278  // there is no file).
280  "Couldn't find Xapian database or table to check");
281  }
282 }
283 
284 // Feature tests for value manipulations.
285 DEFINE_TESTCASE(addvalue1, !backend) {
286  // Regression test for add_value on an existing value (bug#82).
287  Xapian::Document doc;
288  doc.add_value(1, "original");
289  doc.add_value(1, "replacement");
290  TEST_EQUAL(doc.get_value(1), "replacement");
291 
292  doc.add_value(2, "too");
293  doc.add_value(3, "free");
294  doc.add_value(4, "for");
295 
296  doc.remove_value(2);
297  doc.remove_value(4);
298  TEST_EQUAL(doc.get_value(0), "");
299  TEST_EQUAL(doc.get_value(1), "replacement");
300  TEST_EQUAL(doc.get_value(2), "");
301  TEST_EQUAL(doc.get_value(3), "free");
302  TEST_EQUAL(doc.get_value(4), "");
303 }
304 
305 // tests that the collapsing on termpos optimisation gives correct query length
306 DEFINE_TESTCASE(poscollapse2, !backend) {
307  Xapian::Query q(Xapian::Query::OP_OR, Xapian::Query("this", 1, 1), Xapian::Query("this", 1, 1));
308  TEST_EQUAL(q.get_length(), 2);
309 }
310 
311 // regression test of querying an uninitialised database: should report an
312 // error; used to segfault with 1.0.0.
313 DEFINE_TESTCASE(uninitdb1, !backend) {
314  Xapian::Database db;
316  Xapian::Enquire enq(db));
317 }
318 
319 // Test a scaleweight query applied to a match nothing query
320 DEFINE_TESTCASE(scaleweight3, !backend) {
323  TEST_EQUAL(query.get_description(), "Query()");
324 }
325 
326 // Regression test - before 1.1.0, you could add docid 0 to an RSet.
327 DEFINE_TESTCASE(rset3, !backend) {
328  Xapian::RSet rset;
330  TEST(rset.empty());
331  TEST_EQUAL(rset.size(), 0);
332  rset.add_document(1);
333  rset.add_document(static_cast<Xapian::docid>(-1));
335  TEST(!rset.empty());
336  TEST_EQUAL(rset.size(), 2);
337 }
338 
339 // Regression test - RSet::get_description() gave a malformed answer in 1.0.7.
340 DEFINE_TESTCASE(rset4, !backend) {
341  Xapian::RSet rset;
342  rset.add_document(1);
343  // In 1.0.7 this gave: RSet(RSet(RSet::Internal(, 1))
344  TEST_STRINGS_EQUAL(rset.get_description(), "RSet(RSet::Internal(1))");
345 }
346 
347 // Direct test of ValueSetMatchDecider
348 DEFINE_TESTCASE(valuesetmatchdecider1, !backend) {
349  Xapian::ValueSetMatchDecider vsmd1(0, true);
350  vsmd1.add_value("42");
351  Xapian::ValueSetMatchDecider vsmd2(0, false);
352  vsmd2.remove_value("nosuch"); // Test removing a value which isn't present.
353  vsmd2.add_value("42");
354  Xapian::ValueSetMatchDecider vsmd3(0, true);
355  vsmd3.add_value("42");
356  vsmd3.add_value("blah");
357 
358  Xapian::Document doc;
359  TEST(!vsmd1(doc));
360  TEST(vsmd2(doc));
361  TEST(!vsmd3(doc));
362  doc.add_value(0, "42");
363  TEST(vsmd1(doc));
364  TEST(!vsmd2(doc));
365  TEST(vsmd3(doc));
366  doc.add_value(0, "blah");
367  TEST(!vsmd1(doc));
368  TEST(vsmd2(doc));
369  TEST(vsmd3(doc));
370 
371  vsmd3.remove_value("nosuch"); // Test removing a value which isn't present.
372  vsmd3.remove_value("blah");
373  TEST(!vsmd1(doc));
374  TEST(vsmd2(doc));
375  TEST(!vsmd3(doc));
376  doc.add_value(0, "42");
377  TEST(vsmd1(doc));
378  TEST(!vsmd2(doc));
379  TEST(vsmd3(doc));
380 }
381 
382 // Test that asking for the termfreq on an empty mset raises an exception.
383 DEFINE_TESTCASE(emptymset1, !backend) {
384  Xapian::MSet emptymset;
386  emptymset.get_termfreq("foo"));
387 }
388 
389 DEFINE_TESTCASE(expanddeciderfilterprefix1, !backend) {
390  string prefix = "tw";
391  Xapian::ExpandDeciderFilterPrefix decider(prefix);
392  TEST(!decider("one"));
393  TEST(!decider("t"));
394  TEST(!decider(""));
395  TEST(!decider("Two"));
396  TEST(decider("two"));
397  TEST(decider("twitter"));
398  TEST(decider(prefix));
399 }
const TermIterator get_unique_terms_end() const
End iterator for unique terms in the query object.
Definition: query.h:516
Xapian::termcount get_length() const
Return the length of this query object.
Definition: query.cc:187
bool is_none() const
Return true if this is a no-op stemmer.
Definition: stem.h:166
ExpandDecider subclass which restrict terms to a particular prefix.
Xapian::doccount size() const
The number of documents in this R-Set.
Definition: omenquire.cc:92
void add_value(Xapian::valueno slot, const std::string &value)
Add a new value.
Definition: omdocument.cc:107
MatchDecider filtering results based on whether document values are in a user-defined set...
static size_t check(const std::string &path, int opts=0, std::ostream *out=NULL)
Check the integrity of a database or database table.
Definition: database.h:564
#define TEST(a)
Test a condition, without an additional explanation for failure.
Definition: testsuite.h:275
std::string get_description() const
Return a string describing this object.
Definition: omenquire.cc:123
This class is used to access a database, or a group of databases.
Definition: database.h:68
Match documents which an odd number of subqueries match.
Definition: query.h:107
InvalidOperationError indicates the API was used in an invalid way.
Definition: error.h:283
const TermIterator get_terms_begin() const
Begin iterator for terms in the query object.
Definition: query.cc:135
DatabaseOpeningError indicates failure to open a database.
Definition: error.h:581
Class representing a stemming algorithm.
Definition: stem.h:62
op
Query operators.
Definition: query.h:78
void remove_value(const std::string &value)
Remove a value from the test set.
const std::string & get_msg() const
Message giving details of the error, intended for human consumption.
Definition: error.h:122
a generic test suite engine
void add_value(const std::string &value)
Add a value to the test set.
Class representing a list of search results.
Definition: mset.h:44
STL namespace.
static Xapian::Stem stemmer
Definition: stemtest.cc:41
DEFINE_TESTCASE(getqterms1, !backend)
Definition: api_nodb.cc:42
void remove_value(Xapian::valueno slot)
Remove any value with the given number.
Definition: omdocument.cc:114
const TermIterator get_unique_terms_begin() const
Begin iterator for unique terms in the query object.
Definition: query.cc:160
test functionality of the Xapian API
Class for iterating over a list of terms.
Definition: termiterator.h:41
#define TEST_NOT_EQUAL(a, b)
Test for non-equality of two things.
Definition: testsuite.h:305
InvalidArgumentError indicates an invalid parameter value was passed to the API.
Definition: error.h:241
std::ostringstream tout
The debug printing stream.
Definition: testsuite.cc:104
Scale the weight contributed by a subquery.
Definition: query.h:166
Match the first subquery taking extra weight from other subqueries.
Definition: query.h:118
std::string get_description() const
Return a string describing this object.
Definition: stem.cc:147
Public interfaces for the Xapian library.
static std::string get_available_languages()
Return a list of available languages.
Definition: stem.h:181
#define TEST_EXCEPTION(TYPE, CODE)
Check that CODE throws exactly Xapian exception TYPE.
Definition: testutils.h:109
void add_document(Xapian::docid did)
Add a document to the relevance set.
Definition: omenquire.cc:104
#define FAIL_TEST(MSG)
Fail the current testcase with message MSG.
Definition: testsuite.h:68
Match only documents which all subqueries match.
Definition: query.h:84
static Xapian::Query query(Xapian::Query::op op, const string &t1=string(), const string &t2=string(), const string &t3=string(), const string &t4=string(), const string &t5=string(), const string &t6=string(), const string &t7=string(), const string &t8=string(), const string &t9=string(), const string &t10=string())
Definition: api_anydb.cc:63
std::string get_description() const
Return a string describing this object.
Definition: query.cc:232
This class provides an interface to the information retrieval system for the purpose of searching...
Definition: enquire.h:152
bool empty() const
Check if this query is Xapian::Query::MatchNothing.
Definition: query.h:524
Match documents which the first subquery matches but no others do.
Definition: query.h:99
Match documents which at least one subquery matches.
Definition: query.h:92
Xapian-specific test helper functions and macros.
static const Xapian::Query MatchNothing
A query matching no documents.
Definition: query.h:65
#define TEST_STRINGS_EQUAL(a, b)
Test for equality of two strings.
Definition: testsuite.h:287
static string language
Definition: stemtest.cc:39
Xapian::doccount get_termfreq(const std::string &term) const
Get the termfreq of a term.
Definition: omenquire.cc:206
Class representing a query.
Definition: query.h:46
#define TEST_EQUAL(a, b)
Test for equality of two things.
Definition: testsuite.h:278
bool empty() const
Test if this R-Set is empty.
Definition: omenquire.cc:98
const TermIterator get_terms_end() const
End iterator for terms in the query object.
Definition: query.h:502
std::string get_value(Xapian::valueno slot) const
Get value by number.
Definition: omdocument.cc:64
A handle representing a document in a Xapian database.
Definition: document.h:61
A relevance set (R-Set).
Definition: enquire.h:60