1 /* api_anydb.cc: tests which work with any backend
2 *
3 * Copyright 1999,2000,2001 BrightStation PLC
4 * Copyright 2002 Ananova Ltd
5 * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012 Olly Betts
6 * Copyright 2006,2008 Lemur Consulting Ltd
7 * Copyright 2011 Action Without Borders
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_anydb.h"
28
29 #include <algorithm>
30 #include <string>
31
32 #include <xapian.h>
33 #include "backendmanager_local.h"
34 #include "testsuite.h"
35 #include "testutils.h"
36 #include "utils.h"
37
38 #include "apitest.h"
39
40 #include <list>
41
42 using namespace std;
43
44 static void
print_mset_weights(const Xapian::MSet & mset)45 print_mset_weights(const Xapian::MSet &mset)
46 {
47 Xapian::MSetIterator i = mset.begin();
48 for ( ; i != mset.end(); ++i) {
49 tout << " " << i.get_weight();
50 }
51 }
52
53 static void
print_mset_percentages(const Xapian::MSet & mset)54 print_mset_percentages(const Xapian::MSet &mset)
55 {
56 Xapian::MSetIterator i = mset.begin();
57 for ( ; i != mset.end(); ++i) {
58 tout << " " << mset.convert_to_percent(i);
59 }
60 }
61
62 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 ())63 query(Xapian::Query::op op,
64 const string & t1 = string(), const string & t2 = string(),
65 const string & t3 = string(), const string & t4 = string(),
66 const string & t5 = string(), const string & t6 = string(),
67 const string & t7 = string(), const string & t8 = string(),
68 const string & t9 = string(), const string & t10 = string())
69 {
70 vector<string> v;
71 Xapian::Stem stemmer("english");
72 if (!t1.empty()) v.push_back(stemmer(t1));
73 if (!t2.empty()) v.push_back(stemmer(t2));
74 if (!t3.empty()) v.push_back(stemmer(t3));
75 if (!t4.empty()) v.push_back(stemmer(t4));
76 if (!t5.empty()) v.push_back(stemmer(t5));
77 if (!t6.empty()) v.push_back(stemmer(t6));
78 if (!t7.empty()) v.push_back(stemmer(t7));
79 if (!t8.empty()) v.push_back(stemmer(t8));
80 if (!t9.empty()) v.push_back(stemmer(t9));
81 if (!t10.empty()) v.push_back(stemmer(t10));
82 return Xapian::Query(op, v.begin(), v.end());
83 }
84
85 static Xapian::Query
query(Xapian::Query::op op,Xapian::termcount parameter,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 ())86 query(Xapian::Query::op op, Xapian::termcount parameter,
87 const string & t1 = string(), const string & t2 = string(),
88 const string & t3 = string(), const string & t4 = string(),
89 const string & t5 = string(), const string & t6 = string(),
90 const string & t7 = string(), const string & t8 = string(),
91 const string & t9 = string(), const string & t10 = string())
92 {
93 vector<string> v;
94 Xapian::Stem stemmer("english");
95 if (!t1.empty()) v.push_back(stemmer(t1));
96 if (!t2.empty()) v.push_back(stemmer(t2));
97 if (!t3.empty()) v.push_back(stemmer(t3));
98 if (!t4.empty()) v.push_back(stemmer(t4));
99 if (!t5.empty()) v.push_back(stemmer(t5));
100 if (!t6.empty()) v.push_back(stemmer(t6));
101 if (!t7.empty()) v.push_back(stemmer(t7));
102 if (!t8.empty()) v.push_back(stemmer(t8));
103 if (!t9.empty()) v.push_back(stemmer(t9));
104 if (!t10.empty()) v.push_back(stemmer(t10));
105 return Xapian::Query(op, v.begin(), v.end(), parameter);
106 }
107
108 static Xapian::Query
query(const string & t)109 query(const string &t)
110 {
111 return Xapian::Query(Xapian::Stem("english")(t));
112 }
113
114 // #######################################################################
115 // # Tests start here
116
117 // tests that the backend doesn't return zero docids
DEFINE_TESTCASE(zerodocid1,backend)118 DEFINE_TESTCASE(zerodocid1, backend) {
119 // open the database (in this case a simple text file
120 // we prepared earlier)
121
122 Xapian::Database mydb(get_database("apitest_onedoc"));
123
124 Xapian::Enquire enquire(mydb);
125
126 // make a simple query, with one word in it - "word".
127 enquire.set_query(Xapian::Query("word"));
128
129 // retrieve the top ten results (we only expect one)
130 Xapian::MSet mymset = enquire.get_mset(0, 10);
131
132 // We've done the query, now check that the result is what
133 // we expect (1 document, with non-zero docid)
134 TEST_MSET_SIZE(mymset, 1);
135
136 TEST_AND_EXPLAIN(*(mymset.begin()) != 0,
137 "A query on a database returned a zero docid");
138
139 return true;
140 }
141
142 // tests that an empty query returns no matches
DEFINE_TESTCASE(emptyquery1,backend)143 DEFINE_TESTCASE(emptyquery1, backend) {
144 Xapian::Enquire enquire(get_database("apitest_simpledata"));
145
146 enquire.set_query(Xapian::Query());
147 Xapian::MSet mymset = enquire.get_mset(0, 10);
148 TEST_MSET_SIZE(mymset, 0);
149 TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
150 TEST_EQUAL(mymset.get_matches_upper_bound(), 0);
151 TEST_EQUAL(mymset.get_matches_estimated(), 0);
152 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 0);
153 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 0);
154 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 0);
155
156 vector<Xapian::Query> v;
157 enquire.set_query(Xapian::Query(Xapian::Query::OP_AND, v.begin(), v.end()));
158 mymset = enquire.get_mset(0, 10);
159 TEST_MSET_SIZE(mymset, 0);
160 TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
161 TEST_EQUAL(mymset.get_matches_upper_bound(), 0);
162 TEST_EQUAL(mymset.get_matches_estimated(), 0);
163 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 0);
164 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 0);
165 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 0);
166
167 return true;
168 }
169
170 // tests the document count for a simple query
DEFINE_TESTCASE(simplequery1,backend)171 DEFINE_TESTCASE(simplequery1, backend) {
172 Xapian::Enquire enquire(get_database("apitest_simpledata"));
173 enquire.set_query(Xapian::Query("word"));
174 Xapian::MSet mymset = enquire.get_mset(0, 10);
175 TEST_MSET_SIZE(mymset, 2);
176 return true;
177 }
178
179 // tests for the right documents and weights returned with simple query
DEFINE_TESTCASE(simplequery2,backend)180 DEFINE_TESTCASE(simplequery2, backend) {
181 // open the database (in this case a simple text file
182 // we prepared earlier)
183 Xapian::Database db = get_database("apitest_simpledata");
184 Xapian::Enquire enquire(db);
185 enquire.set_query(Xapian::Query("word"));
186
187 // retrieve the top results
188 Xapian::MSet mymset = enquire.get_mset(0, 10);
189
190 // We've done the query, now check that the result is what
191 // we expect (documents 2 and 4)
192 mset_expect_order(mymset, 2, 4);
193
194 // Check the weights
195 Xapian::MSetIterator i = mymset.begin();
196 // These weights are for BM25Weight(1,0,1,0.5,0.5)
197 TEST_EQUAL_DOUBLE(i.get_weight(), 1.04648168717725);
198 i++;
199 TEST_EQUAL_DOUBLE(i.get_weight(), 0.640987686595914);
200
201 return true;
202 }
203
204 // tests for the right document count for another simple query
DEFINE_TESTCASE(simplequery3,backend)205 DEFINE_TESTCASE(simplequery3, backend) {
206 Xapian::Enquire enquire(get_database("apitest_simpledata"));
207 enquire.set_query(query("this"));
208 Xapian::MSet mymset = enquire.get_mset(0, 10);
209
210 // Check that 6 documents were returned.
211 TEST_MSET_SIZE(mymset, 6);
212
213 return true;
214 }
215
216 // tests for the right document count for a wildcard query
217 // FIXME: move this to querytest (and just use an InMemory DB).
DEFINE_TESTCASE(wildquery1,backend)218 DEFINE_TESTCASE(wildquery1, backend) {
219 Xapian::QueryParser queryparser;
220 unsigned flags = Xapian::QueryParser::FLAG_WILDCARD |
221 Xapian::QueryParser::FLAG_LOVEHATE;
222 queryparser.set_stemmer(Xapian::Stem("english"));
223 queryparser.set_stemming_strategy(Xapian::QueryParser::STEM_ALL);
224 Xapian::Database db = get_database("apitest_simpledata");
225 queryparser.set_database(db);
226 Xapian::Enquire enquire(db);
227
228 Xapian::Query qobj = queryparser.parse_query("th*", flags);
229 tout << qobj.get_description() << endl;
230 enquire.set_query(qobj);
231 Xapian::MSet mymset = enquire.get_mset(0, 10);
232 // Check that 6 documents were returned.
233 TEST_MSET_SIZE(mymset, 6);
234
235 qobj = queryparser.parse_query("notindb* \"this\"", flags);
236 tout << qobj.get_description() << endl;
237 enquire.set_query(qobj);
238 mymset = enquire.get_mset(0, 10);
239 // Check that 6 documents were returned.
240 TEST_MSET_SIZE(mymset, 6);
241
242 qobj = queryparser.parse_query("+notindb* \"this\"", flags);
243 tout << qobj.get_description() << endl;
244 enquire.set_query(qobj);
245 mymset = enquire.get_mset(0, 10);
246 // Check that 0 documents were returned.
247 TEST_MSET_SIZE(mymset, 0);
248
249 return true;
250 }
251
252 // multidb1 and multidb2 no longer exist.
253
254 // test that a multidb with 2 dbs query returns correct docids
255 DEFINE_TESTCASE(multidb3, backend && !multi) {
256 Xapian::Database mydb2(get_database("apitest_simpledata"));
257 mydb2.add_database(get_database("apitest_simpledata2"));
258 Xapian::Enquire enquire(mydb2);
259
260 // make a query
261 Xapian::Query myquery = query(Xapian::Query::OP_OR, "inmemory", "word");
262 enquire.set_weighting_scheme(Xapian::BoolWeight());
263 enquire.set_query(myquery);
264
265 // retrieve the top ten results
266 Xapian::MSet mymset = enquire.get_mset(0, 10);
267 mset_expect_order(mymset, 2, 3, 7);
268
269 return true;
270 }
271
272 // test that a multidb with 3 dbs query returns correct docids
273 DEFINE_TESTCASE(multidb4, backend && !multi) {
274 Xapian::Database mydb2(get_database("apitest_simpledata"));
275 mydb2.add_database(get_database("apitest_simpledata2"));
276 mydb2.add_database(get_database("apitest_termorder"));
277 Xapian::Enquire enquire(mydb2);
278
279 // make a query
280 Xapian::Query myquery = query(Xapian::Query::OP_OR, "inmemory", "word");
281 enquire.set_weighting_scheme(Xapian::BoolWeight());
282 enquire.set_query(myquery);
283
284 // retrieve the top ten results
285 Xapian::MSet mymset = enquire.get_mset(0, 10);
286 mset_expect_order(mymset, 2, 3, 4, 10);
287
288 return true;
289 }
290
291 // tests MultiPostList::skip_to().
292 DEFINE_TESTCASE(multidb5, backend && !multi) {
293 Xapian::Database mydb2(get_database("apitest_simpledata"));
294 mydb2.add_database(get_database("apitest_simpledata2"));
295 Xapian::Enquire enquire(mydb2);
296
297 // make a query
298 Xapian::Query myquery = query(Xapian::Query::OP_AND, "inmemory", "word");
299 enquire.set_weighting_scheme(Xapian::BoolWeight());
300 enquire.set_query(myquery);
301
302 // retrieve the top ten results
303 Xapian::MSet mymset = enquire.get_mset(0, 10);
304 mset_expect_order(mymset, 2);
305
306 return true;
307 }
308
309 // tests that when specifying maxitems to get_mset, no more than
310 // that are returned.
DEFINE_TESTCASE(msetmaxitems1,backend)311 DEFINE_TESTCASE(msetmaxitems1, backend) {
312 Xapian::Enquire enquire(get_database("apitest_simpledata"));
313 enquire.set_query(query("this"));
314 Xapian::MSet mymset = enquire.get_mset(0, 1);
315 TEST_MSET_SIZE(mymset, 1);
316
317 mymset = enquire.get_mset(0, 5);
318 TEST_MSET_SIZE(mymset, 5);
319
320 return true;
321 }
322
323 // tests the returned weights are as expected (regression test for remote
324 // backend which was using the average weight rather than the actual document
325 // weight for computing weights - fixed in 1.0.0).
DEFINE_TESTCASE(expandweights1,backend)326 DEFINE_TESTCASE(expandweights1, backend) {
327 Xapian::Enquire enquire(get_database("apitest_simpledata"));
328 enquire.set_query(Xapian::Query("this"));
329
330 Xapian::MSet mymset = enquire.get_mset(0, 10);
331
332 Xapian::RSet myrset;
333 Xapian::MSetIterator i = mymset.begin();
334 myrset.add_document(*i);
335 myrset.add_document(*(++i));
336
337 Xapian::ESet eset = enquire.get_eset(3, myrset, enquire.USE_EXACT_TERMFREQ);
338 TEST_EQUAL(eset.size(), 3);
339 TEST_EQUAL_DOUBLE(eset[0].get_weight(), 6.08904001099445);
340 TEST_EQUAL_DOUBLE(eset[1].get_weight(), 6.08904001099445);
341 TEST_EQUAL_DOUBLE(eset[2].get_weight(), 4.73383620844021);
342
343 return true;
344 }
345
346 // Just like test_expandweights1 but without USE_EXACT_TERMFREQ.
DEFINE_TESTCASE(expandweights2,backend)347 DEFINE_TESTCASE(expandweights2, backend) {
348 Xapian::Enquire enquire(get_database("apitest_simpledata"));
349 enquire.set_query(Xapian::Query("this"));
350
351 Xapian::MSet mymset = enquire.get_mset(0, 10);
352
353 Xapian::RSet myrset;
354 Xapian::MSetIterator i = mymset.begin();
355 myrset.add_document(*i);
356 myrset.add_document(*(++i));
357
358 Xapian::ESet eset = enquire.get_eset(3, myrset);
359 TEST_EQUAL(eset.size(), 3);
360 if (!startswith(get_dbtype(), "multi")) {
361 // For a single database, the weights should be the same with or
362 // without USE_EXACT_TERMFREQ.
363 TEST_EQUAL_DOUBLE(eset[0].get_weight(), 6.08904001099445);
364 TEST_EQUAL_DOUBLE(eset[1].get_weight(), 6.08904001099445);
365 TEST_EQUAL_DOUBLE(eset[2].get_weight(), 4.73383620844021);
366 } else {
367 // For multiple databases, we expect that using USE_EXACT_TERMFREQ
368 // will result in different weights in some cases.
369 TEST_NOT_EQUAL_DOUBLE(eset[0].get_weight(), 6.08904001099445);
370 TEST_EQUAL_DOUBLE(eset[1].get_weight(), 6.08904001099445);
371 TEST_NOT_EQUAL_DOUBLE(eset[2].get_weight(), 4.73383620844021);
372 }
373
374 return true;
375 }
376
DEFINE_TESTCASE(expandweights3,backend)377 DEFINE_TESTCASE(expandweights3, backend) {
378 Xapian::Enquire enquire(get_database("apitest_simpledata"));
379 enquire.set_query(Xapian::Query("this"));
380
381 Xapian::MSet mymset = enquire.get_mset(0, 10);
382
383 Xapian::RSet myrset;
384 Xapian::MSetIterator i = mymset.begin();
385 myrset.add_document(*i);
386 myrset.add_document(*(++i));
387
388 // Set min_wt to 0
389 Xapian::ESet eset = enquire.get_eset(50, myrset, 0, 1.0, 0, 0);
390 if (!startswith(get_dbtype(), "multi")) {
391 // For a single database, the weights should be the same with or
392 // without USE_EXACT_TERMFREQ.
393 TEST_EQUAL_DOUBLE(eset[0].get_weight(), 6.08904001099445);
394 TEST_EQUAL_DOUBLE(eset[1].get_weight(), 6.08904001099445);
395 TEST_EQUAL_DOUBLE(eset[2].get_weight(), 4.73383620844021);
396 } else {
397 // For multiple databases, we expect that using USE_EXACT_TERMFREQ
398 // will result in different weights in some cases.
399 TEST_NOT_EQUAL_DOUBLE(eset[0].get_weight(), 6.08904001099445);
400 TEST_EQUAL_DOUBLE(eset[1].get_weight(), 6.08904001099445);
401 TEST_NOT_EQUAL_DOUBLE(eset[2].get_weight(), 4.73383620844021);
402 }
403 TEST_REL(eset.back().get_weight(),>=,0);
404
405 return true;
406 }
407
408
409 // tests that negative weights are returned
DEFINE_TESTCASE(expandweights4,backend)410 DEFINE_TESTCASE(expandweights4, backend) {
411 Xapian::Enquire enquire(get_database("apitest_simpledata"));
412 enquire.set_query(Xapian::Query("paragraph"));
413
414 Xapian::MSet mymset = enquire.get_mset(0, 10);
415
416 Xapian::RSet myrset;
417 Xapian::MSetIterator i = mymset.begin();
418 myrset.add_document(*i);
419 myrset.add_document(*(++i));
420
421 Xapian::ESet eset = enquire.get_eset(37, myrset, 0, 1.0, 0, -100);
422 // Now include negative weights
423 TEST_EQUAL(eset.size(), 37);
424 TEST_REL(eset[36].get_weight(),<,0);
425 TEST_REL(eset[36].get_weight(),>=,-100);
426
427 return true;
428 }
429
430
431 // tests that when specifying maxitems to get_eset, no more than
432 // that are returned.
DEFINE_TESTCASE(expandmaxitems1,backend)433 DEFINE_TESTCASE(expandmaxitems1, backend) {
434 Xapian::Enquire enquire(get_database("apitest_simpledata"));
435 enquire.set_query(Xapian::Query("this"));
436
437 Xapian::MSet mymset = enquire.get_mset(0, 10);
438 tout << "mymset.size() = " << mymset.size() << endl;
439 TEST(mymset.size() >= 2);
440
441 Xapian::RSet myrset;
442 Xapian::MSetIterator i = mymset.begin();
443 myrset.add_document(*i);
444 myrset.add_document(*(++i));
445
446 Xapian::ESet myeset = enquire.get_eset(1, myrset);
447 TEST_EQUAL(myeset.size(), 1);
448
449 return true;
450 }
451
452 // tests that a pure boolean query has all weights set to 0
DEFINE_TESTCASE(boolquery1,backend)453 DEFINE_TESTCASE(boolquery1, backend) {
454 Xapian::Query myboolquery(query("this"));
455
456 // open the database (in this case a simple text file
457 // we prepared earlier)
458 Xapian::Enquire enquire(get_database("apitest_simpledata"));
459 enquire.set_query(myboolquery);
460 enquire.set_weighting_scheme(Xapian::BoolWeight());
461
462 // retrieve the top results
463 Xapian::MSet mymset = enquire.get_mset(0, 10);
464
465 TEST_NOT_EQUAL(mymset.size(), 0);
466 TEST_EQUAL(mymset.get_max_possible(), 0);
467 for (Xapian::MSetIterator i = mymset.begin(); i != mymset.end(); ++i) {
468 TEST_EQUAL(i.get_weight(), 0);
469 }
470 return true;
471 }
472
473 // tests that get_mset() specifying "this" works as expected
DEFINE_TESTCASE(msetfirst1,backend)474 DEFINE_TESTCASE(msetfirst1, backend) {
475 Xapian::Enquire enquire(get_database("apitest_simpledata"));
476 enquire.set_query(query("this"));
477 Xapian::MSet mymset1 = enquire.get_mset(0, 6);
478 Xapian::MSet mymset2 = enquire.get_mset(3, 3);
479 TEST(mset_range_is_same(mymset1, 3, mymset2, 0, 3));
480
481 // Regression test - we weren't adjusting the index into items[] by
482 // firstitem in api/omenquire.cc.
483 TEST_EQUAL(mymset1[5].get_document().get_data(),
484 mymset2[2].get_document().get_data());
485 return true;
486 }
487
488 // tests the converting-to-percent functions
DEFINE_TESTCASE(topercent1,backend)489 DEFINE_TESTCASE(topercent1, backend) {
490 Xapian::Enquire enquire(get_database("apitest_simpledata"));
491 enquire.set_query(query("this"));
492 Xapian::MSet mymset = enquire.get_mset(0, 20);
493
494 int last_pct = 100;
495 Xapian::MSetIterator i = mymset.begin();
496 for ( ; i != mymset.end(); ++i) {
497 int pct = mymset.convert_to_percent(i);
498 TEST_AND_EXPLAIN(pct == i.get_percent(),
499 "convert_to_%(msetitor) != convert_to_%(wt)");
500 TEST_AND_EXPLAIN(pct == mymset.convert_to_percent(i.get_weight()),
501 "convert_to_%(msetitor) != convert_to_%(wt)");
502 TEST_AND_EXPLAIN(pct >= 0 && pct <= 100,
503 "percentage out of range: " << pct);
504 TEST_AND_EXPLAIN(pct <= last_pct, "percentage increased down mset");
505 last_pct = pct;
506 }
507 return true;
508 }
509
510 // tests the percentage values returned
DEFINE_TESTCASE(topercent2,backend)511 DEFINE_TESTCASE(topercent2, backend) {
512 BackendManagerLocal local_manager;
513 local_manager.set_datadir(test_driver::get_srcdir() + "/testdata/");
514 Xapian::Enquire localenq(local_manager.get_database("apitest_simpledata"));
515 Xapian::Enquire enquire(get_database("apitest_simpledata"));
516
517 int pct;
518
519 // First, test a search in which the top document scores 100%.
520 enquire.set_query(query("this"));
521 localenq.set_query(query("this"));
522 Xapian::MSet mymset = enquire.get_mset(0, 20);
523 Xapian::MSet localmset = localenq.get_mset(0, 20);
524
525 Xapian::MSetIterator i = mymset.begin();
526 TEST(i != mymset.end());
527 pct = mymset.convert_to_percent(i);
528 TEST_EQUAL(pct, 100);
529
530 TEST_EQUAL(mymset.get_matches_lower_bound(), localmset.get_matches_lower_bound());
531 TEST_EQUAL(mymset.get_matches_upper_bound(), localmset.get_matches_upper_bound());
532 TEST_EQUAL(mymset.get_matches_estimated(), localmset.get_matches_estimated());
533 TEST_EQUAL_DOUBLE(mymset.get_max_attained(), localmset.get_max_attained());
534 TEST_EQUAL(mymset.size(), localmset.size());
535 TEST(mset_range_is_same(mymset, 0, localmset, 0, mymset.size()));
536
537 // A search in which the top document doesn't have 100%
538 Xapian::Query q = query(Xapian::Query::OP_OR,
539 "this", "line", "paragraph", "rubbish");
540 enquire.set_query(q);
541 localenq.set_query(q);
542 mymset = enquire.get_mset(0, 20);
543 localmset = localenq.get_mset(0, 20);
544
545 i = mymset.begin();
546 TEST(i != mymset.end());
547 pct = mymset.convert_to_percent(i);
548 TEST_REL(pct,>,60);
549 TEST_REL(pct,<,76);
550
551 ++i;
552
553 TEST(i != mymset.end());
554 pct = mymset.convert_to_percent(i);
555 TEST_REL(pct,>,40);
556 TEST_REL(pct,<,50);
557
558 TEST_EQUAL(mymset.get_matches_lower_bound(), localmset.get_matches_lower_bound());
559 TEST_EQUAL(mymset.get_matches_upper_bound(), localmset.get_matches_upper_bound());
560 TEST_EQUAL(mymset.get_matches_estimated(), localmset.get_matches_estimated());
561 TEST_EQUAL_DOUBLE(mymset.get_max_attained(), localmset.get_max_attained());
562 TEST_EQUAL(mymset.size(), localmset.size());
563 TEST(mset_range_is_same(mymset, 0, localmset, 0, mymset.size()));
564
565 return true;
566 }
567
568 class myExpandFunctor : public Xapian::ExpandDecider {
569 public:
operator ()(const string & tname) const570 bool operator()(const string & tname) const {
571 unsigned long sum = 0;
572 for (string::const_iterator i=tname.begin(); i!=tname.end(); ++i) {
573 sum += *i;
574 }
575 // if (verbose) {
576 // tout << tname << "==> " << sum << "\n";
577 // }
578 return (sum % 2) == 0;
579 }
580 };
581
582 // tests the expand decision functor
DEFINE_TESTCASE(expandfunctor1,backend)583 DEFINE_TESTCASE(expandfunctor1, backend) {
584 Xapian::Enquire enquire(get_database("apitest_simpledata"));
585 enquire.set_query(Xapian::Query("this"));
586
587 Xapian::MSet mymset = enquire.get_mset(0, 10);
588 TEST(mymset.size() >= 2);
589
590 Xapian::RSet myrset;
591 Xapian::MSetIterator i = mymset.begin();
592 myrset.add_document(*i);
593 myrset.add_document(*(++i));
594
595 myExpandFunctor myfunctor;
596
597 Xapian::ESet myeset_orig = enquire.get_eset(1000, myrset);
598 unsigned int neweset_size = 0;
599 Xapian::ESetIterator j = myeset_orig.begin();
600 for ( ; j != myeset_orig.end(); ++j) {
601 if (myfunctor(*j)) neweset_size++;
602 }
603 Xapian::ESet myeset = enquire.get_eset(neweset_size, myrset, &myfunctor);
604
605 #if 0
606 // Compare myeset with the hand-filtered version of myeset_orig.
607 if (verbose) {
608 tout << "orig_eset: ";
609 copy(myeset_orig.begin(), myeset_orig.end(),
610 ostream_iterator<Xapian::ESetItem>(tout, " "));
611 tout << "\n";
612
613 tout << "new_eset: ";
614 copy(myeset.begin(), myeset.end(),
615 ostream_iterator<Xapian::ESetItem>(tout, " "));
616 tout << "\n";
617 }
618 #endif
619 Xapian::ESetIterator orig = myeset_orig.begin();
620 Xapian::ESetIterator filt = myeset.begin();
621 for (; orig != myeset_orig.end() && filt != myeset.end(); ++orig, ++filt) {
622 // skip over items that shouldn't be in myeset
623 while (orig != myeset_orig.end() && !myfunctor(*orig)) {
624 ++orig;
625 }
626
627 TEST_AND_EXPLAIN(*orig == *filt &&
628 orig.get_weight() == filt.get_weight(),
629 "Mismatch in items " << *orig << " vs. " << *filt
630 << " after filtering");
631 }
632
633 while (orig != myeset_orig.end() && !myfunctor(*orig)) {
634 ++orig;
635 }
636
637 TEST_EQUAL(orig, myeset_orig.end());
638 TEST_AND_EXPLAIN(filt == myeset.end(),
639 "Extra items in the filtered eset.");
640 return true;
641 }
642
643 // tests the percent cutoff option
DEFINE_TESTCASE(pctcutoff1,backend)644 DEFINE_TESTCASE(pctcutoff1, backend) {
645 Xapian::Enquire enquire(get_database("apitest_simpledata"));
646 enquire.set_query(query(Xapian::Query::OP_OR,
647 "this", "line", "paragraph", "rubbish"));
648 Xapian::MSet mymset1 = enquire.get_mset(0, 100);
649
650 if (verbose) {
651 tout << "Original mset pcts:";
652 print_mset_percentages(mymset1);
653 tout << "\n";
654 }
655
656 unsigned int num_items = 0;
657 int my_pct = 100;
658 int changes = 0;
659 Xapian::MSetIterator i = mymset1.begin();
660 int c = 0;
661 for ( ; i != mymset1.end(); ++i, ++c) {
662 int new_pct = mymset1.convert_to_percent(i);
663 if (new_pct != my_pct) {
664 changes++;
665 if (changes > 3) break;
666 num_items = c;
667 my_pct = new_pct;
668 }
669 }
670
671 TEST_AND_EXPLAIN(changes > 3, "MSet not varied enough to test");
672 if (verbose) {
673 tout << "Cutoff percent: " << my_pct << "\n";
674 }
675
676 enquire.set_cutoff(my_pct);
677 Xapian::MSet mymset2 = enquire.get_mset(0, 100);
678
679 if (verbose) {
680 tout << "Percentages after cutoff:";
681 print_mset_percentages(mymset2);
682 tout << "\n";
683 }
684
685 TEST_AND_EXPLAIN(mymset2.size() >= num_items,
686 "Match with % cutoff lost too many items");
687
688 TEST_AND_EXPLAIN(mymset2.size() == num_items ||
689 (mymset2.convert_to_percent(mymset2[num_items]) == my_pct &&
690 mymset2.convert_to_percent(mymset2.back()) == my_pct),
691 "Match with % cutoff returned too many items");
692
693 return true;
694 }
695
696 // Tests the percent cutoff option combined with collapsing
DEFINE_TESTCASE(pctcutoff2,backend)697 DEFINE_TESTCASE(pctcutoff2, backend) {
698 Xapian::Enquire enquire(get_database("apitest_simpledata"));
699 enquire.set_query(Xapian::Query("this"));
700 enquire.set_query(Xapian::Query(Xapian::Query::OP_AND_NOT, Xapian::Query("this"), Xapian::Query("banana")));
701 Xapian::MSet mset = enquire.get_mset(0, 100);
702
703 if (verbose) {
704 tout << "Original mset pcts:";
705 print_mset_percentages(mset);
706 tout << "\n";
707 }
708
709 TEST(mset.size() >= 2);
710 TEST(mset[0].get_percent() - mset[1].get_percent() >= 2);
711
712 Xapian::percent cutoff = mset[0].get_percent() + mset[1].get_percent();
713 cutoff /= 2;
714
715 enquire.set_cutoff(cutoff);
716 enquire.set_collapse_key(1234); // Value which is always empty.
717
718 Xapian::MSet mset2 = enquire.get_mset(0, 1);
719 TEST_EQUAL(mset2.size(), 1);
720 TEST_EQUAL(mset2.get_matches_lower_bound(), 1);
721 TEST_REL(mset2.get_uncollapsed_matches_lower_bound(),>=,1);
722 TEST_REL(mset2.get_uncollapsed_matches_lower_bound(),<=,mset.size());
723 TEST_REL(mset2.get_uncollapsed_matches_upper_bound(),>=,mset.size());
724 TEST_REL(mset2.get_uncollapsed_matches_lower_bound(),<=,mset2.get_uncollapsed_matches_estimated());
725 TEST_REL(mset2.get_uncollapsed_matches_upper_bound(),>=,mset2.get_uncollapsed_matches_estimated());
726
727 return true;
728 }
729
730 // Test that the percent cutoff option returns all the answers it should.
DEFINE_TESTCASE(pctcutoff3,backend)731 DEFINE_TESTCASE(pctcutoff3, backend) {
732 Xapian::Enquire enquire(get_database("apitest_simpledata"));
733 enquire.set_query(Xapian::Query("this"));
734 Xapian::MSet mset1 = enquire.get_mset(0, 10);
735
736 if (verbose) {
737 tout << "Original mset pcts:";
738 print_mset_percentages(mset1);
739 tout << "\n";
740 }
741
742 int percent = 100;
743 for (Xapian::MSetIterator i = mset1.begin(); i != mset1.end(); ++i) {
744 int new_percent = mset1.convert_to_percent(i);
745 if (new_percent != percent) {
746 enquire.set_cutoff(percent);
747 Xapian::MSet mset2 = enquire.get_mset(0, 10);
748 TEST_EQUAL(mset2.size(), i.get_rank());
749 percent = new_percent;
750 }
751 }
752
753 return true;
754 }
755
756 // tests the cutoff option
DEFINE_TESTCASE(cutoff1,backend)757 DEFINE_TESTCASE(cutoff1, backend) {
758 Xapian::Enquire enquire(get_database("apitest_simpledata"));
759 enquire.set_query(query(Xapian::Query::OP_OR,
760 "this", "line", "paragraph", "rubbish"));
761 Xapian::MSet mymset1 = enquire.get_mset(0, 100);
762
763 if (verbose) {
764 tout << "Original mset weights:";
765 print_mset_weights(mymset1);
766 tout << "\n";
767 }
768
769 unsigned int num_items = 0;
770 Xapian::weight my_wt = -100;
771 int changes = 0;
772 Xapian::MSetIterator i = mymset1.begin();
773 int c = 0;
774 for ( ; i != mymset1.end(); ++i, ++c) {
775 Xapian::weight new_wt = i.get_weight();
776 if (new_wt != my_wt) {
777 changes++;
778 if (changes > 3) break;
779 num_items = c;
780 my_wt = new_wt;
781 }
782 }
783
784 TEST_AND_EXPLAIN(changes > 3, "MSet not varied enough to test");
785 if (verbose) {
786 tout << "Cutoff weight: " << my_wt << "\n";
787 }
788
789 enquire.set_cutoff(0, my_wt);
790 Xapian::MSet mymset2 = enquire.get_mset(0, 100);
791
792 if (verbose) {
793 tout << "Weights after cutoff:";
794 print_mset_weights(mymset2);
795 tout << "\n";
796 }
797
798 TEST_AND_EXPLAIN(mymset2.size() >= num_items,
799 "Match with cutoff lost too many items");
800
801 TEST_AND_EXPLAIN(mymset2.size() == num_items ||
802 (mymset2[num_items].get_weight() == my_wt &&
803 mymset2.back().get_weight() == my_wt),
804 "Match with cutoff returned too many items");
805
806 return true;
807 }
808
809 // tests the allow query terms expand option
DEFINE_TESTCASE(allowqterms1,backend)810 DEFINE_TESTCASE(allowqterms1, backend) {
811 Xapian::Enquire enquire(get_database("apitest_simpledata"));
812 string term = "paragraph";
813 enquire.set_query(Xapian::Query(term));
814
815 Xapian::MSet mymset = enquire.get_mset(0, 10);
816 TEST(mymset.size() >= 2);
817
818 Xapian::RSet myrset;
819 Xapian::MSetIterator i = mymset.begin();
820 myrset.add_document(*i);
821 myrset.add_document(*(++i));
822
823 Xapian::ESet myeset = enquire.get_eset(1000, myrset);
824 Xapian::ESetIterator j = myeset.begin();
825 for ( ; j != myeset.end(); ++j) {
826 TEST_NOT_EQUAL(*j, term);
827 }
828
829 Xapian::ESet myeset2 = enquire.get_eset(1000, myrset, Xapian::Enquire::INCLUDE_QUERY_TERMS);
830 j = myeset2.begin();
831 for ( ; j != myeset2.end(); ++j) {
832 if (*j == term) break;
833 }
834 TEST(j != myeset2.end());
835 return true;
836 }
837
838 // tests that the MSet max_attained works
DEFINE_TESTCASE(maxattain1,backend)839 DEFINE_TESTCASE(maxattain1, backend) {
840 Xapian::Enquire enquire(get_database("apitest_simpledata"));
841 enquire.set_query(query("this"));
842 Xapian::MSet mymset = enquire.get_mset(0, 100);
843
844 Xapian::weight mymax = 0;
845 Xapian::MSetIterator i = mymset.begin();
846 for ( ; i != mymset.end(); ++i) {
847 if (i.get_weight() > mymax) mymax = i.get_weight();
848 }
849 TEST_EQUAL(mymax, mymset.get_max_attained());
850
851 return true;
852 }
853
854 // tests a reversed boolean query
DEFINE_TESTCASE(reversebool1,backend)855 DEFINE_TESTCASE(reversebool1, backend) {
856 Xapian::Enquire enquire(get_database("apitest_simpledata"));
857 enquire.set_query(Xapian::Query("this"));
858 enquire.set_weighting_scheme(Xapian::BoolWeight());
859
860 Xapian::MSet mymset1 = enquire.get_mset(0, 100);
861 TEST_AND_EXPLAIN(mymset1.size() > 1,
862 "Mset was too small to test properly");
863
864 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
865 Xapian::MSet mymset2 = enquire.get_mset(0, 100);
866 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
867 Xapian::MSet mymset3 = enquire.get_mset(0, 100);
868
869 // mymset1 and mymset2 should be identical
870 TEST_EQUAL(mymset1.size(), mymset2.size());
871
872 {
873 Xapian::MSetIterator i = mymset1.begin();
874 Xapian::MSetIterator j = mymset2.begin();
875 for ( ; i != mymset1.end(); ++i, j++) {
876 TEST(j != mymset2.end());
877 // if this fails, then setting match_sort_forward=true was not
878 // the same as the default.
879 TEST_EQUAL(*i, *j);
880 }
881 TEST(j == mymset2.end());
882 }
883
884 // mymset1 and mymset3 should be same but reversed
885 TEST_EQUAL(mymset1.size(), mymset3.size());
886
887 {
888 Xapian::MSetIterator i = mymset1.begin();
889 Xapian::MSetIterator j = mymset3.end();
890 for ( ; i != mymset1.end(); ++i) {
891 // if this fails, then setting match_sort_forward=false didn't
892 // reverse the results.
893 TEST_EQUAL(*i, *--j);
894 }
895 }
896
897 return true;
898 }
899
900 // tests a reversed boolean query, where the full mset isn't returned
DEFINE_TESTCASE(reversebool2,backend)901 DEFINE_TESTCASE(reversebool2, backend) {
902 Xapian::Enquire enquire(get_database("apitest_simpledata"));
903 enquire.set_query(Xapian::Query("this"));
904 enquire.set_weighting_scheme(Xapian::BoolWeight());
905
906 Xapian::MSet mymset1 = enquire.get_mset(0, 100);
907
908 TEST_AND_EXPLAIN(mymset1.size() > 1,
909 "Mset was too small to test properly");
910
911 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
912 Xapian::doccount msize = mymset1.size() / 2;
913 Xapian::MSet mymset2 = enquire.get_mset(0, msize);
914 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
915 Xapian::MSet mymset3 = enquire.get_mset(0, msize);
916
917 // mymset2 should be first msize items of mymset1
918 TEST_EQUAL(msize, mymset2.size());
919 {
920 Xapian::MSetIterator i = mymset1.begin();
921 Xapian::MSetIterator j = mymset2.begin();
922 for ( ; j != mymset2.end(); ++i, j++) {
923 TEST(i != mymset1.end());
924 // if this fails, then setting match_sort_forward=true was not
925 // the same as the default.
926 TEST_EQUAL(*i, *j);
927 }
928 // mymset1 should be larger.
929 TEST(i != mymset1.end());
930 }
931
932 // mymset3 should be last msize items of mymset1, in reverse order
933 TEST_EQUAL(msize, mymset3.size());
934 {
935 Xapian::MSetIterator i = mymset1.end();
936 Xapian::MSetIterator j;
937 for (j = mymset3.begin(); j != mymset3.end(); j++) {
938 // if this fails, then setting match_sort_forward=false didn't
939 // reverse the results.
940 TEST_EQUAL(*--i, *j);
941 }
942 }
943
944 return true;
945 }
946
947 // tests that get_matching_terms() returns the terms in the right order
DEFINE_TESTCASE(getmterms1,backend)948 DEFINE_TESTCASE(getmterms1, backend) {
949 list<string> answers_list;
950 answers_list.push_back("one");
951 answers_list.push_back("two");
952 answers_list.push_back("three");
953 answers_list.push_back("four");
954
955 Xapian::Database mydb(get_database("apitest_termorder"));
956 Xapian::Enquire enquire(mydb);
957
958 Xapian::Query myquery(Xapian::Query::OP_OR,
959 Xapian::Query(Xapian::Query::OP_AND,
960 Xapian::Query("one", 1, 1),
961 Xapian::Query("three", 1, 3)),
962 Xapian::Query(Xapian::Query::OP_OR,
963 Xapian::Query("four", 1, 4),
964 Xapian::Query("two", 1, 2)));
965
966 enquire.set_query(myquery);
967
968 Xapian::MSet mymset = enquire.get_mset(0, 10);
969
970 TEST_MSET_SIZE(mymset, 1);
971 list<string> list(enquire.get_matching_terms_begin(mymset.begin()),
972 enquire.get_matching_terms_end(mymset.begin()));
973 TEST(list == answers_list);
974
975 return true;
976 }
977
978 // tests that get_matching_terms() returns the terms only once
DEFINE_TESTCASE(getmterms2,backend)979 DEFINE_TESTCASE(getmterms2, backend) {
980 list<string> answers_list;
981 answers_list.push_back("one");
982 answers_list.push_back("two");
983 answers_list.push_back("three");
984
985 Xapian::Database mydb(get_database("apitest_termorder"));
986 Xapian::Enquire enquire(mydb);
987
988 Xapian::Query myquery(Xapian::Query::OP_OR,
989 Xapian::Query(Xapian::Query::OP_AND,
990 Xapian::Query("one", 1, 1),
991 Xapian::Query("three", 1, 3)),
992 Xapian::Query(Xapian::Query::OP_OR,
993 Xapian::Query("one", 1, 4),
994 Xapian::Query("two", 1, 2)));
995
996 enquire.set_query(myquery);
997
998 Xapian::MSet mymset = enquire.get_mset(0, 10);
999
1000 TEST_MSET_SIZE(mymset, 1);
1001 list<string> list(enquire.get_matching_terms_begin(mymset.begin()),
1002 enquire.get_matching_terms_end(mymset.begin()));
1003 TEST(list == answers_list);
1004
1005 return true;
1006 }
1007
1008 // tests that the collapsing on termpos optimisation works
DEFINE_TESTCASE(poscollapse1,backend)1009 DEFINE_TESTCASE(poscollapse1, backend) {
1010 Xapian::Query myquery1(Xapian::Query::OP_OR,
1011 Xapian::Query("this", 1, 1),
1012 Xapian::Query("this", 1, 1));
1013 Xapian::Query myquery2("this", 2, 1);
1014
1015 if (verbose) {
1016 tout << myquery1.get_description() << "\n";
1017 tout << myquery2.get_description() << "\n";
1018 }
1019
1020 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1021 enquire.set_query(myquery1);
1022 Xapian::MSet mymset1 = enquire.get_mset(0, 10);
1023
1024 enquire.set_query(myquery2);
1025 Xapian::MSet mymset2 = enquire.get_mset(0, 10);
1026
1027 TEST_EQUAL(mymset1, mymset2);
1028
1029 return true;
1030 }
1031
1032 // test that running a query twice returns the same results
DEFINE_TESTCASE(repeatquery1,backend)1033 DEFINE_TESTCASE(repeatquery1, backend) {
1034 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1035 enquire.set_query(Xapian::Query("this"));
1036
1037 enquire.set_query(query(Xapian::Query::OP_OR, "this", "word"));
1038
1039 Xapian::MSet mymset1 = enquire.get_mset(0, 10);
1040 Xapian::MSet mymset2 = enquire.get_mset(0, 10);
1041 TEST_EQUAL(mymset1, mymset2);
1042
1043 return true;
1044 }
1045
1046 // test that prefetching documents works (at least, gives same results)
DEFINE_TESTCASE(fetchdocs1,backend)1047 DEFINE_TESTCASE(fetchdocs1, backend) {
1048 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1049 enquire.set_query(Xapian::Query("this"));
1050
1051 enquire.set_query(query(Xapian::Query::OP_OR, "this", "word"));
1052
1053 Xapian::MSet mymset1 = enquire.get_mset(0, 10);
1054 Xapian::MSet mymset2 = enquire.get_mset(0, 10);
1055 TEST_EQUAL(mymset1, mymset2);
1056 mymset2.fetch(mymset2[0], mymset2[mymset2.size() - 1]);
1057 mymset2.fetch(mymset2.begin(), mymset2.end());
1058 mymset2.fetch(mymset2.begin());
1059 mymset2.fetch();
1060
1061 Xapian::MSetIterator it1 = mymset1.begin();
1062 Xapian::MSetIterator it2 = mymset2.begin();
1063
1064 while (it1 != mymset1.end() && it2 != mymset2.end()) {
1065 TEST_EQUAL(it1.get_document().get_data(),
1066 it2.get_document().get_data());
1067 TEST_NOT_EQUAL(it1.get_document().get_data(), "");
1068 TEST_NOT_EQUAL(it2.get_document().get_data(), "");
1069 it1++;
1070 it2++;
1071 }
1072 TEST_EQUAL(it1, mymset1.end());
1073 TEST_EQUAL(it1, mymset2.end());
1074
1075 return true;
1076 }
1077
1078 // test that searching for a term not in the database fails nicely
DEFINE_TESTCASE(absentterm1,backend)1079 DEFINE_TESTCASE(absentterm1, backend) {
1080 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1081 enquire.set_weighting_scheme(Xapian::BoolWeight());
1082 enquire.set_query(Xapian::Query("frink"));
1083
1084 Xapian::MSet mymset = enquire.get_mset(0, 10);
1085 mset_expect_order(mymset);
1086
1087 return true;
1088 }
1089
1090 // as absentterm1, but setting query from a vector of terms
DEFINE_TESTCASE(absentterm2,backend)1091 DEFINE_TESTCASE(absentterm2, backend) {
1092 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1093 vector<string> terms;
1094 terms.push_back("frink");
1095
1096 Xapian::Query query(Xapian::Query::OP_OR, terms.begin(), terms.end());
1097 enquire.set_query(query);
1098
1099 Xapian::MSet mymset = enquire.get_mset(0, 10);
1100 mset_expect_order(mymset);
1101
1102 return true;
1103 }
1104
1105 // test that rsets do sensible things
DEFINE_TESTCASE(rset1,backend)1106 DEFINE_TESTCASE(rset1, backend) {
1107 Xapian::Database mydb(get_database("apitest_rset"));
1108 Xapian::Enquire enquire(mydb);
1109 Xapian::Query myquery = query(Xapian::Query::OP_OR, "giraffe", "tiger");
1110 enquire.set_query(myquery);
1111
1112 Xapian::MSet mymset1 = enquire.get_mset(0, 10);
1113
1114 Xapian::RSet myrset;
1115 myrset.add_document(1);
1116
1117 Xapian::MSet mymset2 = enquire.get_mset(0, 10, &myrset);
1118
1119 // We should have the same documents turn up, but 1 and 3 should
1120 // have higher weights with the RSet.
1121 TEST_MSET_SIZE(mymset1, 3);
1122 TEST_MSET_SIZE(mymset2, 3);
1123
1124 return true;
1125 }
1126
1127 // test that rsets do more sensible things
DEFINE_TESTCASE(rset2,backend)1128 DEFINE_TESTCASE(rset2, backend) {
1129 Xapian::Database mydb(get_database("apitest_rset"));
1130 Xapian::Enquire enquire(mydb);
1131 Xapian::Query myquery = query(Xapian::Query::OP_OR, "cuddly", "people");
1132 enquire.set_query(myquery);
1133
1134 Xapian::MSet mymset1 = enquire.get_mset(0, 10);
1135
1136 Xapian::RSet myrset;
1137 myrset.add_document(2);
1138
1139 Xapian::MSet mymset2 = enquire.get_mset(0, 10, &myrset);
1140
1141 mset_expect_order(mymset1, 1, 2);
1142 mset_expect_order(mymset2, 2, 1);
1143
1144 return true;
1145 }
1146
1147 // test that rsets behave correctly with multiDBs
1148 DEFINE_TESTCASE(rsetmultidb1, backend && !multi) {
1149 Xapian::Database mydb1(get_database("apitest_rset", "apitest_simpledata2"));
1150 Xapian::Database mydb2(get_database("apitest_rset"));
1151 mydb2.add_database(get_database("apitest_simpledata2"));
1152
1153 Xapian::Enquire enquire1(mydb1);
1154 Xapian::Enquire enquire2(mydb2);
1155
1156 Xapian::Query myquery = query(Xapian::Query::OP_OR, "cuddly", "multiple");
1157
1158 enquire1.set_query(myquery);
1159 enquire2.set_query(myquery);
1160
1161 Xapian::RSet myrset1;
1162 Xapian::RSet myrset2;
1163 myrset1.add_document(4);
1164 myrset2.add_document(2);
1165
1166 Xapian::MSet mymset1a = enquire1.get_mset(0, 10);
1167 Xapian::MSet mymset1b = enquire1.get_mset(0, 10, &myrset1);
1168 Xapian::MSet mymset2a = enquire2.get_mset(0, 10);
1169 Xapian::MSet mymset2b = enquire2.get_mset(0, 10, &myrset2);
1170
1171 mset_expect_order(mymset1a, 1, 4);
1172 mset_expect_order(mymset1b, 4, 1);
1173 mset_expect_order(mymset2a, 1, 2);
1174 mset_expect_order(mymset2b, 2, 1);
1175
1176 TEST(mset_range_is_same_weights(mymset1a, 0, mymset2a, 0, 2));
1177 TEST(mset_range_is_same_weights(mymset1b, 0, mymset2b, 0, 2));
1178 TEST_NOT_EQUAL(mymset1a, mymset1b);
1179 TEST_NOT_EQUAL(mymset2a, mymset2b);
1180
1181 return true;
1182 }
1183
1184 // regression tests - used to cause assertion in stats.h to fail
1185 // Doesn't actually fail for multi but it doesn't make sense to run there.
1186 DEFINE_TESTCASE(rsetmultidb3, backend && !multi) {
1187 Xapian::Enquire enquire(get_database("apitest_simpledata2"));
1188 enquire.set_query(query(Xapian::Query::OP_OR, "cuddly", "people"));
1189 Xapian::MSet mset = enquire.get_mset(0, 10); // used to fail assertion
1190 return true;
1191 }
1192
1193 /// Simple test of the elite set operator.
DEFINE_TESTCASE(eliteset1,backend)1194 DEFINE_TESTCASE(eliteset1, backend) {
1195 // FIXME: OP_ELITE_SET erroneously picks the best N terms separately in
1196 // each sub-database!
1197 SKIP_TEST_FOR_BACKEND("multi");
1198
1199 Xapian::Database mydb(get_database("apitest_simpledata"));
1200 Xapian::Enquire enquire(mydb);
1201
1202 Xapian::Query myquery1 = query(Xapian::Query::OP_OR, "word");
1203
1204 Xapian::Query myquery2 = query(Xapian::Query::OP_ELITE_SET, 1,
1205 "simple", "word");
1206
1207 enquire.set_query(myquery1, 2); // So the query lengths are the same.
1208 Xapian::MSet mymset1 = enquire.get_mset(0, 10);
1209
1210 enquire.set_query(myquery2);
1211 Xapian::MSet mymset2 = enquire.get_mset(0, 10);
1212
1213 TEST_EQUAL(mymset1, mymset2);
1214 return true;
1215 }
1216
1217 /// Test that the elite set operator works if the set contains
1218 /// sub-expressions (regression test)
DEFINE_TESTCASE(eliteset2,backend)1219 DEFINE_TESTCASE(eliteset2, backend) {
1220 // FIXME: OP_ELITE_SET erroneously picks the best N terms separately in
1221 // each sub-database!
1222 SKIP_TEST_FOR_BACKEND("multi");
1223
1224 Xapian::Database mydb(get_database("apitest_simpledata"));
1225 Xapian::Enquire enquire(mydb);
1226
1227 Xapian::Query myquery1 = query(Xapian::Query::OP_AND, "word", "search");
1228
1229 vector<Xapian::Query> qs;
1230 qs.push_back(query("this"));
1231 qs.push_back(query(Xapian::Query::OP_AND, "word", "search"));
1232 Xapian::Query myquery2(Xapian::Query::OP_ELITE_SET,
1233 qs.begin(), qs.end(), 1);
1234
1235 enquire.set_query(myquery1);
1236 Xapian::MSet mymset1 = enquire.get_mset(0, 10);
1237
1238 enquire.set_query(myquery2);
1239 Xapian::MSet mymset2 = enquire.get_mset(0, 10);
1240
1241 TEST_EQUAL(mymset1, mymset2);
1242 // query lengths differ so mset weights not the same (with some weighting
1243 // parameters)
1244 //test_mset_order_equal(mymset1, mymset2);
1245
1246 return true;
1247 }
1248
1249 /// Test that elite set doesn't affect query results if we have fewer
1250 /// terms than the threshold
DEFINE_TESTCASE(eliteset3,backend)1251 DEFINE_TESTCASE(eliteset3, backend) {
1252 Xapian::Database mydb1(get_database("apitest_simpledata"));
1253 Xapian::Enquire enquire1(mydb1);
1254
1255 Xapian::Database mydb2(get_database("apitest_simpledata"));
1256 Xapian::Enquire enquire2(mydb2);
1257
1258 // make a query
1259 Xapian::Stem stemmer("english");
1260
1261 string term1 = stemmer("word");
1262 string term2 = stemmer("rubbish");
1263 string term3 = stemmer("banana");
1264
1265 vector<string> terms;
1266 terms.push_back(term1);
1267 terms.push_back(term2);
1268 terms.push_back(term3);
1269
1270 Xapian::Query myquery1(Xapian::Query::OP_OR, terms.begin(), terms.end());
1271 enquire1.set_query(myquery1);
1272
1273 Xapian::Query myquery2(Xapian::Query::OP_ELITE_SET, terms.begin(), terms.end(), 3);
1274 enquire2.set_query(myquery2);
1275
1276 // retrieve the results
1277 Xapian::MSet mymset1 = enquire1.get_mset(0, 10);
1278 Xapian::MSet mymset2 = enquire2.get_mset(0, 10);
1279
1280 TEST_EQUAL(mymset1.get_termfreq(term1),
1281 mymset2.get_termfreq(term1));
1282 TEST_EQUAL(mymset1.get_termweight(term1),
1283 mymset2.get_termweight(term1));
1284 TEST_EQUAL(mymset1.get_termfreq(term2),
1285 mymset2.get_termfreq(term2));
1286 TEST_EQUAL(mymset1.get_termweight(term2),
1287 mymset2.get_termweight(term2));
1288 TEST_EQUAL(mymset1.get_termfreq(term3),
1289 mymset2.get_termfreq(term3));
1290 TEST_EQUAL(mymset1.get_termweight(term3),
1291 mymset2.get_termweight(term3));
1292 // TEST_EQUAL(mymset1, mymset2);
1293
1294 return true;
1295 }
1296
1297 /// Test that elite set doesn't pick terms with 0 frequency
DEFINE_TESTCASE(eliteset4,backend)1298 DEFINE_TESTCASE(eliteset4, backend) {
1299 // FIXME: OP_ELITE_SET erroneously picks the best N terms separately in
1300 // each sub-database!
1301 SKIP_TEST_FOR_BACKEND("multi");
1302
1303 Xapian::Database mydb1(get_database("apitest_simpledata"));
1304 Xapian::Enquire enquire1(mydb1);
1305
1306 Xapian::Database mydb2(get_database("apitest_simpledata"));
1307 Xapian::Enquire enquire2(mydb2);
1308
1309 Xapian::Query myquery1 = query("rubbish");
1310 Xapian::Query myquery2 = query(Xapian::Query::OP_ELITE_SET, 1,
1311 "word", "rubbish", "fibble");
1312 enquire1.set_query(myquery1);
1313 enquire2.set_query(myquery2);
1314
1315 // retrieve the results
1316 Xapian::MSet mymset1 = enquire1.get_mset(0, 10);
1317 Xapian::MSet mymset2 = enquire2.get_mset(0, 10);
1318
1319 TEST_NOT_EQUAL(mymset2.size(), 0);
1320 TEST_EQUAL(mymset1, mymset2);
1321 // TEST_EQUAL(mymset1, mymset2);
1322
1323 return true;
1324 }
1325
1326 /// Regression test for problem with excess precision.
DEFINE_TESTCASE(eliteset5,backend)1327 DEFINE_TESTCASE(eliteset5, backend) {
1328 SKIP_TEST_FOR_BACKEND("multi");
1329
1330 Xapian::Database mydb1(get_database("apitest_simpledata"));
1331 Xapian::Enquire enquire1(mydb1);
1332
1333 vector<string> v;
1334 for (int i = 0; i != 3; ++i) {
1335 v.push_back("simpl");
1336 v.push_back("queri");
1337
1338 v.push_back("rubbish");
1339 v.push_back("rubbish");
1340 v.push_back("rubbish");
1341 v.push_back("word");
1342 v.push_back("word");
1343 v.push_back("word");
1344 }
1345
1346 Xapian::Query myquery1 = Xapian::Query(Xapian::Query::OP_ELITE_SET,
1347 v.begin(), v.end(), 1);
1348 myquery1 = Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT,
1349 myquery1,
1350 0.004);
1351
1352 enquire1.set_query(myquery1);
1353 // On architectures with excess precision (or, at least, on x86), the
1354 // following call used to result in a segfault.
1355 enquire1.get_mset(0, 10);
1356
1357 return true;
1358 }
1359
1360 /// Test that the termfreq returned by termlists is correct.
DEFINE_TESTCASE(termlisttermfreq1,backend)1361 DEFINE_TESTCASE(termlisttermfreq1, backend) {
1362 Xapian::Database mydb(get_database("apitest_simpledata"));
1363 Xapian::Enquire enquire(mydb);
1364 Xapian::Stem stemmer("english");
1365 Xapian::RSet rset1;
1366 Xapian::RSet rset2;
1367 rset1.add_document(5);
1368 rset2.add_document(6);
1369
1370 Xapian::ESet eset1 = enquire.get_eset(1000, rset1);
1371 Xapian::ESet eset2 = enquire.get_eset(1000, rset2);
1372
1373 // search for weight of term 'another'
1374 string theterm = stemmer("another");
1375
1376 Xapian::weight wt1 = 0;
1377 Xapian::weight wt2 = 0;
1378 {
1379 Xapian::ESetIterator i = eset1.begin();
1380 for ( ; i != eset1.end(); i++) {
1381 if (*i == theterm) {
1382 wt1 = i.get_weight();
1383 break;
1384 }
1385 }
1386 }
1387 {
1388 Xapian::ESetIterator i = eset2.begin();
1389 for ( ; i != eset2.end(); i++) {
1390 if (*i == theterm) {
1391 wt2 = i.get_weight();
1392 break;
1393 }
1394 }
1395 }
1396
1397 TEST_NOT_EQUAL(wt1, 0);
1398 TEST_NOT_EQUAL(wt2, 0);
1399 TEST_EQUAL(wt1, wt2);
1400
1401 return true;
1402 }
1403
1404 /// Test the termfrequency and termweight info returned for query terms
DEFINE_TESTCASE(qterminfo1,backend)1405 DEFINE_TESTCASE(qterminfo1, backend) {
1406 Xapian::Database mydb1(get_database("apitest_simpledata", "apitest_simpledata2"));
1407 Xapian::Enquire enquire1(mydb1);
1408
1409 Xapian::Database mydb2(get_database("apitest_simpledata"));
1410 mydb2.add_database(get_database("apitest_simpledata2"));
1411 Xapian::Enquire enquire2(mydb2);
1412
1413 // make a query
1414 Xapian::Stem stemmer("english");
1415
1416 string term1 = stemmer("word");
1417 string term2 = stemmer("inmemory");
1418 string term3 = stemmer("flibble");
1419
1420 Xapian::Query myquery(Xapian::Query::OP_OR,
1421 Xapian::Query(term1),
1422 Xapian::Query(Xapian::Query::OP_OR,
1423 Xapian::Query(term2),
1424 Xapian::Query(term3)));
1425 enquire1.set_query(myquery);
1426 enquire2.set_query(myquery);
1427
1428 // retrieve the results
1429 Xapian::MSet mymset1a = enquire1.get_mset(0, 0);
1430 Xapian::MSet mymset2a = enquire2.get_mset(0, 0);
1431
1432 TEST_EQUAL(mymset1a.get_termfreq(term1),
1433 mymset2a.get_termfreq(term1));
1434 TEST_EQUAL(mymset1a.get_termfreq(term2),
1435 mymset2a.get_termfreq(term2));
1436 TEST_EQUAL(mymset1a.get_termfreq(term3),
1437 mymset2a.get_termfreq(term3));
1438
1439 TEST_EQUAL(mymset1a.get_termfreq(term1), 3);
1440 TEST_EQUAL(mymset1a.get_termfreq(term2), 1);
1441 TEST_EQUAL(mymset1a.get_termfreq(term3), 0);
1442
1443 TEST_NOT_EQUAL(mymset1a.get_termweight(term1), 0);
1444 TEST_NOT_EQUAL(mymset1a.get_termweight(term2), 0);
1445 // non-existent terms should have 0 weight.
1446 TEST_EQUAL(mymset1a.get_termweight(term3), 0);
1447
1448 TEST_EQUAL(mymset1a.get_termfreq(stemmer("banana")), 1);
1449 TEST_EXCEPTION(Xapian::InvalidArgumentError,
1450 mymset1a.get_termweight(stemmer("banana")));
1451
1452 TEST_EQUAL(mymset1a.get_termfreq("sponge"), 0);
1453 TEST_EXCEPTION(Xapian::InvalidArgumentError,
1454 mymset1a.get_termweight("sponge"));
1455
1456 return true;
1457 }
1458
1459 /// Regression test for bug #37.
DEFINE_TESTCASE(qterminfo2,backend)1460 DEFINE_TESTCASE(qterminfo2, backend) {
1461 Xapian::Database db(get_database("apitest_simpledata"));
1462 Xapian::Enquire enquire(db);
1463
1464 // make a query
1465 Xapian::Stem stemmer("english");
1466
1467 string term1 = stemmer("paragraph");
1468 string term2 = stemmer("another");
1469
1470 Xapian::Query query(Xapian::Query::OP_AND_NOT, term1,
1471 Xapian::Query(Xapian::Query::OP_AND, term1, term2));
1472 enquire.set_query(query);
1473
1474 // retrieve the results
1475 // Note: get_mset() used to throw "AssertionError" in debug builds
1476 Xapian::MSet mset = enquire.get_mset(0, 10);
1477
1478 TEST_NOT_EQUAL(mset.get_termweight("paragraph"), 0);
1479
1480 return true;
1481 }
1482
1483 // tests that when specifying that no items are to be returned, those
1484 // statistics which should be the same are.
DEFINE_TESTCASE(msetzeroitems1,backend)1485 DEFINE_TESTCASE(msetzeroitems1, backend) {
1486 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1487 enquire.set_query(query("this"));
1488 Xapian::MSet mymset1 = enquire.get_mset(0, 0);
1489
1490 Xapian::MSet mymset2 = enquire.get_mset(0, 1);
1491
1492 TEST_EQUAL(mymset1.get_max_possible(), mymset2.get_max_possible());
1493
1494 return true;
1495 }
1496
1497 // test that the matches_* of a simple query are as expected
DEFINE_TESTCASE(matches1,backend)1498 DEFINE_TESTCASE(matches1, backend) {
1499 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1500 Xapian::Query myquery;
1501 Xapian::MSet mymset;
1502
1503 myquery = query("word");
1504 enquire.set_query(myquery);
1505 mymset = enquire.get_mset(0, 10);
1506 TEST_EQUAL(mymset.get_matches_lower_bound(), 2);
1507 TEST_EQUAL(mymset.get_matches_estimated(), 2);
1508 TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
1509 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 2);
1510 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 2);
1511 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 2);
1512
1513 myquery = query(Xapian::Query::OP_OR, "inmemory", "word");
1514 enquire.set_query(myquery);
1515 mymset = enquire.get_mset(0, 10);
1516 TEST_EQUAL(mymset.get_matches_lower_bound(), 2);
1517 TEST_EQUAL(mymset.get_matches_estimated(), 2);
1518 TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
1519 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 2);
1520 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 2);
1521 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 2);
1522
1523 myquery = query(Xapian::Query::OP_AND, "inmemory", "word");
1524 enquire.set_query(myquery);
1525 mymset = enquire.get_mset(0, 10);
1526 TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
1527 TEST_EQUAL(mymset.get_matches_estimated(), 0);
1528 TEST_EQUAL(mymset.get_matches_upper_bound(), 0);
1529 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 0);
1530 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 0);
1531 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 0);
1532
1533 myquery = query(Xapian::Query::OP_AND, "simple", "word");
1534 enquire.set_query(myquery);
1535 mymset = enquire.get_mset(0, 10);
1536 TEST_EQUAL(mymset.get_matches_lower_bound(), 2);
1537 TEST_EQUAL(mymset.get_matches_estimated(), 2);
1538 TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
1539 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 2);
1540 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 2);
1541 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 2);
1542
1543 myquery = query(Xapian::Query::OP_AND, "simple", "word");
1544 enquire.set_query(myquery);
1545 mymset = enquire.get_mset(0, 0);
1546 // For a single database, this is true, but not for "multi" (since there
1547 // one sub-database has 3 documents and simple and word both have termfreq
1548 // of 2, so the matcher can tell at least one document must match!)
1549 // TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
1550 TEST_REL(mymset.get_matches_lower_bound(),<=,mymset.get_matches_estimated());
1551 TEST_EQUAL(mymset.get_matches_estimated(), 1);
1552 TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
1553 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),<=,mymset.get_uncollapsed_matches_estimated());
1554 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 1);
1555 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 2);
1556
1557 mymset = enquire.get_mset(0, 1);
1558 TEST_EQUAL(mymset.get_matches_lower_bound(), 2);
1559 TEST_EQUAL(mymset.get_matches_estimated(), 2);
1560 TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
1561 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 2);
1562 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 2);
1563 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 2);
1564
1565 mymset = enquire.get_mset(0, 2);
1566 TEST_EQUAL(mymset.get_matches_lower_bound(), 2);
1567 TEST_EQUAL(mymset.get_matches_estimated(), 2);
1568 TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
1569 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 2);
1570 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 2);
1571 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 2);
1572
1573 myquery = query(Xapian::Query::OP_AND, "paragraph", "another");
1574 enquire.set_query(myquery);
1575 mymset = enquire.get_mset(0, 0);
1576 TEST_EQUAL(mymset.get_matches_lower_bound(), 1);
1577 TEST_EQUAL(mymset.get_matches_estimated(), 2);
1578 TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
1579 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 1);
1580 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 2);
1581 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 2);
1582
1583 mymset = enquire.get_mset(0, 1);
1584 TEST_EQUAL(mymset.get_matches_lower_bound(), 1);
1585 TEST_EQUAL(mymset.get_matches_estimated(), 2);
1586 TEST_EQUAL(mymset.get_matches_upper_bound(), 2);
1587 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 1);
1588 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 2);
1589 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 2);
1590
1591 mymset = enquire.get_mset(0, 2);
1592 TEST_EQUAL(mymset.get_matches_lower_bound(), 1);
1593 TEST_EQUAL(mymset.get_matches_estimated(), 1);
1594 TEST_EQUAL(mymset.get_matches_upper_bound(), 1);
1595 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 1);
1596 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 1);
1597 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 1);
1598
1599 mymset = enquire.get_mset(1, 20);
1600 TEST_EQUAL(mymset.get_matches_lower_bound(), 1);
1601 TEST_EQUAL(mymset.get_matches_estimated(), 1);
1602 TEST_EQUAL(mymset.get_matches_upper_bound(), 1);
1603 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 1);
1604 TEST_EQUAL(mymset.get_uncollapsed_matches_estimated(), 1);
1605 TEST_EQUAL(mymset.get_uncollapsed_matches_upper_bound(), 1);
1606
1607 return true;
1608 }
1609
1610 // tests that wqf affects the document weights
DEFINE_TESTCASE(wqf1,backend)1611 DEFINE_TESTCASE(wqf1, backend) {
1612 // Both queries have length 2; in q1 word has wqf=2, in q2 word has wqf=1
1613 Xapian::Query q1("word", 2);
1614 Xapian::Query q2("word");
1615 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1616 enquire.set_query(q1);
1617 Xapian::MSet mset1 = enquire.get_mset(0, 10);
1618 enquire.set_query(q2);
1619 Xapian::MSet mset2 = enquire.get_mset(0, 2);
1620 // Check the weights
1621 TEST(mset1.begin().get_weight() > mset2.begin().get_weight());
1622 return true;
1623 }
1624
1625 // tests that query length affects the document weights
DEFINE_TESTCASE(qlen1,backend)1626 DEFINE_TESTCASE(qlen1, backend) {
1627 Xapian::Query q1("word");
1628 Xapian::Query q2("word");
1629 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1630 enquire.set_query(q1);
1631 Xapian::MSet mset1 = enquire.get_mset(0, 10);
1632 enquire.set_query(q2);
1633 Xapian::MSet mset2 = enquire.get_mset(0, 2);
1634 // Check the weights
1635 //TEST(mset1.begin().get_weight() < mset2.begin().get_weight());
1636 TEST(mset1.begin().get_weight() == mset2.begin().get_weight());
1637 return true;
1638 }
1639
1640 // tests that opening a non-existent termlist throws the correct exception
DEFINE_TESTCASE(termlist1,backend)1641 DEFINE_TESTCASE(termlist1, backend) {
1642 Xapian::Database db(get_database("apitest_onedoc"));
1643 TEST_EXCEPTION(Xapian::InvalidArgumentError,
1644 Xapian::TermIterator t = db.termlist_begin(0));
1645 TEST_EXCEPTION(Xapian::DocNotFoundError,
1646 Xapian::TermIterator t = db.termlist_begin(2));
1647 /* Cause the database to be used properly, showing up problems
1648 * with the link being in a bad state. CME */
1649 Xapian::TermIterator temp = db.termlist_begin(1);
1650 TEST_EXCEPTION(Xapian::DocNotFoundError,
1651 Xapian::TermIterator t = db.termlist_begin(999999999));
1652 return true;
1653 }
1654
1655 // tests that a Xapian::TermIterator works as an STL iterator
DEFINE_TESTCASE(termlist2,backend)1656 DEFINE_TESTCASE(termlist2, backend) {
1657 Xapian::Database db(get_database("apitest_onedoc"));
1658 Xapian::TermIterator t = db.termlist_begin(1);
1659 Xapian::TermIterator tend = db.termlist_end(1);
1660
1661 // test operator= creates a copy which compares equal
1662 Xapian::TermIterator t_copy = t;
1663 TEST_EQUAL(t, t_copy);
1664
1665 // test copy constructor creates a copy which compares equal
1666 Xapian::TermIterator t_clone(t);
1667 TEST_EQUAL(t, t_clone);
1668
1669 vector<string> v(t, tend);
1670
1671 t = db.termlist_begin(1);
1672 tend = db.termlist_end(1);
1673 vector<string>::const_iterator i;
1674 for (i = v.begin(); i != v.end(); ++i) {
1675 TEST_NOT_EQUAL(t, tend);
1676 TEST_EQUAL(*i, *t);
1677 t++;
1678 }
1679 TEST_EQUAL(t, tend);
1680 return true;
1681 }
1682
1683 static Xapian::TermIterator
test_termlist3_helper()1684 test_termlist3_helper()
1685 {
1686 Xapian::Database db(get_database("apitest_onedoc"));
1687 return db.termlist_begin(1);
1688 }
1689
1690 // tests that a Xapian::TermIterator still works when the DB is deleted
DEFINE_TESTCASE(termlist3,backend)1691 DEFINE_TESTCASE(termlist3, backend) {
1692 Xapian::TermIterator u = test_termlist3_helper();
1693 Xapian::Database db(get_database("apitest_onedoc"));
1694 Xapian::TermIterator t = db.termlist_begin(1);
1695 Xapian::TermIterator tend = db.termlist_end(1);
1696
1697 while (t != tend) {
1698 TEST_EQUAL(*t, *u);
1699 t++;
1700 u++;
1701 }
1702 return true;
1703 }
1704
1705 // tests skip_to
DEFINE_TESTCASE(termlist4,backend)1706 DEFINE_TESTCASE(termlist4, backend) {
1707 Xapian::Database db(get_database("apitest_onedoc"));
1708 Xapian::TermIterator i = db.termlist_begin(1);
1709 i.skip_to("");
1710 i.skip_to("\xff");
1711 return true;
1712 }
1713
1714 // tests punctuation is OK in terms (particularly in remote queries)
DEFINE_TESTCASE(puncterms1,backend)1715 DEFINE_TESTCASE(puncterms1, backend) {
1716 Xapian::Database db(get_database("apitest_punc"));
1717 Xapian::Enquire enquire(db);
1718
1719 Xapian::Query q1("semi;colon");
1720 enquire.set_query(q1);
1721 Xapian::MSet m1 = enquire.get_mset(0, 10);
1722
1723 Xapian::Query q2("col:on");
1724 enquire.set_query(q2);
1725 Xapian::MSet m2 = enquire.get_mset(0, 10);
1726
1727 Xapian::Query q3("com,ma");
1728 enquire.set_query(q3);
1729 Xapian::MSet m3 = enquire.get_mset(0, 10);
1730
1731 return true;
1732 }
1733
1734 // test that searching for a term with a space or backslash in it works
DEFINE_TESTCASE(spaceterms1,backend)1735 DEFINE_TESTCASE(spaceterms1, backend) {
1736 Xapian::Enquire enquire(get_database("apitest_space"));
1737 Xapian::MSet mymset;
1738 Xapian::doccount count;
1739 Xapian::MSetIterator m;
1740 Xapian::Stem stemmer("english");
1741
1742 enquire.set_query(stemmer("space man"));
1743 mymset = enquire.get_mset(0, 10);
1744 TEST_MSET_SIZE(mymset, 1);
1745 count = 0;
1746 for (m = mymset.begin(); m != mymset.end(); ++m) ++count;
1747 TEST_EQUAL(count, 1);
1748
1749 for (Xapian::valueno value_no = 1; value_no < 7; ++value_no) {
1750 TEST_NOT_EQUAL(mymset.begin().get_document().get_data(), "");
1751 TEST_NOT_EQUAL(mymset.begin().get_document().get_value(value_no), "");
1752 }
1753
1754 enquire.set_query(stemmer("tab\tby"));
1755 mymset = enquire.get_mset(0, 10);
1756 TEST_MSET_SIZE(mymset, 1);
1757 count = 0;
1758 for (m = mymset.begin(); m != mymset.end(); ++m) ++count;
1759 TEST_EQUAL(count, 1);
1760
1761 for (Xapian::valueno value_no = 0; value_no < 7; ++value_no) {
1762 string value = mymset.begin().get_document().get_value(value_no);
1763 TEST_NOT_EQUAL(value, "");
1764 if (value_no == 0) {
1765 TEST(value.size() > 262);
1766 TEST_EQUAL(static_cast<unsigned char>(value[262]), 255);
1767 }
1768 }
1769
1770 enquire.set_query(stemmer("back\\slash"));
1771 mymset = enquire.get_mset(0, 10);
1772 TEST_MSET_SIZE(mymset, 1);
1773 count = 0;
1774 for (m = mymset.begin(); m != mymset.end(); ++m) ++count;
1775 TEST_EQUAL(count, 1);
1776
1777 return true;
1778 }
1779
1780 // test that XOR queries work
DEFINE_TESTCASE(xor1,backend)1781 DEFINE_TESTCASE(xor1, backend) {
1782 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1783 Xapian::Stem stemmer("english");
1784
1785 vector<string> terms;
1786 terms.push_back(stemmer("this"));
1787 terms.push_back(stemmer("word"));
1788 terms.push_back(stemmer("of"));
1789
1790 Xapian::Query query(Xapian::Query::OP_XOR, terms.begin(), terms.end());
1791 enquire.set_weighting_scheme(Xapian::BoolWeight());
1792 enquire.set_query(query);
1793
1794 Xapian::MSet mymset = enquire.get_mset(0, 10);
1795 // Docid this word of Match?
1796 // 1 * *
1797 // 2 * * * *
1798 // 3 * *
1799 // 4 * *
1800 // 5 * *
1801 // 6 * *
1802 mset_expect_order(mymset, 1, 2, 5, 6);
1803
1804 return true;
1805 }
1806
1807 /// Test that weighted XOR queries work (bug fixed in 1.2.1 and 1.0.21).
DEFINE_TESTCASE(xor2,backend)1808 DEFINE_TESTCASE(xor2, backend) {
1809 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1810 Xapian::Stem stemmer("english");
1811
1812 vector<string> terms;
1813 terms.push_back(stemmer("this"));
1814 terms.push_back(stemmer("word"));
1815 terms.push_back(stemmer("of"));
1816
1817 Xapian::Query query(Xapian::Query::OP_XOR, terms.begin(), terms.end());
1818 enquire.set_query(query);
1819
1820 Xapian::MSet mymset = enquire.get_mset(0, 10);
1821 // Docid LEN this word of Match?
1822 // 1 28 2 *
1823 // 2 81 5 8 1 *
1824 // 3 15 1 2
1825 // 4 31 1 1
1826 // 5 15 1 *
1827 // 6 15 1 *
1828 mset_expect_order(mymset, 2, 1, 5, 6);
1829
1830 return true;
1831 }
1832
1833 // test Xapian::Database::get_document()
DEFINE_TESTCASE(getdoc1,backend)1834 DEFINE_TESTCASE(getdoc1, backend) {
1835 Xapian::Database db(get_database("apitest_onedoc"));
1836 Xapian::Document doc(db.get_document(1));
1837 TEST_EXCEPTION(Xapian::InvalidArgumentError, db.get_document(0));
1838 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(999999999));
1839 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(123456789));
1840 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(3));
1841 TEST_EXCEPTION(Xapian::DocNotFoundError, db.get_document(2));
1842 // Check that Document works as a handle on modification
1843 // (this was broken for the first try at Xapian::Document prior to 0.7).
1844 Xapian::Document doc2 = doc;
1845 doc.set_data("modified!");
1846 TEST_EQUAL(doc.get_data(), "modified!");
1847 TEST_EQUAL(doc.get_data(), doc2.get_data());
1848 return true;
1849 }
1850
1851 // test whether operators with no elements work as a null query
DEFINE_TESTCASE(emptyop1,backend)1852 DEFINE_TESTCASE(emptyop1, backend) {
1853 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1854 vector<Xapian::Query> nullvec;
1855
1856 Xapian::Query query1(Xapian::Query::OP_XOR, nullvec.begin(), nullvec.end());
1857
1858 enquire.set_query(query1);
1859 Xapian::MSet mymset = enquire.get_mset(0, 10);
1860 TEST_MSET_SIZE(mymset, 0);
1861 TEST_EXCEPTION(Xapian::InvalidArgumentError, enquire.get_matching_terms_begin(1));
1862
1863 return true;
1864 }
1865
1866 // Regression test for check_at_least SEGV when there are no matches.
DEFINE_TESTCASE(checkatleast1,backend)1867 DEFINE_TESTCASE(checkatleast1, backend) {
1868 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1869 enquire.set_query(Xapian::Query("thom"));
1870 Xapian::MSet mymset = enquire.get_mset(0, 10, 11);
1871 TEST_EQUAL(0, mymset.size());
1872
1873 return true;
1874 }
1875
1876 // Regression test - if check_at_least was set we returned (check_at_least - 1)
1877 // results, rather than the requested msize. Fixed in 1.0.2.
DEFINE_TESTCASE(checkatleast2,backend)1878 DEFINE_TESTCASE(checkatleast2, backend) {
1879 Xapian::Enquire enquire(get_database("apitest_simpledata"));
1880 enquire.set_query(Xapian::Query("paragraph"));
1881
1882 Xapian::MSet mymset = enquire.get_mset(0, 3, 10);
1883 TEST_MSET_SIZE(mymset, 3);
1884 TEST_EQUAL(mymset.get_matches_lower_bound(), 5);
1885 TEST_EQUAL(mymset.get_uncollapsed_matches_lower_bound(), 5);
1886
1887 mymset = enquire.get_mset(0, 2, 4);
1888 TEST_MSET_SIZE(mymset, 2);
1889 TEST_REL(mymset.get_matches_lower_bound(),>=,4);
1890 TEST_REL(mymset.get_matches_lower_bound(),>=,4);
1891 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),>=,4);
1892 TEST_REL(mymset.get_uncollapsed_matches_lower_bound(),>=,4);
1893
1894 return true;
1895 }
1896
1897 // Feature tests - check_at_least with various sorting options.
DEFINE_TESTCASE(checkatleast3,backend)1898 DEFINE_TESTCASE(checkatleast3, backend) {
1899 Xapian::Enquire enquire(get_database("etext"));
1900 enquire.set_query(Xapian::Query("prussian")); // 60 matches.
1901
1902 for (int order = 0; order < 3; ++order) {
1903 switch (order) {
1904 case 0:
1905 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
1906 break;
1907 case 1:
1908 enquire.set_docid_order(Xapian::Enquire::DESCENDING);
1909 break;
1910 case 2:
1911 enquire.set_docid_order(Xapian::Enquire::DONT_CARE);
1912 break;
1913 }
1914
1915 for (int sort = 0; sort < 7; ++sort) {
1916 bool reverse = (sort & 1);
1917 switch (sort) {
1918 case 0:
1919 enquire.set_sort_by_relevance();
1920 break;
1921 case 1: case 2:
1922 enquire.set_sort_by_value(0, reverse);
1923 break;
1924 case 3: case 4:
1925 enquire.set_sort_by_value_then_relevance(0, reverse);
1926 break;
1927 case 5: case 6:
1928 enquire.set_sort_by_relevance_then_value(0, reverse);
1929 break;
1930 }
1931
1932 Xapian::MSet mset = enquire.get_mset(0, 100, 500);
1933 TEST_MSET_SIZE(mset, 60);
1934 TEST_EQUAL(mset.get_matches_lower_bound(), 60);
1935 TEST_EQUAL(mset.get_matches_estimated(), 60);
1936 TEST_EQUAL(mset.get_matches_upper_bound(), 60);
1937 TEST_EQUAL(mset.get_uncollapsed_matches_lower_bound(), 60);
1938 TEST_EQUAL(mset.get_uncollapsed_matches_estimated(), 60);
1939 TEST_EQUAL(mset.get_uncollapsed_matches_upper_bound(), 60);
1940
1941 mset = enquire.get_mset(0, 50, 100);
1942 TEST_MSET_SIZE(mset, 50);
1943 TEST_EQUAL(mset.get_matches_lower_bound(), 60);
1944 TEST_EQUAL(mset.get_matches_estimated(), 60);
1945 TEST_EQUAL(mset.get_matches_upper_bound(), 60);
1946 TEST_EQUAL(mset.get_uncollapsed_matches_lower_bound(), 60);
1947 TEST_EQUAL(mset.get_uncollapsed_matches_estimated(), 60);
1948 TEST_EQUAL(mset.get_uncollapsed_matches_upper_bound(), 60);
1949
1950 mset = enquire.get_mset(0, 10, 50);
1951 TEST_MSET_SIZE(mset, 10);
1952 TEST_REL(mset.get_matches_lower_bound(),>=,50);
1953 TEST_REL(mset.get_uncollapsed_matches_lower_bound(),>=,50);
1954 }
1955 }
1956
1957 return true;
1958 }
1959
1960 // tests all document postlists
DEFINE_TESTCASE(allpostlist1,backend)1961 DEFINE_TESTCASE(allpostlist1, backend) {
1962 Xapian::Database db(get_database("apitest_manydocs"));
1963 Xapian::PostingIterator i = db.postlist_begin("");
1964 unsigned int j = 1;
1965 while (i != db.postlist_end("")) {
1966 TEST_EQUAL(*i, j);
1967 i++;
1968 j++;
1969 }
1970 TEST_EQUAL(j, 513);
1971
1972 i = db.postlist_begin("");
1973 j = 1;
1974 while (i != db.postlist_end("")) {
1975 TEST_EQUAL(*i, j);
1976 i++;
1977 j++;
1978 if (j == 50) {
1979 j += 10;
1980 i.skip_to(j);
1981 }
1982 }
1983 TEST_EQUAL(j, 513);
1984
1985 return true;
1986 }
1987
test_emptyterm1_helper(Xapian::Database & db)1988 static void test_emptyterm1_helper(Xapian::Database & db)
1989 {
1990 // Don't bother with postlist_begin() because allpostlist tests cover that.
1991 TEST_EXCEPTION(Xapian::InvalidArgumentError, db.positionlist_begin(1, ""));
1992 TEST_EQUAL(db.get_doccount(), db.get_termfreq(""));
1993 TEST_EQUAL(db.get_doccount() != 0, db.term_exists(""));
1994 TEST_EQUAL(db.get_doccount(), db.get_collection_freq(""));
1995 }
1996
1997 // tests results of passing an empty term to various methods
DEFINE_TESTCASE(emptyterm1,backend)1998 DEFINE_TESTCASE(emptyterm1, backend) {
1999 Xapian::Database db(get_database("apitest_manydocs"));
2000 TEST_EQUAL(db.get_doccount(), 512);
2001 test_emptyterm1_helper(db);
2002
2003 db = get_database("apitest_onedoc");
2004 TEST_EQUAL(db.get_doccount(), 1);
2005 test_emptyterm1_helper(db);
2006
2007 db = get_database("");
2008 TEST_EQUAL(db.get_doccount(), 0);
2009 test_emptyterm1_helper(db);
2010
2011 return true;
2012 }
2013
2014 // Test for alldocs postlist with a sparse database.
DEFINE_TESTCASE(alldocspl1,writable)2015 DEFINE_TESTCASE(alldocspl1, writable) {
2016 Xapian::WritableDatabase db = get_writable_database();
2017 Xapian::Document doc;
2018 doc.set_data("5");
2019 doc.add_value(0, "5");
2020 db.replace_document(5, doc);
2021
2022 Xapian::PostingIterator i = db.postlist_begin("");
2023 TEST(i != db.postlist_end(""));
2024 TEST_EQUAL(*i, 5);
2025 TEST_EQUAL(i.get_doclength(), 0);
2026 TEST_EQUAL(i.get_wdf(), 1);
2027 ++i;
2028 TEST(i == db.postlist_end(""));
2029
2030 return true;
2031 }
2032
2033 // Test reading and writing a modified alldocspostlist.
DEFINE_TESTCASE(alldocspl2,writable)2034 DEFINE_TESTCASE(alldocspl2, writable) {
2035 Xapian::PostingIterator i, end;
2036 {
2037 Xapian::WritableDatabase db = get_writable_database();
2038 Xapian::Document doc;
2039 doc.set_data("5");
2040 doc.add_value(0, "5");
2041 db.replace_document(5, doc);
2042
2043 // Test iterating before committing the changes.
2044 i = db.postlist_begin("");
2045 end = db.postlist_end("");
2046 TEST(i != end);
2047 TEST_EQUAL(*i, 5);
2048 TEST_EQUAL(i.get_doclength(), 0);
2049 TEST_EQUAL(i.get_wdf(), 1);
2050 ++i;
2051 TEST(i == end);
2052
2053 db.commit();
2054
2055 // Test iterating after committing the changes.
2056 i = db.postlist_begin("");
2057 end = db.postlist_end("");
2058 TEST(i != end);
2059 TEST_EQUAL(*i, 5);
2060 TEST_EQUAL(i.get_doclength(), 0);
2061 TEST_EQUAL(i.get_wdf(), 1);
2062 ++i;
2063 TEST(i == end);
2064
2065 // Add another document.
2066 doc = Xapian::Document();
2067 doc.set_data("5");
2068 doc.add_value(0, "7");
2069 db.replace_document(7, doc);
2070
2071 // Test iterating through before committing the changes.
2072 i = db.postlist_begin("");
2073 end = db.postlist_end("");
2074 TEST(i != end);
2075 TEST_EQUAL(*i, 5);
2076 TEST_EQUAL(i.get_doclength(), 0);
2077 TEST_EQUAL(i.get_wdf(), 1);
2078 ++i;
2079 TEST(i != end);
2080 TEST_EQUAL(*i, 7);
2081 TEST_EQUAL(i.get_doclength(), 0);
2082 TEST_EQUAL(i.get_wdf(), 1);
2083 ++i;
2084 TEST(i == end);
2085
2086 // Delete the first document.
2087 db.delete_document(5);
2088
2089 // Test iterating through before committing the changes.
2090 i = db.postlist_begin("");
2091 end = db.postlist_end("");
2092 TEST(i != end);
2093 TEST_EQUAL(*i, 7);
2094 TEST_EQUAL(i.get_doclength(), 0);
2095 TEST_EQUAL(i.get_wdf(), 1);
2096 ++i;
2097 TEST(i == end);
2098
2099 // Test iterating through after committing the changes, and dropping the
2100 // reference to the main DB.
2101 db.commit();
2102 i = db.postlist_begin("");
2103 end = db.postlist_end("");
2104 }
2105
2106 TEST(i != end);
2107 TEST_EQUAL(*i, 7);
2108 TEST_EQUAL(i.get_doclength(), 0);
2109 TEST_EQUAL(i.get_wdf(), 1);
2110 ++i;
2111 TEST(i == end);
2112
2113 return true;
2114 }
2115
2116 // Feature test for Query::OP_SCALE_WEIGHT.
DEFINE_TESTCASE(scaleweight1,backend)2117 DEFINE_TESTCASE(scaleweight1, backend) {
2118 Xapian::Database db(get_database("apitest_phrase"));
2119 Xapian::Enquire enq(db);
2120 Xapian::QueryParser qp;
2121
2122 static const char * queries[] = {
2123 "pad",
2124 "milk fridge",
2125 "leave milk on fridge",
2126 "ordered milk operator",
2127 "ordered phrase operator",
2128 "leave \"milk on fridge\"",
2129 "notpresent",
2130 "leave \"milk notpresent\"",
2131 NULL
2132 };
2133 static const double multipliers[] = {
2134 -1000000, -2.5, -1, -0.5, 0, 0.5, 1, 2.5, 1000000,
2135 0, 0
2136 };
2137
2138 for (const char **qstr = queries; *qstr; ++qstr) {
2139 tout.str(string());
2140 Xapian::Query query1 = qp.parse_query(*qstr);
2141 tout << "query1: " << query1.get_description() << endl;
2142 for (const double *multp = multipliers; multp[0] != multp[1]; ++multp) {
2143 double mult = *multp;
2144 if (mult < 0) {
2145 TEST_EXCEPTION(Xapian::InvalidArgumentError,
2146 Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT,
2147 query1, mult));
2148 continue;
2149 }
2150 Xapian::Query query2(Xapian::Query::OP_SCALE_WEIGHT, query1, mult);
2151 tout << "query2: " << query2.get_description() << endl;
2152
2153 enq.set_query(query1);
2154 Xapian::MSet mset1 = enq.get_mset(0, 20);
2155 enq.set_query(query2);
2156 Xapian::MSet mset2 = enq.get_mset(0, 20);
2157
2158 TEST_EQUAL(mset1.size(), mset2.size());
2159
2160 Xapian::MSetIterator i1, i2;
2161 if (mult > 0) {
2162 for (i1 = mset1.begin(), i2 = mset2.begin();
2163 i1 != mset1.end() && i2 != mset2.end(); ++i1, ++i2) {
2164 TEST_EQUAL_DOUBLE(i1.get_weight() * mult, i2.get_weight());
2165 TEST_EQUAL(*i1, *i2);
2166 }
2167 } else {
2168 // Weights in mset2 are 0; so it should be sorted by docid.
2169 vector<Xapian::docid> ids1;
2170 vector<Xapian::docid> ids2;
2171 for (i1 = mset1.begin(), i2 = mset2.begin();
2172 i1 != mset1.end() && i2 != mset2.end(); ++i1, ++i2) {
2173 TEST_NOT_EQUAL_DOUBLE(i1.get_weight(), 0);
2174 TEST_EQUAL_DOUBLE(i2.get_weight(), 0);
2175 ids1.push_back(*i1);
2176 ids2.push_back(*i2);
2177 }
2178 sort(ids1.begin(), ids1.end());
2179 TEST_EQUAL(ids1, ids2);
2180 }
2181 }
2182 }
2183 return true;
2184 }
2185
2186 // Test Query::OP_SCALE_WEIGHT being used to multiply some of the weights of a
2187 // search by zero.
DEFINE_TESTCASE(scaleweight2,backend)2188 DEFINE_TESTCASE(scaleweight2, backend) {
2189 Xapian::Database db(get_database("apitest_phrase"));
2190 Xapian::Enquire enq(db);
2191 Xapian::MSetIterator i;
2192
2193 Xapian::Query query1("fridg");
2194 Xapian::Query query2(Xapian::Query::OP_SCALE_WEIGHT, query1, 2.5);
2195 Xapian::Query query3("milk");
2196 Xapian::Query query4(Xapian::Query::OP_SCALE_WEIGHT, query3, 0);
2197 Xapian::Query query5(Xapian::Query::OP_OR, query2, query4);
2198
2199 // query5 should first return the same results as query1, in the same
2200 // order, and then return the results of query3 which aren't also results
2201 // of query1, in ascending docid order. We test that this happens.
2202
2203 // First, build a vector of docids matching the first part of the query,
2204 // and append the non-duplicate docids matching the second part of the
2205 // query.
2206 vector<Xapian::docid> ids1;
2207 set<Xapian::docid> idsin1;
2208 vector<Xapian::docid> ids3;
2209
2210 enq.set_query(query1);
2211 Xapian::MSet mset1 = enq.get_mset(0, 20);
2212 enq.set_query(query3);
2213 Xapian::MSet mset3 = enq.get_mset(0, 20);
2214 TEST_NOT_EQUAL(mset1.size(), 0);
2215 for (i = mset1.begin(); i != mset1.end(); ++i) {
2216 ids1.push_back(*i);
2217 idsin1.insert(*i);
2218 }
2219 TEST_NOT_EQUAL(mset3.size(), 0);
2220 for (i = mset3.begin(); i != mset3.end(); ++i) {
2221 if (idsin1.find(*i) != idsin1.end())
2222 continue;
2223 ids3.push_back(*i);
2224 }
2225 sort(ids3.begin(), ids3.end());
2226 ids1.insert(ids1.end(), ids3.begin(), ids3.end());
2227
2228 // Now, run the combined query and build a vector of the matching docids.
2229 vector<Xapian::docid> ids5;
2230 enq.set_query(query5);
2231 Xapian::MSet mset5 = enq.get_mset(0, 20);
2232 for (i = mset5.begin(); i != mset5.end(); ++i) {
2233 ids5.push_back(*i);
2234 }
2235
2236 TEST_EQUAL(ids1, ids5);
2237 return true;
2238 }
2239
2240 // Regression test for bug fixed in 1.0.5 - this test would failed under
2241 // valgrind because it used an uninitialised value.
DEFINE_TESTCASE(bm25weight1,backend)2242 DEFINE_TESTCASE(bm25weight1, backend) {
2243 Xapian::Enquire enquire(get_database("apitest_simpledata"));
2244 enquire.set_weighting_scheme(Xapian::BM25Weight(1, 25, 1, 0.01, 0.5));
2245 enquire.set_query(Xapian::Query("word") );
2246
2247 Xapian::MSet mset = enquire.get_mset(0, 25);
2248
2249 return true;
2250 }
2251
2252 // Feature test for TradWeight.
DEFINE_TESTCASE(tradweight1,backend)2253 DEFINE_TESTCASE(tradweight1, backend) {
2254 Xapian::Enquire enquire(get_database("apitest_simpledata"));
2255 enquire.set_weighting_scheme(Xapian::TradWeight());
2256 enquire.set_query(Xapian::Query("word") );
2257
2258 Xapian::MSet mset = enquire.get_mset(0, 25);
2259 TEST_EQUAL(mset.size(), 2);
2260
2261 enquire.set_weighting_scheme(Xapian::TradWeight(0));
2262 enquire.set_query(Xapian::Query("this") );
2263
2264 mset = enquire.get_mset(0, 25);
2265 TEST_EQUAL(mset.size(), 6);
2266
2267 // Check that TradWeight(0) means wdf and doc length really don't affect
2268 // the weights as stated in the documentation.
2269 TEST_EQUAL(mset[0].get_weight(), mset[5].get_weight());
2270
2271 return true;
2272 }
2273
2274 // Test TradWeight when weighting documents using an RSet.
2275 // Simply changed the weighting scheme used by rset2 testcase.
DEFINE_TESTCASE(tradweight4,backend)2276 DEFINE_TESTCASE(tradweight4, backend) {
2277 Xapian::Database mydb(get_database("apitest_rset"));
2278 Xapian::Enquire enquire(mydb);
2279 Xapian::Query myquery = query(Xapian::Query::OP_OR, "cuddly", "people");
2280
2281 enquire.set_query(myquery);
2282 enquire.set_weighting_scheme(Xapian::TradWeight());
2283
2284 Xapian::MSet mymset1 = enquire.get_mset(0, 10);
2285
2286 Xapian::RSet myrset;
2287 myrset.add_document(2);
2288
2289 Xapian::MSet mymset2 = enquire.get_mset(0, 10, &myrset);
2290
2291 mset_expect_order(mymset1, 1, 2);
2292 // Document 2 should have higher weight than document 1 despite the wdf of
2293 // "people" being 1 because "people" indexes a document in the RSet whereas
2294 // "cuddly" (wdf=2) does not.
2295 mset_expect_order(mymset2, 2, 1);
2296
2297 return true;
2298 }
2299
2300 // Feature test for Database::get_uuid().
2301 DEFINE_TESTCASE(uuid1, backend && !multi) {
2302 SKIP_TEST_FOR_BACKEND("inmemory");
2303 Xapian::Database db = get_database("apitest_simpledata");
2304 string uuid1 = db.get_uuid();
2305 TEST_EQUAL(uuid1.size(), 36);
2306
2307 // A database with no sub-databases has an empty UUID.
2308 Xapian::Database db2;
2309 TEST(db2.get_uuid().empty());
2310
2311 db2.add_database(db);
2312 TEST_EQUAL(uuid1, db2.get_uuid());
2313
2314 // Multi-database has multiple UUIDs (we don't define the format exactly
2315 // so this assumes something about the implementation).
2316 db2.add_database(db);
2317 TEST_EQUAL(uuid1 + ":" + uuid1, db2.get_uuid());
2318
2319 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
2320 // This relies on InMemory databases not supporting uuids.
2321 // A multi-database containing a database with no uuid has no uuid.
2322 db2.add_database(Xapian::InMemory::open());
2323 TEST(db2.get_uuid().empty());
2324 #endif
2325
2326 return true;
2327 }
2328