xapian-core  2.0.0
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-2024 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, see
21  * <https://www.gnu.org/licenses/>.
22  */
23 
24 #include <config.h>
25 
26 #include "api_nodb.h"
27 
28 #include <xapian.h>
29 
30 #include "apitest.h"
31 #include "testsuite.h"
32 #include "testutils.h"
33 
34 #include <list>
35 #include <string>
36 #include <vector>
37 
38 using namespace std;
39 
40 // tests that get_query_terms() returns the terms in the right order
41 DEFINE_TESTCASE(getqterms1, !backend) {
42  list<string> answers_list;
43  answers_list.push_back("one");
44  answers_list.push_back("two");
45  answers_list.push_back("three");
46  answers_list.push_back("four");
47 
50  Xapian::Query("one", 1, 1),
51  Xapian::Query("three", 1, 3)),
53  Xapian::Query("four", 1, 4),
54  Xapian::Query("two", 1, 2)));
55 
56  list<string> list1;
57  {
59  for (t = myquery.get_terms_begin(); t != myquery.get_terms_end(); ++t)
60  list1.push_back(*t);
61  }
62  TEST(list1 == answers_list);
63  list<string> list2(myquery.get_terms_begin(), myquery.get_terms_end());
64  TEST(list2 == answers_list);
65 }
66 
67 // tests that get_query_terms() doesn't SEGV on an empty query
68 // (regression test for bug in 0.9.0)
69 DEFINE_TESTCASE(getqterms2, !backend) {
70  Xapian::Query empty_query;
71  TEST_EQUAL(empty_query.get_terms_begin(), empty_query.get_terms_end());
72  TEST_EQUAL(empty_query.get_unique_terms_begin(),
73  empty_query.get_unique_terms_end());
74 }
75 
76 // tests that empty queries work correctly
77 DEFINE_TESTCASE(emptyquery2, !backend) {
78  // test that Query::empty() is true for an empty query.
79  TEST(Xapian::Query().empty());
80  // test that an empty query has length 0
81  TEST(Xapian::Query().get_length() == 0);
82  vector<Xapian::Query> v;
83  TEST(Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end()).empty());
84  TEST(Xapian::Query(Xapian::Query::OP_OR, v.begin(), v.end()).get_length() == 0);
85 }
86 
88 DEFINE_TESTCASE(emptyquery3, !backend) {
89  static const Xapian::Query::op ops[] = {
95  };
96 
97  for (size_t i = 0; i < sizeof(ops) / sizeof(ops[0]); ++i) {
98  tout << "Testing op #" << i << '\n';
99  Xapian::Query empty;
100  Xapian::Query q("test");
101  Xapian::Query qcombine(ops[i], empty, q);
102  tout << qcombine.get_description() << '\n';
103  Xapian::Query qcombine2(ops[i], q, empty);
104  tout << qcombine2.get_description() << '\n';
105  Xapian::Query qcombine3(ops[i], empty, empty);
106  tout << qcombine3.get_description() << '\n';
107  }
108 }
109 
110 // tests that query lengths are calculated correctly
111 DEFINE_TESTCASE(querylen1, !backend) {
112  // test that a simple query has the right length
113  Xapian::Query myquery;
115  Xapian::Query("foo"),
116  Xapian::Query("bar"));
118  myquery,
120  Xapian::Query("wibble"),
121  Xapian::Query("spoon")));
122 
123  TEST_EQUAL(myquery.get_length(), 4);
124  TEST(!myquery.empty());
125 }
126 
127 // tests that query lengths are calculated correctly
128 DEFINE_TESTCASE(querylen2, !backend) {
129  // test with an even bigger and strange query
130  string terms[3] = {
131  "foo",
132  "bar",
133  "baz"
134  };
135  Xapian::Query queries[3] = {
136  Xapian::Query("wibble"),
137  Xapian::Query("wobble"),
138  Xapian::Query(Xapian::Query::OP_OR, string("jelly"), string("belly"))
139  };
140 
141  Xapian::Query myquery;
142  vector<string> v1(terms, terms + 3);
143  vector<Xapian::Query> v2(queries, queries + 3);
144  vector<Xapian::Query *> v3;
145  Xapian::Query query1(Xapian::Query::OP_AND, string("ball"), string("club"));
146  Xapian::Query query2("ring");
147  v3.push_back(&query1);
148  v3.push_back(&query2);
149 
150  Xapian::Query myq1 = Xapian::Query(Xapian::Query::OP_AND, v1.begin(), v1.end());
151  tout << "myq1=" << myq1 << "\n";
152  TEST_EQUAL(myq1.get_length(), 3);
153 
154  Xapian::Query myq2_1 = Xapian::Query(Xapian::Query::OP_OR, v2.begin(), v2.end());
155  tout << "myq2_1=" << myq2_1 << "\n";
156  TEST_EQUAL(myq2_1.get_length(), 4);
157 
158  Xapian::Query myq2_2 = Xapian::Query(Xapian::Query::OP_AND, v3.begin(), v3.end());
159  tout << "myq2_2=" << myq2_2 << "\n";
160  TEST_EQUAL(myq2_2.get_length(), 3);
161 
162  Xapian::Query myq2 = Xapian::Query(Xapian::Query::OP_OR, myq2_1, myq2_2);
163  tout << "myq2=" << myq2 << "\n";
164  TEST_EQUAL(myq2.get_length(), 7);
165 
166  myquery = Xapian::Query(Xapian::Query::OP_OR, myq1, myq2);
167  tout << "myquery=" << myquery << "\n";
168  TEST_EQUAL(myquery.get_length(), 10);
169 }
170 
177 DEFINE_TESTCASE(dontflattensubqueries1, !backend) {
178  Xapian::Query queries1[3] = {
179  Xapian::Query("wibble"),
180  Xapian::Query("wobble"),
181  Xapian::Query(Xapian::Query::OP_OR, string("jelly"), string("belly"))
182  };
183 
184  Xapian::Query queries2[3] = {
185  Xapian::Query(Xapian::Query::OP_AND, string("jelly"), string("belly")),
186  Xapian::Query("wibble"),
187  Xapian::Query("wobble")
188  };
189 
190  vector<Xapian::Query> vec1(queries1, queries1 + 3);
191  Xapian::Query myquery1(Xapian::Query::OP_OR, vec1.begin(), vec1.end());
192  TEST_EQUAL(myquery1.get_description(),
193  "Query((wibble OR wobble OR (jelly OR belly)))");
194 
195  vector<Xapian::Query> vec2(queries2, queries2 + 3);
196  Xapian::Query myquery2(Xapian::Query::OP_AND, vec2.begin(), vec2.end());
197  TEST_EQUAL(myquery2.get_description(),
198  "Query(((jelly AND belly) AND wibble AND wobble))");
199 }
200 
201 // test behaviour when creating a query from an empty vector
202 DEFINE_TESTCASE(emptyquerypart1, !backend) {
203  vector<string> emptyterms;
204  Xapian::Query query(Xapian::Query::OP_OR, emptyterms.begin(), emptyterms.end());
206  TEST(Xapian::Query(Xapian::Query::OP_AND, query, Xapian::Query("x")).get_length() == 0);
208  TEST(Xapian::Query(Xapian::Query::OP_OR, query, Xapian::Query("x")).get_length() == 1);
209 }
210 
211 DEFINE_TESTCASE(stemlangs1, !backend) {
212  string langs = Xapian::Stem::get_available_languages();
213  tout << "available languages '" << langs << "'\n";
214  TEST(!langs.empty());
215 
216  // Also test the language codes.
217  langs += " ar hy eu ca da nl en fi fr de hu id ga it lt ne nb nn no pt ro"
218  " ru es sv ta tr";
219 
220  string::size_type i = 0;
221  while (true) {
222  string::size_type spc = langs.find(' ', i);
223  // The only spaces in langs should be a single one between each pair
224  // of language names.
225  TEST_NOT_EQUAL(i, spc);
226 
227  // Try making a stemmer for this language. We should be able to create
228  // it without an exception being thrown.
229  string language(langs, i, spc - i);
230  tout << "checking language code '" << language << "' works\n";
232  TEST(!stemmer.is_none());
233  if (language.size() > 2) {
234  string expected("Xapian::Stem(");
235  expected += language;
236  expected += ')';
237  TEST_EQUAL(stemmer.get_description(), expected);
238  }
239 
240  if (spc == string::npos) break;
241  i = spc + 1;
242  }
243 
244  {
245  // Stem("none") should give a no-op stemmer.
246  Xapian::Stem stem_nothing = Xapian::Stem("none");
247  TEST(stem_nothing.is_none());
248  TEST_EQUAL(stem_nothing.get_description(), "Xapian::Stem(none)");
249  }
250 
251  {
252  // Stem("") should be equivalent.
253  Xapian::Stem stem_nothing = Xapian::Stem("");
254  TEST(stem_nothing.is_none());
255  TEST_EQUAL(stem_nothing.get_description(), "Xapian::Stem(none)");
256  }
257 }
258 
259 // Regression test.
260 DEFINE_TESTCASE(nosuchdb1, !backend) {
261  // This is a "nodb" test because it doesn't test a particular backend.
262  try {
263  Xapian::Database db("NOsuChdaTabASe");
264  FAIL_TEST("Managed to open 'NOsuChdaTabASe'");
265  } catch (const Xapian::DatabaseOpeningError & e) {
266  // We don't really require this exact message, but in Xapian <= 1.1.0
267  // this gave "Couldn't detect type of database".
268  TEST_STRINGS_EQUAL(e.get_msg(), "Couldn't stat 'NOsuChdaTabASe'");
269  }
270 
271  try {
272  Xapian::Database::check("NOsuChdaTabASe");
273  FAIL_TEST("Managed to check 'NOsuChdaTabASe'");
274  } catch (const Xapian::DatabaseOpeningError & e) {
275  // In 1.4.3 and earlier, this threw DatabaseError with the message:
276  // "File is not a Xapian database or database table" (confusing as
277  // there is no file).
279  "Couldn't find Xapian database or table to check");
280  }
281 }
282 
283 // Feature tests for value manipulations.
284 DEFINE_TESTCASE(addvalue1, !backend) {
285  // Regression test for add_value on an existing value (bug#82).
286  Xapian::Document doc;
287  doc.add_value(1, "original");
288  doc.add_value(1, "replacement");
289  TEST_EQUAL(doc.get_value(1), "replacement");
290 
291  doc.add_value(2, "too");
292  doc.add_value(3, "free");
293  doc.add_value(4, "for");
294 
295  doc.remove_value(2);
296  doc.remove_value(4);
297  TEST_EQUAL(doc.get_value(0), "");
298  TEST_EQUAL(doc.get_value(1), "replacement");
299  TEST_EQUAL(doc.get_value(2), "");
300  TEST_EQUAL(doc.get_value(3), "free");
301  TEST_EQUAL(doc.get_value(4), "");
302 }
303 
304 // tests that the collapsing on termpos optimisation gives correct query length
305 DEFINE_TESTCASE(poscollapse2, !backend) {
306  Xapian::Query q(Xapian::Query::OP_OR, Xapian::Query("this", 1, 1), Xapian::Query("this", 1, 1));
307  TEST_EQUAL(q.get_length(), 2);
308 }
309 
310 // Regression test: query on an uninitialised database segfaulted with 1.0.0.
311 // As of 2.0.0, this is just handled as an empty database.
312 DEFINE_TESTCASE(uninitdb1, !backend) {
313  Xapian::Database db;
314  Xapian::Enquire enq(db);
315 }
316 
317 // Test a scaleweight query applied to a match nothing query
318 DEFINE_TESTCASE(scaleweight3, !backend) {
321  TEST_EQUAL(query.get_description(), "Query()");
322 }
323 
324 // Regression test - before 1.1.0, you could add docid 0 to an RSet.
325 DEFINE_TESTCASE(rset3, !backend) {
326  Xapian::RSet rset;
328  TEST(rset.empty());
329  TEST_EQUAL(rset.size(), 0);
330  rset.add_document(1);
331  rset.add_document(static_cast<Xapian::docid>(-1));
333  TEST(!rset.empty());
334  TEST_EQUAL(rset.size(), 2);
335 }
336 
337 // Regression test - RSet::get_description() gave a malformed answer in 1.0.7.
338 DEFINE_TESTCASE(rset4, !backend) {
339  Xapian::RSet rset;
340  TEST_STRINGS_EQUAL(rset.get_description(), "RSet()");
341  rset.add_document(2);
342  // In 1.0.7 this gave: RSet(RSet(RSet::Internal(, 2))
343  TEST_STRINGS_EQUAL(rset.get_description(), "RSet(2)");
344  rset.add_document(1);
345  TEST_STRINGS_EQUAL(rset.get_description(), "RSet(1,2)");
346 }
347 
348 // Direct test of ValueSetMatchDecider
349 DEFINE_TESTCASE(valuesetmatchdecider1, !backend) {
350  Xapian::ValueSetMatchDecider vsmd1(0, true);
351  vsmd1.add_value("42");
352  Xapian::ValueSetMatchDecider vsmd2(0, false);
353  vsmd2.remove_value("nosuch"); // Test removing a value which isn't present.
354  vsmd2.add_value("42");
355  Xapian::ValueSetMatchDecider vsmd3(0, true);
356  vsmd3.add_value("42");
357  vsmd3.add_value("blah");
358 
359  Xapian::Document doc;
360  TEST(!vsmd1(doc));
361  TEST(vsmd2(doc));
362  TEST(!vsmd3(doc));
363  doc.add_value(0, "42");
364  TEST(vsmd1(doc));
365  TEST(!vsmd2(doc));
366  TEST(vsmd3(doc));
367  doc.add_value(0, "blah");
368  TEST(!vsmd1(doc));
369  TEST(vsmd2(doc));
370  TEST(vsmd3(doc));
371 
372  vsmd3.remove_value("nosuch"); // Test removing a value which isn't present.
373  vsmd3.remove_value("blah");
374  TEST(!vsmd1(doc));
375  TEST(vsmd2(doc));
376  TEST(!vsmd3(doc));
377  doc.add_value(0, "42");
378  TEST(vsmd1(doc));
379  TEST(!vsmd2(doc));
380  TEST(vsmd3(doc));
381 }
382 
383 // Test that requesting termfreq or termweight on an empty mset returns 0.
384 // New behaviour as of 2.0.0 - previously both methods threw
385 // Xapian::InvalidOperationError.
386 DEFINE_TESTCASE(emptymset1, !backend) {
387  Xapian::MSet emptymset;
388  TEST_EQUAL(emptymset.get_termfreq("foo"), 0);
389  TEST_EQUAL(emptymset.get_termweight("foo"), 0.0);
390 }
391 
392 DEFINE_TESTCASE(expanddeciderfilterprefix1, !backend) {
393  string prefix = "tw";
394  Xapian::ExpandDeciderFilterPrefix decider(prefix);
395  TEST(!decider("one"));
396  TEST(!decider("t"));
397  TEST(!decider(""));
398  TEST(!decider("Two"));
399  TEST(decider("two"));
400  TEST(decider("twitter"));
401  TEST(decider(prefix));
402 }
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:62
DEFINE_TESTCASE(getqterms1, !backend)
Definition: api_nodb.cc:41
test functionality of the Xapian API
DatabaseOpeningError indicates failure to open a database.
Definition: error.h:569
An indexed database of documents.
Definition: database.h:75
static size_t check(std::string_view path, int opts=0, std::ostream *out=NULL)
Check the integrity of a database or database table.
Definition: database.h:669
Class representing a document.
Definition: document.h:64
std::string get_value(Xapian::valueno slot) const
Read a value slot in this document.
Definition: document.cc:185
void remove_value(Xapian::valueno slot)
Remove any value from the specified slot.
Definition: document.h:242
void add_value(Xapian::valueno slot, std::string_view value)
Add a value to a slot in this document.
Definition: document.cc:191
Querying session.
Definition: enquire.h:57
const std::string & get_msg() const noexcept
Message giving details of the error, intended for human consumption.
Definition: error.h:111
ExpandDecider subclass which restrict terms to a particular prefix.
InvalidArgumentError indicates an invalid parameter value was passed to the API.
Definition: error.h:229
Class representing a list of search results.
Definition: mset.h:46
Xapian::doccount get_termfreq(std::string_view term) const
Get the termfreq of a term.
Definition: mset.cc:281
double get_termweight(std::string_view term) const
Get the term weight of a term.
Definition: mset.cc:300
Class representing a query.
Definition: query.h:45
const TermIterator get_terms_begin() const
Begin iterator for terms in the query object.
Definition: query.cc:198
const TermIterator get_unique_terms_begin() const
Begin iterator for unique terms in the query object.
Definition: query.cc:223
std::string get_description() const
Return a string describing this object.
Definition: query.cc:307
const TermIterator get_unique_terms_end() const noexcept
End iterator for unique terms in the query object.
Definition: query.h:653
const TermIterator get_terms_end() const noexcept
End iterator for terms in the query object.
Definition: query.h:639
op
Query operators.
Definition: query.h:78
@ OP_SCALE_WEIGHT
Scale the weight contributed by a subquery.
Definition: query.h:166
@ OP_XOR
Match documents which an odd number of subqueries match.
Definition: query.h:107
@ OP_AND_MAYBE
Match the first subquery taking extra weight from other subqueries.
Definition: query.h:118
@ OP_AND
Match only documents which all subqueries match.
Definition: query.h:84
@ OP_OR
Match documents which at least one subquery matches.
Definition: query.h:92
@ OP_AND_NOT
Match documents which the first subquery matches but no others do.
Definition: query.h:99
static const Xapian::Query MatchNothing
A query matching no documents.
Definition: query.h:64
bool empty() const noexcept
Check if this query is Xapian::Query::MatchNothing.
Definition: query.h:661
Xapian::termcount get_length() const noexcept
Return the length of this query object.
Definition: query.cc:250
Class representing a set of documents judged as relevant.
Definition: rset.h:39
void add_document(Xapian::docid did)
Mark a document as relevant.
Definition: rset.cc:55
Xapian::doccount size() const
Return number of documents in this RSet object.
Definition: rset.cc:49
bool empty() const
Return true if this RSet object is empty.
Definition: rset.h:81
std::string get_description() const
Return a string describing this object.
Definition: rset.cc:80
Class representing a stemming algorithm.
Definition: stem.h:74
static std::string get_available_languages()
Return a list of available languages.
Definition: stem.h:208
std::string get_description() const
Return a string describing this object.
Definition: stem.cc:57
bool is_none() const
Return true if this is a no-op stemmer.
Definition: stem.h:193
Class for iterating over a list of terms.
Definition: termiterator.h:41
MatchDecider filtering results based on whether document values are in a user-defined set.
void remove_value(const std::string &value)
Remove a value from the test set.
void add_value(const std::string &value)
Add a value to the test set.
unsigned XAPIAN_DOCID_BASE_TYPE docid
A unique identifier for a document.
Definition: types.h:51
static string language
Definition: stemtest.cc:40
static Xapian::Stem stemmer
Definition: stemtest.cc:42
std::ostringstream tout
The debug printing stream.
Definition: testsuite.cc:104
a generic test suite engine
#define FAIL_TEST(MSG)
Fail the current testcase with message MSG.
Definition: testsuite.h:65
#define TEST_EQUAL(a, b)
Test for equality of two things.
Definition: testsuite.h:276
#define TEST_STRINGS_EQUAL(a, b)
Test for equality of two strings.
Definition: testsuite.h:285
#define TEST(a)
Test a condition, without an additional explanation for failure.
Definition: testsuite.h:273
#define TEST_NOT_EQUAL(a, b)
Test for non-equality of two things.
Definition: testsuite.h:303
Xapian-specific test helper functions and macros.
#define TEST_EXCEPTION(TYPE, CODE)
Check that CODE throws exactly Xapian exception TYPE.
Definition: testutils.h:112
Public interfaces for the Xapian library.