1 /* api_anydb.cc: tests which work with any backend
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,2015,2016 Olly Betts
6 * Copyright 2006,2008 Lemur Consulting Ltd
7 * Copyright 2011 Action Without Borders
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.
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.
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
27 #include "api_anydb.h"
32 #define XAPIAN_DEPRECATED(X) X
34 #include "backendmanager_local.h"
35 #include "testsuite.h"
36 #include "testutils.h"
45 print_mset_weights(const Xapian::MSet
&mset
)
47 Xapian::MSetIterator i
= mset
.begin();
48 for ( ; i
!= mset
.end(); ++i
) {
49 tout
<< " " << i
.get_weight();
54 print_mset_percentages(const Xapian::MSet
&mset
)
56 Xapian::MSetIterator i
= mset
.begin();
57 for ( ; i
!= mset
.end(); ++i
) {
58 tout
<< " " << mset
.convert_to_percent(i
);
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())
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());
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())
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
);
109 query(const string
&t
)
111 return Xapian::Query(Xapian::Stem("english")(t
));
114 // #######################################################################
115 // # Tests start here
117 // tests that the backend doesn't return zero docids
118 DEFINE_TESTCASE(zerodocid1
, backend
) {
119 // open the database (in this case a simple text file
120 // we prepared earlier)
122 Xapian::Database
mydb(get_database("apitest_onedoc"));
124 Xapian::Enquire
enquire(mydb
);
126 // make a simple query, with one word in it - "word".
127 enquire
.set_query(Xapian::Query("word"));
129 // retrieve the top ten results (we only expect one)
130 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
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);
136 TEST_AND_EXPLAIN(*(mymset
.begin()) != 0,
137 "A query on a database returned a zero docid");
142 // tests that an empty query returns no matches
143 DEFINE_TESTCASE(emptyquery1
, backend
) {
144 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
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);
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);
170 // tests the document count for a simple query
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);
179 // tests for the right documents and weights returned with simple query
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"));
187 // retrieve the top results
188 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
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);
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);
199 TEST_EQUAL_DOUBLE(i
.get_weight(), 0.640987686595914);
204 // tests for the right document count for another simple query
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);
210 // Check that 6 documents were returned.
211 TEST_MSET_SIZE(mymset
, 6);
216 // multidb1 and multidb2 no longer exist.
218 // test that a multidb with 2 dbs query returns correct docids
219 DEFINE_TESTCASE(multidb3
, backend
&& !multi
) {
220 Xapian::Database
mydb2(get_database("apitest_simpledata"));
221 mydb2
.add_database(get_database("apitest_simpledata2"));
222 Xapian::Enquire
enquire(mydb2
);
225 Xapian::Query myquery
= query(Xapian::Query::OP_OR
, "inmemory", "word");
226 enquire
.set_weighting_scheme(Xapian::BoolWeight());
227 enquire
.set_query(myquery
);
229 // retrieve the top ten results
230 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
231 mset_expect_order(mymset
, 2, 3, 7);
236 // test that a multidb with 3 dbs query returns correct docids
237 DEFINE_TESTCASE(multidb4
, backend
&& !multi
) {
238 Xapian::Database
mydb2(get_database("apitest_simpledata"));
239 mydb2
.add_database(get_database("apitest_simpledata2"));
240 mydb2
.add_database(get_database("apitest_termorder"));
241 Xapian::Enquire
enquire(mydb2
);
244 Xapian::Query myquery
= query(Xapian::Query::OP_OR
, "inmemory", "word");
245 enquire
.set_weighting_scheme(Xapian::BoolWeight());
246 enquire
.set_query(myquery
);
248 // retrieve the top ten results
249 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
250 mset_expect_order(mymset
, 2, 3, 4, 10);
255 // tests MultiPostList::skip_to().
256 DEFINE_TESTCASE(multidb5
, backend
&& !multi
) {
257 Xapian::Database
mydb2(get_database("apitest_simpledata"));
258 mydb2
.add_database(get_database("apitest_simpledata2"));
259 Xapian::Enquire
enquire(mydb2
);
262 Xapian::Query myquery
= query(Xapian::Query::OP_AND
, "inmemory", "word");
263 enquire
.set_weighting_scheme(Xapian::BoolWeight());
264 enquire
.set_query(myquery
);
266 // retrieve the top ten results
267 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
268 mset_expect_order(mymset
, 2);
273 // tests that when specifying maxitems to get_mset, no more than
274 // that are returned.
275 DEFINE_TESTCASE(msetmaxitems1
, backend
) {
276 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
277 enquire
.set_query(query("this"));
278 Xapian::MSet mymset
= enquire
.get_mset(0, 1);
279 TEST_MSET_SIZE(mymset
, 1);
281 mymset
= enquire
.get_mset(0, 5);
282 TEST_MSET_SIZE(mymset
, 5);
287 // tests the returned weights are as expected (regression test for remote
288 // backend which was using the average weight rather than the actual document
289 // weight for computing weights - fixed in 1.0.0).
290 DEFINE_TESTCASE(expandweights1
, backend
) {
291 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
292 enquire
.set_query(Xapian::Query("this"));
294 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
297 Xapian::MSetIterator i
= mymset
.begin();
298 myrset
.add_document(*i
);
299 myrset
.add_document(*(++i
));
301 Xapian::ESet eset
= enquire
.get_eset(3, myrset
, enquire
.USE_EXACT_TERMFREQ
);
302 TEST_EQUAL(eset
.size(), 3);
303 TEST_REL(eset
.get_ebound(), >=, eset
.size());
304 TEST_EQUAL_DOUBLE(eset
[0].get_weight(), 6.08904001099445);
305 TEST_EQUAL_DOUBLE(eset
[1].get_weight(), 6.08904001099445);
306 TEST_EQUAL_DOUBLE(eset
[2].get_weight(), 4.73383620844021);
308 // Test non-default k too.
309 eset
= enquire
.get_eset(3, myrset
, enquire
.USE_EXACT_TERMFREQ
, 2.0);
310 TEST_EQUAL(eset
.size(), 3);
311 TEST_REL(eset
.get_ebound(), >=, eset
.size());
312 TEST_EQUAL_DOUBLE(eset
[0].get_weight(), 5.88109547674955);
313 TEST_EQUAL_DOUBLE(eset
[1].get_weight(), 5.88109547674955);
314 TEST_EQUAL_DOUBLE(eset
[2].get_weight(), 5.44473599216144);
319 // Just like test_expandweights1 but without USE_EXACT_TERMFREQ.
320 DEFINE_TESTCASE(expandweights2
, backend
) {
321 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
322 enquire
.set_query(Xapian::Query("this"));
324 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
327 Xapian::MSetIterator i
= mymset
.begin();
328 myrset
.add_document(*i
);
329 myrset
.add_document(*(++i
));
331 Xapian::ESet eset
= enquire
.get_eset(3, myrset
);
332 TEST_EQUAL(eset
.size(), 3);
333 TEST_REL(eset
.get_ebound(), >=, eset
.size());
334 if (!startswith(get_dbtype(), "multi")) {
335 // For a single database, the weights should be the same with or
336 // without USE_EXACT_TERMFREQ.
337 TEST_EQUAL_DOUBLE(eset
[0].get_weight(), 6.08904001099445);
338 TEST_EQUAL_DOUBLE(eset
[1].get_weight(), 6.08904001099445);
339 TEST_EQUAL_DOUBLE(eset
[2].get_weight(), 4.73383620844021);
341 // For multiple databases, we expect that using USE_EXACT_TERMFREQ
342 // will result in different weights in some cases.
343 TEST_NOT_EQUAL_DOUBLE(eset
[0].get_weight(), 6.08904001099445);
344 TEST_EQUAL_DOUBLE(eset
[1].get_weight(), 6.08904001099445);
345 TEST_NOT_EQUAL_DOUBLE(eset
[2].get_weight(), 4.73383620844021);
351 DEFINE_TESTCASE(expandweights3
, backend
) {
352 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
353 enquire
.set_query(Xapian::Query("this"));
355 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
358 Xapian::MSetIterator i
= mymset
.begin();
359 myrset
.add_document(*i
);
360 myrset
.add_document(*(++i
));
363 Xapian::ESet eset
= enquire
.get_eset(50, myrset
, 0, 0, 0.0);
364 TEST_EQUAL(eset
.size(), 50);
365 TEST_REL(eset
.get_ebound(), >=, eset
.size());
366 if (!startswith(get_dbtype(), "multi")) {
367 // For a single database, the weights should be the same with or
368 // without USE_EXACT_TERMFREQ.
369 TEST_EQUAL_DOUBLE(eset
[0].get_weight(), 6.08904001099445);
370 TEST_EQUAL_DOUBLE(eset
[1].get_weight(), 6.08904001099445);
371 TEST_EQUAL_DOUBLE(eset
[2].get_weight(), 4.73383620844021);
373 // For multiple databases, we expect that using USE_EXACT_TERMFREQ
374 // will result in different weights in some cases.
375 TEST_NOT_EQUAL_DOUBLE(eset
[0].get_weight(), 6.08904001099445);
376 TEST_EQUAL_DOUBLE(eset
[1].get_weight(), 6.08904001099445);
377 TEST_NOT_EQUAL_DOUBLE(eset
[2].get_weight(), 4.73383620844021);
379 TEST_REL(eset
.back().get_weight(),>=,0);
385 // tests that negative weights are returned
386 DEFINE_TESTCASE(expandweights4
, backend
) {
387 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
388 enquire
.set_query(Xapian::Query("paragraph"));
390 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
393 Xapian::MSetIterator i
= mymset
.begin();
394 myrset
.add_document(*i
);
395 myrset
.add_document(*(++i
));
397 Xapian::ESet eset
= enquire
.get_eset(37, myrset
, 0, 0, -100);
398 // Now include negative weights
399 TEST_EQUAL(eset
.size(), 37);
400 TEST_REL(eset
.get_ebound(), >=, eset
.size());
401 TEST_REL(eset
[36].get_weight(),<,0);
402 TEST_REL(eset
[36].get_weight(),>=,-100);
407 // test for Bo1EWeight
408 DEFINE_TESTCASE(expandweights5
, backend
) {
409 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
410 enquire
.set_query(Xapian::Query("this"));
412 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
415 Xapian::MSetIterator i
= mymset
.begin();
416 myrset
.add_document(*i
);
417 myrset
.add_document(*(++i
));
419 enquire
.set_expansion_scheme("bo1");
420 Xapian::ESet eset
= enquire
.get_eset(3, myrset
);
422 TEST_EQUAL(eset
.size(), 3);
423 TEST_REL(eset
.get_ebound(), >=, eset
.size());
424 TEST_EQUAL_DOUBLE(eset
[0].get_weight(), 7.21765284821702);
425 TEST_EQUAL_DOUBLE(eset
[1].get_weight(), 6.661623193760022);
426 TEST_EQUAL_DOUBLE(eset
[2].get_weight(), 5.58090119783738);
431 // test that "trad" can be set as an expansion scheme.
432 DEFINE_TESTCASE(expandweights6
, backend
) {
433 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
434 enquire
.set_query(Xapian::Query("this"));
436 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
439 Xapian::MSetIterator i
= mymset
.begin();
440 myrset
.add_document(*i
);
441 myrset
.add_document(*(++i
));
443 enquire
.set_expansion_scheme("trad");
444 Xapian::ESet eset
= enquire
.get_eset(3, myrset
, enquire
.USE_EXACT_TERMFREQ
);
446 TEST_EQUAL(eset
.size(), 3);
447 TEST_REL(eset
.get_ebound(), >=, eset
.size());
448 TEST_EQUAL_DOUBLE(eset
[0].get_weight(), 6.08904001099445);
449 TEST_EQUAL_DOUBLE(eset
[1].get_weight(), 6.08904001099445);
450 TEST_EQUAL_DOUBLE(eset
[2].get_weight(), 4.73383620844021);
455 // test that invalid scheme names are not accepted
456 DEFINE_TESTCASE(expandweights7
, backend
) {
457 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
459 TEST_EXCEPTION(Xapian::InvalidArgumentError
,
460 enquire
.set_expansion_scheme("no_such_scheme"));
465 // test that "expand_k" can be passed as a parameter to get_eset
466 DEFINE_TESTCASE(expandweights8
, backend
) {
467 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
468 enquire
.set_query(Xapian::Query("this"));
470 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
473 Xapian::MSetIterator i
= mymset
.begin();
474 myrset
.add_document(*i
);
475 myrset
.add_document(*(++i
));
477 // Set expand_k to 1.0 and min_wt to 0
478 Xapian::ESet eset
= enquire
.get_eset(50, myrset
, 0, 1.0, 0, 0);
479 if (!startswith(get_dbtype(), "multi")) {
480 // For a single database, the weights should be the same with or
481 // without USE_EXACT_TERMFREQ.
482 TEST_EQUAL_DOUBLE(eset
[0].get_weight(), 6.08904001099445);
483 TEST_EQUAL_DOUBLE(eset
[1].get_weight(), 6.08904001099445);
484 TEST_EQUAL_DOUBLE(eset
[2].get_weight(), 4.73383620844021);
486 // For multiple databases, we expect that using USE_EXACT_TERMFREQ
487 // will result in different weights in some cases.
488 TEST_NOT_EQUAL_DOUBLE(eset
[0].get_weight(), 6.08904001099445);
489 TEST_EQUAL_DOUBLE(eset
[1].get_weight(), 6.08904001099445);
490 TEST_NOT_EQUAL_DOUBLE(eset
[2].get_weight(), 4.73383620844021);
492 TEST_REL(eset
.back().get_weight(),>=,0);
497 // tests that when specifying maxitems to get_eset, no more than
498 // that are returned.
499 DEFINE_TESTCASE(expandmaxitems1
, backend
) {
500 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
501 enquire
.set_query(Xapian::Query("this"));
503 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
504 tout
<< "mymset.size() = " << mymset
.size() << endl
;
505 TEST(mymset
.size() >= 2);
508 Xapian::MSetIterator i
= mymset
.begin();
509 myrset
.add_document(*i
);
510 myrset
.add_document(*(++i
));
512 Xapian::ESet myeset
= enquire
.get_eset(1, myrset
);
513 TEST_EQUAL(myeset
.size(), 1);
514 TEST_REL(myeset
.get_ebound(), >=, myeset
.size());
519 // tests that a pure boolean query has all weights set to 0
520 DEFINE_TESTCASE(boolquery1
, backend
) {
521 Xapian::Query
myboolquery(query("this"));
523 // open the database (in this case a simple text file
524 // we prepared earlier)
525 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
526 enquire
.set_query(myboolquery
);
527 enquire
.set_weighting_scheme(Xapian::BoolWeight());
529 // retrieve the top results
530 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
532 TEST_NOT_EQUAL(mymset
.size(), 0);
533 TEST_EQUAL(mymset
.get_max_possible(), 0);
534 for (Xapian::MSetIterator i
= mymset
.begin(); i
!= mymset
.end(); ++i
) {
535 TEST_EQUAL(i
.get_weight(), 0);
540 // tests that get_mset() specifying "this" works as expected
541 DEFINE_TESTCASE(msetfirst1
, backend
) {
542 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
543 enquire
.set_query(query("this"));
544 Xapian::MSet mymset1
= enquire
.get_mset(0, 6);
545 Xapian::MSet mymset2
= enquire
.get_mset(3, 3);
546 TEST(mset_range_is_same(mymset1
, 3, mymset2
, 0, 3));
548 // Regression test - we weren't adjusting the index into items[] by
549 // firstitem in api/omenquire.cc.
550 TEST_EQUAL(mymset1
[5].get_document().get_data(),
551 mymset2
[2].get_document().get_data());
555 // tests the converting-to-percent functions
556 DEFINE_TESTCASE(topercent1
, backend
) {
557 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
558 enquire
.set_query(query("this"));
559 Xapian::MSet mymset
= enquire
.get_mset(0, 20);
562 Xapian::MSetIterator i
= mymset
.begin();
563 for ( ; i
!= mymset
.end(); ++i
) {
564 int pct
= mymset
.convert_to_percent(i
);
565 TEST_AND_EXPLAIN(pct
== i
.get_percent(),
566 "convert_to_%(msetitor) != convert_to_%(wt)");
567 TEST_AND_EXPLAIN(pct
== mymset
.convert_to_percent(i
.get_weight()),
568 "convert_to_%(msetitor) != convert_to_%(wt)");
569 TEST_AND_EXPLAIN(pct
>= 0 && pct
<= 100,
570 "percentage out of range: " << pct
);
571 TEST_AND_EXPLAIN(pct
<= last_pct
, "percentage increased down mset");
577 // tests the percentage values returned
578 DEFINE_TESTCASE(topercent2
, backend
) {
579 BackendManagerLocal local_manager
;
580 local_manager
.set_datadir(test_driver::get_srcdir() + "/testdata/");
581 Xapian::Enquire
localenq(local_manager
.get_database("apitest_simpledata"));
582 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
586 // First, test a search in which the top document scores 100%.
587 enquire
.set_query(query("this"));
588 localenq
.set_query(query("this"));
589 Xapian::MSet mymset
= enquire
.get_mset(0, 20);
590 Xapian::MSet localmset
= localenq
.get_mset(0, 20);
592 Xapian::MSetIterator i
= mymset
.begin();
593 TEST(i
!= mymset
.end());
594 pct
= mymset
.convert_to_percent(i
);
595 TEST_EQUAL(pct
, 100);
597 TEST_EQUAL(mymset
.get_matches_lower_bound(), localmset
.get_matches_lower_bound());
598 TEST_EQUAL(mymset
.get_matches_upper_bound(), localmset
.get_matches_upper_bound());
599 TEST_EQUAL(mymset
.get_matches_estimated(), localmset
.get_matches_estimated());
600 TEST_EQUAL_DOUBLE(mymset
.get_max_attained(), localmset
.get_max_attained());
601 TEST_EQUAL(mymset
.size(), localmset
.size());
602 TEST(mset_range_is_same(mymset
, 0, localmset
, 0, mymset
.size()));
604 // A search in which the top document doesn't have 100%
605 Xapian::Query q
= query(Xapian::Query::OP_OR
,
606 "this", "line", "paragraph", "rubbish");
607 enquire
.set_query(q
);
608 localenq
.set_query(q
);
609 mymset
= enquire
.get_mset(0, 20);
610 localmset
= localenq
.get_mset(0, 20);
613 TEST(i
!= mymset
.end());
614 pct
= mymset
.convert_to_percent(i
);
620 TEST(i
!= mymset
.end());
621 pct
= mymset
.convert_to_percent(i
);
625 TEST_EQUAL(mymset
.get_matches_lower_bound(), localmset
.get_matches_lower_bound());
626 TEST_EQUAL(mymset
.get_matches_upper_bound(), localmset
.get_matches_upper_bound());
627 TEST_EQUAL(mymset
.get_matches_estimated(), localmset
.get_matches_estimated());
628 TEST_EQUAL_DOUBLE(mymset
.get_max_attained(), localmset
.get_max_attained());
629 TEST_EQUAL(mymset
.size(), localmset
.size());
630 TEST(mset_range_is_same(mymset
, 0, localmset
, 0, mymset
.size()));
635 class myExpandFunctor
: public Xapian::ExpandDecider
{
637 bool operator()(const string
& tname
) const {
638 unsigned long sum
= 0;
639 for (unsigned ch
: tname
) {
643 // tout << tname << "==> " << sum << "\n";
645 return (sum
% 2) == 0;
649 // tests the expand decision functor
650 DEFINE_TESTCASE(expandfunctor1
, backend
) {
651 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
652 enquire
.set_query(Xapian::Query("this"));
654 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
655 TEST(mymset
.size() >= 2);
658 Xapian::MSetIterator i
= mymset
.begin();
659 myrset
.add_document(*i
);
660 myrset
.add_document(*(++i
));
662 myExpandFunctor myfunctor
;
664 Xapian::ESet myeset_orig
= enquire
.get_eset(1000, myrset
);
665 unsigned int neweset_size
= 0;
666 Xapian::ESetIterator j
= myeset_orig
.begin();
667 for ( ; j
!= myeset_orig
.end(); ++j
) {
668 if (myfunctor(*j
)) neweset_size
++;
670 Xapian::ESet myeset
= enquire
.get_eset(neweset_size
, myrset
, &myfunctor
);
673 // Compare myeset with the hand-filtered version of myeset_orig.
675 tout
<< "orig_eset: ";
676 copy(myeset_orig
.begin(), myeset_orig
.end(),
677 ostream_iterator
<Xapian::ESetItem
>(tout
, " "));
680 tout
<< "new_eset: ";
681 copy(myeset
.begin(), myeset
.end(),
682 ostream_iterator
<Xapian::ESetItem
>(tout
, " "));
686 Xapian::ESetIterator orig
= myeset_orig
.begin();
687 Xapian::ESetIterator filt
= myeset
.begin();
688 for (; orig
!= myeset_orig
.end() && filt
!= myeset
.end(); ++orig
, ++filt
) {
689 // skip over items that shouldn't be in myeset
690 while (orig
!= myeset_orig
.end() && !myfunctor(*orig
)) {
694 TEST_AND_EXPLAIN(*orig
== *filt
&&
695 orig
.get_weight() == filt
.get_weight(),
696 "Mismatch in items " << *orig
<< " vs. " << *filt
697 << " after filtering");
700 while (orig
!= myeset_orig
.end() && !myfunctor(*orig
)) {
704 TEST_EQUAL(orig
, myeset_orig
.end());
705 TEST_AND_EXPLAIN(filt
== myeset
.end(),
706 "Extra items in the filtered eset.");
710 DEFINE_TESTCASE(expanddeciderfilterprefix2
, backend
) {
711 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
712 enquire
.set_query(Xapian::Query("this"));
714 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
715 TEST(mymset
.size() >= 2);
718 Xapian::MSetIterator i
= mymset
.begin();
719 myrset
.add_document(*i
);
720 myrset
.add_document(*(++i
));
722 Xapian::ESet myeset_orig
= enquire
.get_eset(1000, myrset
);
723 unsigned int neweset_size
= 0;
725 // Choose the first char in the first term as prefix.
726 Xapian::ESetIterator j
= myeset_orig
.begin();
727 TEST(myeset_orig
.size() >= 1);
728 string
prefix(*j
, 0, 1);
729 Xapian::ExpandDeciderFilterPrefix
myfunctor(prefix
);
731 for ( ; j
!= myeset_orig
.end(); ++j
) {
732 if (myfunctor(*j
)) neweset_size
++;
734 Xapian::ESet myeset
= enquire
.get_eset(neweset_size
, myrset
, &myfunctor
);
736 Xapian::ESetIterator orig
= myeset_orig
.begin();
737 Xapian::ESetIterator filt
= myeset
.begin();
738 for (; orig
!= myeset_orig
.end() && filt
!= myeset
.end(); ++orig
, ++filt
) {
739 // skip over items that shouldn't be in myeset
740 while (orig
!= myeset_orig
.end() && !myfunctor(*orig
)) {
744 TEST_AND_EXPLAIN(*orig
== *filt
&&
745 orig
.get_weight() == filt
.get_weight(),
746 "Mismatch in items " << *orig
<< " vs. " << *filt
747 << " after filtering");
750 while (orig
!= myeset_orig
.end() && !myfunctor(*orig
)) {
754 TEST_EQUAL(orig
, myeset_orig
.end());
755 TEST_AND_EXPLAIN(filt
== myeset
.end(),
756 "Extra items in the filtered eset.");
761 // tests the percent cutoff option
762 DEFINE_TESTCASE(pctcutoff1
, backend
) {
763 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
764 enquire
.set_query(query(Xapian::Query::OP_OR
,
765 "this", "line", "paragraph", "rubbish"));
766 Xapian::MSet mymset1
= enquire
.get_mset(0, 100);
769 tout
<< "Original mset pcts:";
770 print_mset_percentages(mymset1
);
774 unsigned int num_items
= 0;
777 Xapian::MSetIterator i
= mymset1
.begin();
779 for ( ; i
!= mymset1
.end(); ++i
, ++c
) {
780 int new_pct
= mymset1
.convert_to_percent(i
);
781 if (new_pct
!= my_pct
) {
783 if (changes
> 3) break;
789 TEST_AND_EXPLAIN(changes
> 3, "MSet not varied enough to test");
791 tout
<< "Cutoff percent: " << my_pct
<< "\n";
794 enquire
.set_cutoff(my_pct
);
795 Xapian::MSet mymset2
= enquire
.get_mset(0, 100);
798 tout
<< "Percentages after cutoff:";
799 print_mset_percentages(mymset2
);
803 TEST_AND_EXPLAIN(mymset2
.size() >= num_items
,
804 "Match with % cutoff lost too many items");
806 TEST_AND_EXPLAIN(mymset2
.size() == num_items
||
807 (mymset2
.convert_to_percent(mymset2
[num_items
]) == my_pct
&&
808 mymset2
.convert_to_percent(mymset2
.back()) == my_pct
),
809 "Match with % cutoff returned too many items");
814 // Tests the percent cutoff option combined with collapsing
815 DEFINE_TESTCASE(pctcutoff2
, backend
) {
816 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
817 enquire
.set_query(Xapian::Query("this"));
818 enquire
.set_query(Xapian::Query(Xapian::Query::OP_AND_NOT
, Xapian::Query("this"), Xapian::Query("banana")));
819 Xapian::MSet mset
= enquire
.get_mset(0, 100);
822 tout
<< "Original mset pcts:";
823 print_mset_percentages(mset
);
827 TEST(mset
.size() >= 2);
828 TEST(mset
[0].get_percent() - mset
[1].get_percent() >= 2);
830 int cutoff
= mset
[0].get_percent() + mset
[1].get_percent();
833 enquire
.set_cutoff(cutoff
);
834 enquire
.set_collapse_key(1234); // Value which is always empty.
836 Xapian::MSet mset2
= enquire
.get_mset(0, 1);
837 TEST_EQUAL(mset2
.size(), 1);
838 TEST_EQUAL(mset2
.get_matches_lower_bound(), 1);
839 TEST_REL(mset2
.get_uncollapsed_matches_lower_bound(),>=,1);
840 TEST_REL(mset2
.get_uncollapsed_matches_lower_bound(),<=,mset
.size());
841 TEST_REL(mset2
.get_uncollapsed_matches_upper_bound(),>=,mset
.size());
842 TEST_REL(mset2
.get_uncollapsed_matches_lower_bound(),<=,mset2
.get_uncollapsed_matches_estimated());
843 TEST_REL(mset2
.get_uncollapsed_matches_upper_bound(),>=,mset2
.get_uncollapsed_matches_estimated());
848 // Test that the percent cutoff option returns all the answers it should.
849 DEFINE_TESTCASE(pctcutoff3
, backend
) {
850 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
851 enquire
.set_query(Xapian::Query("this"));
852 Xapian::MSet mset1
= enquire
.get_mset(0, 10);
855 tout
<< "Original mset pcts:";
856 print_mset_percentages(mset1
);
861 for (Xapian::MSetIterator i
= mset1
.begin(); i
!= mset1
.end(); ++i
) {
862 int new_percent
= mset1
.convert_to_percent(i
);
863 if (new_percent
!= percent
) {
864 enquire
.set_cutoff(percent
);
865 Xapian::MSet mset2
= enquire
.get_mset(0, 10);
866 TEST_EQUAL(mset2
.size(), i
.get_rank());
867 percent
= new_percent
;
874 // tests the cutoff option
875 DEFINE_TESTCASE(cutoff1
, backend
) {
876 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
877 enquire
.set_query(query(Xapian::Query::OP_OR
,
878 "this", "line", "paragraph", "rubbish"));
879 Xapian::MSet mymset1
= enquire
.get_mset(0, 100);
882 tout
<< "Original mset weights:";
883 print_mset_weights(mymset1
);
887 unsigned int num_items
= 0;
890 Xapian::MSetIterator i
= mymset1
.begin();
892 for ( ; i
!= mymset1
.end(); ++i
, ++c
) {
893 double new_wt
= i
.get_weight();
894 if (new_wt
!= my_wt
) {
896 if (changes
> 3) break;
902 TEST_AND_EXPLAIN(changes
> 3, "MSet not varied enough to test");
904 tout
<< "Cutoff weight: " << my_wt
<< "\n";
907 enquire
.set_cutoff(0, my_wt
);
908 Xapian::MSet mymset2
= enquire
.get_mset(0, 100);
911 tout
<< "Weights after cutoff:";
912 print_mset_weights(mymset2
);
916 TEST_AND_EXPLAIN(mymset2
.size() >= num_items
,
917 "Match with cutoff lost too many items");
919 TEST_AND_EXPLAIN(mymset2
.size() == num_items
||
920 (mymset2
[num_items
].get_weight() == my_wt
&&
921 mymset2
.back().get_weight() == my_wt
),
922 "Match with cutoff returned too many items");
927 // tests the allow query terms expand option
928 DEFINE_TESTCASE(allowqterms1
, backend
) {
929 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
930 string term
= "paragraph";
931 enquire
.set_query(Xapian::Query(term
));
933 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
934 TEST(mymset
.size() >= 2);
937 Xapian::MSetIterator i
= mymset
.begin();
938 myrset
.add_document(*i
);
939 myrset
.add_document(*(++i
));
941 Xapian::ESet myeset
= enquire
.get_eset(1000, myrset
);
942 Xapian::ESetIterator j
= myeset
.begin();
943 for ( ; j
!= myeset
.end(); ++j
) {
944 TEST_NOT_EQUAL(*j
, term
);
947 Xapian::ESet myeset2
= enquire
.get_eset(1000, myrset
, Xapian::Enquire::INCLUDE_QUERY_TERMS
);
949 for ( ; j
!= myeset2
.end(); ++j
) {
950 if (*j
== term
) break;
952 TEST(j
!= myeset2
.end());
956 // tests that the MSet max_attained works
957 DEFINE_TESTCASE(maxattain1
, backend
) {
958 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
959 enquire
.set_query(query("this"));
960 Xapian::MSet mymset
= enquire
.get_mset(0, 100);
963 Xapian::MSetIterator i
= mymset
.begin();
964 for ( ; i
!= mymset
.end(); ++i
) {
965 if (i
.get_weight() > mymax
) mymax
= i
.get_weight();
967 TEST_EQUAL(mymax
, mymset
.get_max_attained());
972 // tests a reversed boolean query
973 DEFINE_TESTCASE(reversebool1
, backend
) {
974 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
975 enquire
.set_query(Xapian::Query("this"));
976 enquire
.set_weighting_scheme(Xapian::BoolWeight());
978 Xapian::MSet mymset1
= enquire
.get_mset(0, 100);
979 TEST_AND_EXPLAIN(mymset1
.size() > 1,
980 "Mset was too small to test properly");
982 enquire
.set_docid_order(Xapian::Enquire::ASCENDING
);
983 Xapian::MSet mymset2
= enquire
.get_mset(0, 100);
984 enquire
.set_docid_order(Xapian::Enquire::DESCENDING
);
985 Xapian::MSet mymset3
= enquire
.get_mset(0, 100);
987 // mymset1 and mymset2 should be identical
988 TEST_EQUAL(mymset1
.size(), mymset2
.size());
991 Xapian::MSetIterator i
= mymset1
.begin();
992 Xapian::MSetIterator j
= mymset2
.begin();
993 for ( ; i
!= mymset1
.end(); ++i
, j
++) {
994 TEST(j
!= mymset2
.end());
995 // if this fails, then setting match_sort_forward=true was not
996 // the same as the default.
999 TEST(j
== mymset2
.end());
1002 // mymset1 and mymset3 should be same but reversed
1003 TEST_EQUAL(mymset1
.size(), mymset3
.size());
1006 Xapian::MSetIterator i
= mymset1
.begin();
1007 Xapian::MSetIterator j
= mymset3
.end();
1008 for ( ; i
!= mymset1
.end(); ++i
) {
1009 // if this fails, then setting match_sort_forward=false didn't
1010 // reverse the results.
1011 TEST_EQUAL(*i
, *--j
);
1018 // tests a reversed boolean query, where the full mset isn't returned
1019 DEFINE_TESTCASE(reversebool2
, backend
) {
1020 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1021 enquire
.set_query(Xapian::Query("this"));
1022 enquire
.set_weighting_scheme(Xapian::BoolWeight());
1024 Xapian::MSet mymset1
= enquire
.get_mset(0, 100);
1026 TEST_AND_EXPLAIN(mymset1
.size() > 1,
1027 "Mset was too small to test properly");
1029 enquire
.set_docid_order(Xapian::Enquire::ASCENDING
);
1030 Xapian::doccount msize
= mymset1
.size() / 2;
1031 Xapian::MSet mymset2
= enquire
.get_mset(0, msize
);
1032 enquire
.set_docid_order(Xapian::Enquire::DESCENDING
);
1033 Xapian::MSet mymset3
= enquire
.get_mset(0, msize
);
1035 // mymset2 should be first msize items of mymset1
1036 TEST_EQUAL(msize
, mymset2
.size());
1038 Xapian::MSetIterator i
= mymset1
.begin();
1039 Xapian::MSetIterator j
= mymset2
.begin();
1040 for ( ; j
!= mymset2
.end(); ++i
, j
++) {
1041 TEST(i
!= mymset1
.end());
1042 // if this fails, then setting match_sort_forward=true was not
1043 // the same as the default.
1046 // mymset1 should be larger.
1047 TEST(i
!= mymset1
.end());
1050 // mymset3 should be last msize items of mymset1, in reverse order
1051 TEST_EQUAL(msize
, mymset3
.size());
1053 Xapian::MSetIterator i
= mymset1
.end();
1054 Xapian::MSetIterator j
;
1055 for (j
= mymset3
.begin(); j
!= mymset3
.end(); j
++) {
1056 // if this fails, then setting match_sort_forward=false didn't
1057 // reverse the results.
1058 TEST_EQUAL(*--i
, *j
);
1065 // tests that get_matching_terms() returns the terms in the right order
1066 DEFINE_TESTCASE(getmterms1
, backend
) {
1067 list
<string
> answers_list
;
1068 answers_list
.push_back("one");
1069 answers_list
.push_back("two");
1070 answers_list
.push_back("three");
1071 answers_list
.push_back("four");
1073 Xapian::Database
mydb(get_database("apitest_termorder"));
1074 Xapian::Enquire
enquire(mydb
);
1076 Xapian::Query
myquery(Xapian::Query::OP_OR
,
1077 Xapian::Query(Xapian::Query::OP_AND
,
1078 Xapian::Query("one", 1, 1),
1079 Xapian::Query("three", 1, 3)),
1080 Xapian::Query(Xapian::Query::OP_OR
,
1081 Xapian::Query("four", 1, 4),
1082 Xapian::Query("two", 1, 2)));
1084 enquire
.set_query(myquery
);
1086 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
1088 TEST_MSET_SIZE(mymset
, 1);
1089 list
<string
> list(enquire
.get_matching_terms_begin(mymset
.begin()),
1090 enquire
.get_matching_terms_end(mymset
.begin()));
1091 TEST(list
== answers_list
);
1096 // tests that get_matching_terms() returns the terms only once
1097 DEFINE_TESTCASE(getmterms2
, backend
) {
1098 list
<string
> answers_list
;
1099 answers_list
.push_back("one");
1100 answers_list
.push_back("two");
1101 answers_list
.push_back("three");
1103 Xapian::Database
mydb(get_database("apitest_termorder"));
1104 Xapian::Enquire
enquire(mydb
);
1106 Xapian::Query
myquery(Xapian::Query::OP_OR
,
1107 Xapian::Query(Xapian::Query::OP_AND
,
1108 Xapian::Query("one", 1, 1),
1109 Xapian::Query("three", 1, 3)),
1110 Xapian::Query(Xapian::Query::OP_OR
,
1111 Xapian::Query("one", 1, 4),
1112 Xapian::Query("two", 1, 2)));
1114 enquire
.set_query(myquery
);
1116 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
1118 TEST_MSET_SIZE(mymset
, 1);
1119 list
<string
> list(enquire
.get_matching_terms_begin(mymset
.begin()),
1120 enquire
.get_matching_terms_end(mymset
.begin()));
1121 TEST(list
== answers_list
);
1126 // test that running a query twice returns the same results
1127 DEFINE_TESTCASE(repeatquery1
, backend
) {
1128 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1129 enquire
.set_query(Xapian::Query("this"));
1131 enquire
.set_query(query(Xapian::Query::OP_OR
, "this", "word"));
1133 Xapian::MSet mymset1
= enquire
.get_mset(0, 10);
1134 Xapian::MSet mymset2
= enquire
.get_mset(0, 10);
1135 TEST_EQUAL(mymset1
, mymset2
);
1140 // test that prefetching documents works (at least, gives same results)
1141 DEFINE_TESTCASE(fetchdocs1
, backend
) {
1142 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1143 enquire
.set_query(Xapian::Query("this"));
1145 enquire
.set_query(query(Xapian::Query::OP_OR
, "this", "word"));
1147 Xapian::MSet mymset1
= enquire
.get_mset(0, 10);
1148 Xapian::MSet mymset2
= enquire
.get_mset(0, 10);
1149 TEST_EQUAL(mymset1
, mymset2
);
1150 mymset2
.fetch(mymset2
[0], mymset2
[mymset2
.size() - 1]);
1151 mymset2
.fetch(mymset2
.begin(), mymset2
.end());
1152 mymset2
.fetch(mymset2
.begin());
1155 Xapian::MSetIterator it1
= mymset1
.begin();
1156 Xapian::MSetIterator it2
= mymset2
.begin();
1158 while (it1
!= mymset1
.end() && it2
!= mymset2
.end()) {
1159 TEST_EQUAL(it1
.get_document().get_data(),
1160 it2
.get_document().get_data());
1161 TEST_NOT_EQUAL(it1
.get_document().get_data(), "");
1162 TEST_NOT_EQUAL(it2
.get_document().get_data(), "");
1166 TEST_EQUAL(it1
, mymset1
.end());
1167 TEST_EQUAL(it1
, mymset2
.end());
1172 // test that searching for a term not in the database fails nicely
1173 DEFINE_TESTCASE(absentterm1
, backend
) {
1174 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1175 enquire
.set_weighting_scheme(Xapian::BoolWeight());
1176 enquire
.set_query(Xapian::Query("frink"));
1178 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
1179 mset_expect_order(mymset
);
1184 // as absentterm1, but setting query from a vector of terms
1185 DEFINE_TESTCASE(absentterm2
, backend
) {
1186 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1187 vector
<string
> terms
;
1188 terms
.push_back("frink");
1190 Xapian::Query
query(Xapian::Query::OP_OR
, terms
.begin(), terms
.end());
1191 enquire
.set_query(query
);
1193 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
1194 mset_expect_order(mymset
);
1199 // test that rsets do sensible things
1200 DEFINE_TESTCASE(rset1
, backend
) {
1201 Xapian::Database
mydb(get_database("apitest_rset"));
1202 Xapian::Enquire
enquire(mydb
);
1203 Xapian::Query myquery
= query(Xapian::Query::OP_OR
, "giraffe", "tiger");
1204 enquire
.set_query(myquery
);
1206 Xapian::MSet mymset1
= enquire
.get_mset(0, 10);
1208 Xapian::RSet myrset
;
1209 myrset
.add_document(1);
1211 Xapian::MSet mymset2
= enquire
.get_mset(0, 10, &myrset
);
1213 // We should have the same documents turn up, but 1 and 3 should
1214 // have higher weights with the RSet.
1215 TEST_MSET_SIZE(mymset1
, 3);
1216 TEST_MSET_SIZE(mymset2
, 3);
1221 // test that rsets do more sensible things
1222 DEFINE_TESTCASE(rset2
, backend
) {
1223 Xapian::Database
mydb(get_database("apitest_rset"));
1224 Xapian::Enquire
enquire(mydb
);
1225 Xapian::Query myquery
= query(Xapian::Query::OP_OR
, "cuddly", "people");
1226 enquire
.set_query(myquery
);
1228 Xapian::MSet mymset1
= enquire
.get_mset(0, 10);
1230 Xapian::RSet myrset
;
1231 myrset
.add_document(2);
1233 Xapian::MSet mymset2
= enquire
.get_mset(0, 10, &myrset
);
1235 mset_expect_order(mymset1
, 1, 2);
1236 mset_expect_order(mymset2
, 2, 1);
1241 // test that rsets behave correctly with multiDBs
1242 DEFINE_TESTCASE(rsetmultidb1
, backend
&& !multi
) {
1243 Xapian::Database
mydb1(get_database("apitest_rset", "apitest_simpledata2"));
1244 Xapian::Database
mydb2(get_database("apitest_rset"));
1245 mydb2
.add_database(get_database("apitest_simpledata2"));
1247 Xapian::Enquire
enquire1(mydb1
);
1248 Xapian::Enquire
enquire2(mydb2
);
1250 Xapian::Query myquery
= query(Xapian::Query::OP_OR
, "cuddly", "multiple");
1252 enquire1
.set_query(myquery
);
1253 enquire2
.set_query(myquery
);
1255 Xapian::RSet myrset1
;
1256 Xapian::RSet myrset2
;
1257 myrset1
.add_document(4);
1258 myrset2
.add_document(2);
1260 Xapian::MSet mymset1a
= enquire1
.get_mset(0, 10);
1261 Xapian::MSet mymset1b
= enquire1
.get_mset(0, 10, &myrset1
);
1262 Xapian::MSet mymset2a
= enquire2
.get_mset(0, 10);
1263 Xapian::MSet mymset2b
= enquire2
.get_mset(0, 10, &myrset2
);
1265 mset_expect_order(mymset1a
, 1, 4);
1266 mset_expect_order(mymset1b
, 4, 1);
1267 mset_expect_order(mymset2a
, 1, 2);
1268 mset_expect_order(mymset2b
, 2, 1);
1270 TEST(mset_range_is_same_weights(mymset1a
, 0, mymset2a
, 0, 2));
1271 TEST(mset_range_is_same_weights(mymset1b
, 0, mymset2b
, 0, 2));
1272 TEST_NOT_EQUAL(mymset1a
, mymset1b
);
1273 TEST_NOT_EQUAL(mymset2a
, mymset2b
);
1278 // regression tests - used to cause assertion in stats.h to fail
1279 // Doesn't actually fail for multi but it doesn't make sense to run there.
1280 DEFINE_TESTCASE(rsetmultidb3
, backend
&& !multi
) {
1281 Xapian::Enquire
enquire(get_database("apitest_simpledata2"));
1282 enquire
.set_query(query(Xapian::Query::OP_OR
, "cuddly", "people"));
1283 Xapian::MSet mset
= enquire
.get_mset(0, 10); // used to fail assertion
1287 /// Simple test of the elite set operator.
1288 DEFINE_TESTCASE(eliteset1
, backend
) {
1289 Xapian::Database
mydb(get_database("apitest_simpledata"));
1290 Xapian::Enquire
enquire(mydb
);
1292 Xapian::Query myquery1
= query(Xapian::Query::OP_OR
, "word");
1294 Xapian::Query myquery2
= query(Xapian::Query::OP_ELITE_SET
, 1,
1297 enquire
.set_query(myquery1
, 2); // So the query lengths are the same.
1298 Xapian::MSet mymset1
= enquire
.get_mset(0, 10);
1300 enquire
.set_query(myquery2
);
1301 Xapian::MSet mymset2
= enquire
.get_mset(0, 10);
1303 TEST_EQUAL(mymset1
, mymset2
);
1307 /// Test that the elite set operator works if the set contains
1308 /// sub-expressions (regression test)
1309 DEFINE_TESTCASE(eliteset2
, backend
) {
1310 Xapian::Database
mydb(get_database("apitest_simpledata"));
1311 Xapian::Enquire
enquire(mydb
);
1313 Xapian::Query myquery1
= query(Xapian::Query::OP_AND
, "word", "search");
1315 vector
<Xapian::Query
> qs
;
1316 qs
.push_back(query("this"));
1317 qs
.push_back(query(Xapian::Query::OP_AND
, "word", "search"));
1318 Xapian::Query
myquery2(Xapian::Query::OP_ELITE_SET
,
1319 qs
.begin(), qs
.end(), 1);
1321 enquire
.set_query(myquery1
);
1322 Xapian::MSet mymset1
= enquire
.get_mset(0, 10);
1324 enquire
.set_query(myquery2
);
1325 Xapian::MSet mymset2
= enquire
.get_mset(0, 10);
1327 TEST_EQUAL(mymset1
, mymset2
);
1328 // query lengths differ so mset weights not the same (with some weighting
1330 //test_mset_order_equal(mymset1, mymset2);
1335 /// Test that elite set doesn't affect query results if we have fewer
1336 /// terms than the threshold
1337 DEFINE_TESTCASE(eliteset3
, backend
) {
1338 Xapian::Database
mydb1(get_database("apitest_simpledata"));
1339 Xapian::Enquire
enquire1(mydb1
);
1341 Xapian::Database
mydb2(get_database("apitest_simpledata"));
1342 Xapian::Enquire
enquire2(mydb2
);
1345 Xapian::Stem
stemmer("english");
1347 string term1
= stemmer("word");
1348 string term2
= stemmer("rubbish");
1349 string term3
= stemmer("banana");
1351 vector
<string
> terms
;
1352 terms
.push_back(term1
);
1353 terms
.push_back(term2
);
1354 terms
.push_back(term3
);
1356 Xapian::Query
myquery1(Xapian::Query::OP_OR
, terms
.begin(), terms
.end());
1357 enquire1
.set_query(myquery1
);
1359 Xapian::Query
myquery2(Xapian::Query::OP_ELITE_SET
, terms
.begin(), terms
.end(), 3);
1360 enquire2
.set_query(myquery2
);
1362 // retrieve the results
1363 Xapian::MSet mymset1
= enquire1
.get_mset(0, 10);
1364 Xapian::MSet mymset2
= enquire2
.get_mset(0, 10);
1366 TEST_EQUAL(mymset1
.get_termfreq(term1
),
1367 mymset2
.get_termfreq(term1
));
1368 TEST_EQUAL(mymset1
.get_termweight(term1
),
1369 mymset2
.get_termweight(term1
));
1370 TEST_EQUAL(mymset1
.get_termfreq(term2
),
1371 mymset2
.get_termfreq(term2
));
1372 TEST_EQUAL(mymset1
.get_termweight(term2
),
1373 mymset2
.get_termweight(term2
));
1374 TEST_EQUAL(mymset1
.get_termfreq(term3
),
1375 mymset2
.get_termfreq(term3
));
1376 TEST_EQUAL(mymset1
.get_termweight(term3
),
1377 mymset2
.get_termweight(term3
));
1378 // TEST_EQUAL(mymset1, mymset2);
1383 /// Test that elite set doesn't pick terms with 0 frequency
1384 DEFINE_TESTCASE(eliteset4
, backend
) {
1385 Xapian::Database
mydb1(get_database("apitest_simpledata"));
1386 Xapian::Enquire
enquire1(mydb1
);
1388 Xapian::Database
mydb2(get_database("apitest_simpledata"));
1389 Xapian::Enquire
enquire2(mydb2
);
1391 Xapian::Query myquery1
= query("rubbish");
1392 Xapian::Query myquery2
= query(Xapian::Query::OP_ELITE_SET
, 1,
1393 "word", "rubbish", "fibble");
1394 enquire1
.set_query(myquery1
);
1395 enquire2
.set_query(myquery2
);
1397 // retrieve the results
1398 Xapian::MSet mymset1
= enquire1
.get_mset(0, 10);
1399 Xapian::MSet mymset2
= enquire2
.get_mset(0, 10);
1401 TEST_NOT_EQUAL(mymset2
.size(), 0);
1402 TEST_EQUAL(mymset1
, mymset2
);
1403 // TEST_EQUAL(mymset1, mymset2);
1408 /// Regression test for problem with excess precision.
1409 DEFINE_TESTCASE(eliteset5
, backend
) {
1410 Xapian::Database
mydb1(get_database("apitest_simpledata"));
1411 Xapian::Enquire
enquire1(mydb1
);
1414 for (int i
= 0; i
!= 3; ++i
) {
1415 v
.push_back("simpl");
1416 v
.push_back("queri");
1418 v
.push_back("rubbish");
1419 v
.push_back("rubbish");
1420 v
.push_back("rubbish");
1421 v
.push_back("word");
1422 v
.push_back("word");
1423 v
.push_back("word");
1426 Xapian::Query myquery1
= Xapian::Query(Xapian::Query::OP_ELITE_SET
,
1427 v
.begin(), v
.end(), 1);
1428 myquery1
= Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT
,
1432 enquire1
.set_query(myquery1
);
1433 // On architectures with excess precision (or, at least, on x86), the
1434 // following call used to result in a segfault.
1435 enquire1
.get_mset(0, 10);
1440 /// Test that the termfreq returned by termlists is correct.
1441 DEFINE_TESTCASE(termlisttermfreq1
, backend
) {
1442 Xapian::Database
mydb(get_database("apitest_simpledata"));
1443 Xapian::Enquire
enquire(mydb
);
1444 Xapian::Stem
stemmer("english");
1447 rset1
.add_document(5);
1448 rset2
.add_document(6);
1450 Xapian::ESet eset1
= enquire
.get_eset(1000, rset1
);
1451 Xapian::ESet eset2
= enquire
.get_eset(1000, rset2
);
1453 // search for weight of term 'another'
1454 string theterm
= stemmer("another");
1459 Xapian::ESetIterator i
= eset1
.begin();
1460 for ( ; i
!= eset1
.end(); i
++) {
1461 if (*i
== theterm
) {
1462 wt1
= i
.get_weight();
1468 Xapian::ESetIterator i
= eset2
.begin();
1469 for ( ; i
!= eset2
.end(); i
++) {
1470 if (*i
== theterm
) {
1471 wt2
= i
.get_weight();
1477 TEST_NOT_EQUAL(wt1
, 0);
1478 TEST_NOT_EQUAL(wt2
, 0);
1479 TEST_EQUAL(wt1
, wt2
);
1484 /// Test the termfrequency and termweight info returned for query terms
1485 DEFINE_TESTCASE(qterminfo1
, backend
) {
1486 Xapian::Database
mydb1(get_database("apitest_simpledata", "apitest_simpledata2"));
1487 Xapian::Enquire
enquire1(mydb1
);
1489 Xapian::Database
mydb2(get_database("apitest_simpledata"));
1490 mydb2
.add_database(get_database("apitest_simpledata2"));
1491 Xapian::Enquire
enquire2(mydb2
);
1494 Xapian::Stem
stemmer("english");
1496 string term1
= stemmer("word");
1497 string term2
= stemmer("inmemory");
1498 string term3
= stemmer("flibble");
1500 Xapian::Query
myquery(Xapian::Query::OP_OR
,
1501 Xapian::Query(term1
),
1502 Xapian::Query(Xapian::Query::OP_OR
,
1503 Xapian::Query(term2
),
1504 Xapian::Query(term3
)));
1505 enquire1
.set_query(myquery
);
1506 enquire2
.set_query(myquery
);
1508 // retrieve the results
1509 Xapian::MSet mymset1a
= enquire1
.get_mset(0, 0);
1510 Xapian::MSet mymset2a
= enquire2
.get_mset(0, 0);
1512 TEST_EQUAL(mymset1a
.get_termfreq(term1
),
1513 mymset2a
.get_termfreq(term1
));
1514 TEST_EQUAL(mymset1a
.get_termfreq(term2
),
1515 mymset2a
.get_termfreq(term2
));
1516 TEST_EQUAL(mymset1a
.get_termfreq(term3
),
1517 mymset2a
.get_termfreq(term3
));
1519 TEST_EQUAL(mymset1a
.get_termfreq(term1
), 3);
1520 TEST_EQUAL(mymset1a
.get_termfreq(term2
), 1);
1521 TEST_EQUAL(mymset1a
.get_termfreq(term3
), 0);
1523 TEST_NOT_EQUAL(mymset1a
.get_termweight(term1
), 0);
1524 TEST_NOT_EQUAL(mymset1a
.get_termweight(term2
), 0);
1525 // non-existent terms should have 0 weight.
1526 TEST_EQUAL(mymset1a
.get_termweight(term3
), 0);
1528 TEST_EQUAL(mymset1a
.get_termfreq(stemmer("banana")), 1);
1529 TEST_EXCEPTION(Xapian::InvalidArgumentError
,
1530 mymset1a
.get_termweight(stemmer("banana")));
1532 TEST_EQUAL(mymset1a
.get_termfreq("sponge"), 0);
1533 TEST_EXCEPTION(Xapian::InvalidArgumentError
,
1534 mymset1a
.get_termweight("sponge"));
1539 /// Regression test for bug #37.
1540 DEFINE_TESTCASE(qterminfo2
, backend
) {
1541 Xapian::Database
db(get_database("apitest_simpledata"));
1542 Xapian::Enquire
enquire(db
);
1545 Xapian::Stem
stemmer("english");
1547 string term1
= stemmer("paragraph");
1548 string term2
= stemmer("another");
1550 Xapian::Query
query(Xapian::Query::OP_AND_NOT
, term1
,
1551 Xapian::Query(Xapian::Query::OP_AND
, term1
, term2
));
1552 enquire
.set_query(query
);
1554 // retrieve the results
1555 // Note: get_mset() used to throw "AssertionError" in debug builds
1556 Xapian::MSet mset
= enquire
.get_mset(0, 10);
1558 TEST_NOT_EQUAL(mset
.get_termweight("paragraph"), 0);
1563 // tests that when specifying that no items are to be returned, those
1564 // statistics which should be the same are.
1565 DEFINE_TESTCASE(msetzeroitems1
, backend
) {
1566 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1567 enquire
.set_query(query("this"));
1568 Xapian::MSet mymset1
= enquire
.get_mset(0, 0);
1570 Xapian::MSet mymset2
= enquire
.get_mset(0, 1);
1572 TEST_EQUAL(mymset1
.get_max_possible(), mymset2
.get_max_possible());
1577 // test that the matches_* of a simple query are as expected
1578 DEFINE_TESTCASE(matches1
, backend
) {
1579 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1580 Xapian::Query myquery
;
1581 Xapian::MSet mymset
;
1583 myquery
= query("word");
1584 enquire
.set_query(myquery
);
1585 mymset
= enquire
.get_mset(0, 10);
1586 TEST_EQUAL(mymset
.get_matches_lower_bound(), 2);
1587 TEST_EQUAL(mymset
.get_matches_estimated(), 2);
1588 TEST_EQUAL(mymset
.get_matches_upper_bound(), 2);
1589 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 2);
1590 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 2);
1591 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 2);
1593 myquery
= query(Xapian::Query::OP_OR
, "inmemory", "word");
1594 enquire
.set_query(myquery
);
1595 mymset
= enquire
.get_mset(0, 10);
1596 TEST_EQUAL(mymset
.get_matches_lower_bound(), 2);
1597 TEST_EQUAL(mymset
.get_matches_estimated(), 2);
1598 TEST_EQUAL(mymset
.get_matches_upper_bound(), 2);
1599 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 2);
1600 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 2);
1601 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 2);
1603 myquery
= query(Xapian::Query::OP_AND
, "inmemory", "word");
1604 enquire
.set_query(myquery
);
1605 mymset
= enquire
.get_mset(0, 10);
1606 TEST_EQUAL(mymset
.get_matches_lower_bound(), 0);
1607 TEST_EQUAL(mymset
.get_matches_estimated(), 0);
1608 TEST_EQUAL(mymset
.get_matches_upper_bound(), 0);
1609 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 0);
1610 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 0);
1611 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 0);
1613 myquery
= query(Xapian::Query::OP_AND
, "simple", "word");
1614 enquire
.set_query(myquery
);
1615 mymset
= enquire
.get_mset(0, 10);
1616 TEST_EQUAL(mymset
.get_matches_lower_bound(), 2);
1617 TEST_EQUAL(mymset
.get_matches_estimated(), 2);
1618 TEST_EQUAL(mymset
.get_matches_upper_bound(), 2);
1619 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 2);
1620 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 2);
1621 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 2);
1623 myquery
= query(Xapian::Query::OP_AND
, "simple", "word");
1624 enquire
.set_query(myquery
);
1625 mymset
= enquire
.get_mset(0, 0);
1626 // For a single database, this is true, but not for "multi" (since there
1627 // one sub-database has 3 documents and simple and word both have termfreq
1628 // of 2, so the matcher can tell at least one document must match!)
1629 // TEST_EQUAL(mymset.get_matches_lower_bound(), 0);
1630 TEST_REL(mymset
.get_matches_lower_bound(),<=,mymset
.get_matches_estimated());
1631 TEST_EQUAL(mymset
.get_matches_estimated(), 1);
1632 TEST_EQUAL(mymset
.get_matches_upper_bound(), 2);
1633 TEST_REL(mymset
.get_uncollapsed_matches_lower_bound(),<=,mymset
.get_uncollapsed_matches_estimated());
1634 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 1);
1635 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 2);
1637 mymset
= enquire
.get_mset(0, 1);
1638 TEST_EQUAL(mymset
.get_matches_lower_bound(), 2);
1639 TEST_EQUAL(mymset
.get_matches_estimated(), 2);
1640 TEST_EQUAL(mymset
.get_matches_upper_bound(), 2);
1641 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 2);
1642 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 2);
1643 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 2);
1645 mymset
= enquire
.get_mset(0, 2);
1646 TEST_EQUAL(mymset
.get_matches_lower_bound(), 2);
1647 TEST_EQUAL(mymset
.get_matches_estimated(), 2);
1648 TEST_EQUAL(mymset
.get_matches_upper_bound(), 2);
1649 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 2);
1650 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 2);
1651 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 2);
1653 myquery
= query(Xapian::Query::OP_AND
, "paragraph", "another");
1654 enquire
.set_query(myquery
);
1655 mymset
= enquire
.get_mset(0, 0);
1656 TEST_EQUAL(mymset
.get_matches_lower_bound(), 1);
1657 TEST_EQUAL(mymset
.get_matches_estimated(), 2);
1658 TEST_EQUAL(mymset
.get_matches_upper_bound(), 2);
1659 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 1);
1660 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 2);
1661 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 2);
1663 mymset
= enquire
.get_mset(0, 1);
1664 TEST_EQUAL(mymset
.get_matches_lower_bound(), 1);
1665 TEST_EQUAL(mymset
.get_matches_estimated(), 2);
1666 TEST_EQUAL(mymset
.get_matches_upper_bound(), 2);
1667 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 1);
1668 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 2);
1669 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 2);
1671 mymset
= enquire
.get_mset(0, 2);
1672 TEST_EQUAL(mymset
.get_matches_lower_bound(), 1);
1673 TEST_EQUAL(mymset
.get_matches_estimated(), 1);
1674 TEST_EQUAL(mymset
.get_matches_upper_bound(), 1);
1675 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 1);
1676 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 1);
1677 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 1);
1679 mymset
= enquire
.get_mset(1, 20);
1680 TEST_EQUAL(mymset
.get_matches_lower_bound(), 1);
1681 TEST_EQUAL(mymset
.get_matches_estimated(), 1);
1682 TEST_EQUAL(mymset
.get_matches_upper_bound(), 1);
1683 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 1);
1684 TEST_EQUAL(mymset
.get_uncollapsed_matches_estimated(), 1);
1685 TEST_EQUAL(mymset
.get_uncollapsed_matches_upper_bound(), 1);
1690 // tests that wqf affects the document weights
1691 DEFINE_TESTCASE(wqf1
, backend
) {
1692 // Both queries have length 2; in q1 word has wqf=2, in q2 word has wqf=1
1693 Xapian::Query
q1("word", 2);
1694 Xapian::Query
q2("word");
1695 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1696 enquire
.set_query(q1
);
1697 Xapian::MSet mset1
= enquire
.get_mset(0, 10);
1698 enquire
.set_query(q2
);
1699 Xapian::MSet mset2
= enquire
.get_mset(0, 2);
1700 // Check the weights
1701 TEST(mset1
.begin().get_weight() > mset2
.begin().get_weight());
1705 // tests that query length affects the document weights
1706 DEFINE_TESTCASE(qlen1
, backend
) {
1707 Xapian::Query
q1("word");
1708 Xapian::Query
q2("word");
1709 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1710 enquire
.set_query(q1
);
1711 Xapian::MSet mset1
= enquire
.get_mset(0, 10);
1712 enquire
.set_query(q2
);
1713 Xapian::MSet mset2
= enquire
.get_mset(0, 2);
1714 // Check the weights
1715 //TEST(mset1.begin().get_weight() < mset2.begin().get_weight());
1716 TEST(mset1
.begin().get_weight() == mset2
.begin().get_weight());
1720 // tests that opening a non-existent termlist throws the correct exception
1721 DEFINE_TESTCASE(termlist1
, backend
) {
1722 Xapian::Database
db(get_database("apitest_onedoc"));
1723 TEST_EXCEPTION(Xapian::InvalidArgumentError
,
1724 Xapian::TermIterator t
= db
.termlist_begin(0));
1725 TEST_EXCEPTION(Xapian::DocNotFoundError
,
1726 Xapian::TermIterator t
= db
.termlist_begin(2));
1727 /* Cause the database to be used properly, showing up problems
1728 * with the link being in a bad state. CME */
1729 Xapian::TermIterator temp
= db
.termlist_begin(1);
1730 TEST_EXCEPTION(Xapian::DocNotFoundError
,
1731 Xapian::TermIterator t
= db
.termlist_begin(999999999));
1735 // tests that a Xapian::TermIterator works as an STL iterator
1736 DEFINE_TESTCASE(termlist2
, backend
) {
1737 Xapian::Database
db(get_database("apitest_onedoc"));
1738 Xapian::TermIterator t
= db
.termlist_begin(1);
1739 Xapian::TermIterator tend
= db
.termlist_end(1);
1741 // test operator= creates a copy which compares equal
1742 Xapian::TermIterator t_copy
= t
;
1743 TEST_EQUAL(t
, t_copy
);
1745 // test copy constructor creates a copy which compares equal
1746 Xapian::TermIterator
t_clone(t
);
1747 TEST_EQUAL(t
, t_clone
);
1749 vector
<string
> v(t
, tend
);
1751 t
= db
.termlist_begin(1);
1752 tend
= db
.termlist_end(1);
1753 vector
<string
>::const_iterator i
;
1754 for (i
= v
.begin(); i
!= v
.end(); ++i
) {
1755 TEST_NOT_EQUAL(t
, tend
);
1759 TEST_EQUAL(t
, tend
);
1763 static Xapian::TermIterator
1764 test_termlist3_helper()
1766 Xapian::Database
db(get_database("apitest_onedoc"));
1767 return db
.termlist_begin(1);
1770 // tests that a Xapian::TermIterator still works when the DB is deleted
1771 DEFINE_TESTCASE(termlist3
, backend
) {
1772 Xapian::TermIterator u
= test_termlist3_helper();
1773 Xapian::Database
db(get_database("apitest_onedoc"));
1774 Xapian::TermIterator t
= db
.termlist_begin(1);
1775 Xapian::TermIterator tend
= db
.termlist_end(1);
1786 DEFINE_TESTCASE(termlist4
, backend
) {
1787 Xapian::Database
db(get_database("apitest_onedoc"));
1788 Xapian::TermIterator i
= db
.termlist_begin(1);
1794 // tests punctuation is OK in terms (particularly in remote queries)
1795 DEFINE_TESTCASE(puncterms1
, backend
) {
1796 Xapian::Database
db(get_database("apitest_punc"));
1797 Xapian::Enquire
enquire(db
);
1799 Xapian::Query
q1("semi;colon");
1800 enquire
.set_query(q1
);
1801 Xapian::MSet m1
= enquire
.get_mset(0, 10);
1803 Xapian::Query
q2("col:on");
1804 enquire
.set_query(q2
);
1805 Xapian::MSet m2
= enquire
.get_mset(0, 10);
1807 Xapian::Query
q3("com,ma");
1808 enquire
.set_query(q3
);
1809 Xapian::MSet m3
= enquire
.get_mset(0, 10);
1814 // test that searching for a term with a space or backslash in it works
1815 DEFINE_TESTCASE(spaceterms1
, backend
) {
1816 Xapian::Enquire
enquire(get_database("apitest_space"));
1817 Xapian::MSet mymset
;
1818 Xapian::doccount count
;
1819 Xapian::MSetIterator m
;
1820 Xapian::Stem
stemmer("english");
1822 enquire
.set_query(stemmer("space man"));
1823 mymset
= enquire
.get_mset(0, 10);
1824 TEST_MSET_SIZE(mymset
, 1);
1826 for (m
= mymset
.begin(); m
!= mymset
.end(); ++m
) ++count
;
1827 TEST_EQUAL(count
, 1);
1829 for (Xapian::valueno value_no
= 1; value_no
< 7; ++value_no
) {
1830 TEST_NOT_EQUAL(mymset
.begin().get_document().get_data(), "");
1831 TEST_NOT_EQUAL(mymset
.begin().get_document().get_value(value_no
), "");
1834 enquire
.set_query(stemmer("tab\tby"));
1835 mymset
= enquire
.get_mset(0, 10);
1836 TEST_MSET_SIZE(mymset
, 1);
1838 for (m
= mymset
.begin(); m
!= mymset
.end(); ++m
) ++count
;
1839 TEST_EQUAL(count
, 1);
1841 for (Xapian::valueno value_no
= 0; value_no
< 7; ++value_no
) {
1842 string value
= mymset
.begin().get_document().get_value(value_no
);
1843 TEST_NOT_EQUAL(value
, "");
1844 if (value_no
== 0) {
1845 TEST(value
.size() > 262);
1846 TEST_EQUAL(static_cast<unsigned char>(value
[262]), 255);
1850 enquire
.set_query(stemmer("back\\slash"));
1851 mymset
= enquire
.get_mset(0, 10);
1852 TEST_MSET_SIZE(mymset
, 1);
1854 for (m
= mymset
.begin(); m
!= mymset
.end(); ++m
) ++count
;
1855 TEST_EQUAL(count
, 1);
1860 // test that XOR queries work
1861 DEFINE_TESTCASE(xor1
, backend
) {
1862 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1863 Xapian::Stem
stemmer("english");
1865 vector
<string
> terms
;
1866 terms
.push_back(stemmer("this"));
1867 terms
.push_back(stemmer("word"));
1868 terms
.push_back(stemmer("of"));
1870 Xapian::Query
query(Xapian::Query::OP_XOR
, terms
.begin(), terms
.end());
1871 enquire
.set_weighting_scheme(Xapian::BoolWeight());
1872 enquire
.set_query(query
);
1874 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
1875 // Docid this word of Match?
1882 mset_expect_order(mymset
, 1, 2, 5, 6);
1887 /// Test that weighted XOR queries work (bug fixed in 1.2.1 and 1.0.21).
1888 DEFINE_TESTCASE(xor2
, backend
) {
1889 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1890 Xapian::Stem
stemmer("english");
1892 vector
<string
> terms
;
1893 terms
.push_back(stemmer("this"));
1894 terms
.push_back(stemmer("word"));
1895 terms
.push_back(stemmer("of"));
1897 Xapian::Query
query(Xapian::Query::OP_XOR
, terms
.begin(), terms
.end());
1898 enquire
.set_query(query
);
1900 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
1901 // Docid LEN this word of Match?
1908 mset_expect_order(mymset
, 2, 1, 5, 6);
1913 // test Xapian::Database::get_document()
1914 DEFINE_TESTCASE(getdoc1
, backend
) {
1915 Xapian::Database
db(get_database("apitest_onedoc"));
1916 Xapian::Document
doc(db
.get_document(1));
1917 TEST_EXCEPTION(Xapian::InvalidArgumentError
, db
.get_document(0));
1918 TEST_EXCEPTION(Xapian::DocNotFoundError
, db
.get_document(999999999));
1919 TEST_EXCEPTION(Xapian::DocNotFoundError
, db
.get_document(123456789));
1920 TEST_EXCEPTION(Xapian::DocNotFoundError
, db
.get_document(3));
1921 TEST_EXCEPTION(Xapian::DocNotFoundError
, db
.get_document(2));
1922 // Check that Document works as a handle on modification
1923 // (this was broken for the first try at Xapian::Document prior to 0.7).
1924 Xapian::Document doc2
= doc
;
1925 doc
.set_data("modified!");
1926 TEST_EQUAL(doc
.get_data(), "modified!");
1927 TEST_EQUAL(doc
.get_data(), doc2
.get_data());
1931 // test whether operators with no elements work as a null query
1932 DEFINE_TESTCASE(emptyop1
, backend
) {
1933 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1934 vector
<Xapian::Query
> nullvec
;
1936 Xapian::Query
query1(Xapian::Query::OP_XOR
, nullvec
.begin(), nullvec
.end());
1938 enquire
.set_query(query1
);
1939 Xapian::MSet mymset
= enquire
.get_mset(0, 10);
1940 TEST_MSET_SIZE(mymset
, 0);
1941 // In Xapian < 1.3.0, this gave InvalidArgumentError (because
1942 // query1.empty()) but elsewhere we treat an empty query as just not
1943 // matching any documents, so we now do the same here too.
1944 TEST_EQUAL(enquire
.get_matching_terms_begin(1),
1945 enquire
.get_matching_terms_end(1));
1950 // Regression test for check_at_least SEGV when there are no matches.
1951 DEFINE_TESTCASE(checkatleast1
, backend
) {
1952 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1953 enquire
.set_query(Xapian::Query("thom"));
1954 Xapian::MSet mymset
= enquire
.get_mset(0, 10, 11);
1955 TEST_EQUAL(0, mymset
.size());
1960 // Regression test - if check_at_least was set we returned (check_at_least - 1)
1961 // results, rather than the requested msize. Fixed in 1.0.2.
1962 DEFINE_TESTCASE(checkatleast2
, backend
) {
1963 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
1964 enquire
.set_query(Xapian::Query("paragraph"));
1966 Xapian::MSet mymset
= enquire
.get_mset(0, 3, 10);
1967 TEST_MSET_SIZE(mymset
, 3);
1968 TEST_EQUAL(mymset
.get_matches_lower_bound(), 5);
1969 TEST_EQUAL(mymset
.get_uncollapsed_matches_lower_bound(), 5);
1971 mymset
= enquire
.get_mset(0, 2, 4);
1972 TEST_MSET_SIZE(mymset
, 2);
1973 TEST_REL(mymset
.get_matches_lower_bound(),>=,4);
1974 TEST_REL(mymset
.get_matches_lower_bound(),>=,4);
1975 TEST_REL(mymset
.get_uncollapsed_matches_lower_bound(),>=,4);
1976 TEST_REL(mymset
.get_uncollapsed_matches_lower_bound(),>=,4);
1981 // Feature tests - check_at_least with various sorting options.
1982 DEFINE_TESTCASE(checkatleast3
, backend
) {
1983 Xapian::Enquire
enquire(get_database("etext"));
1984 enquire
.set_query(Xapian::Query("prussian")); // 60 matches.
1986 for (int order
= 0; order
< 3; ++order
) {
1989 enquire
.set_docid_order(Xapian::Enquire::ASCENDING
);
1992 enquire
.set_docid_order(Xapian::Enquire::DESCENDING
);
1995 enquire
.set_docid_order(Xapian::Enquire::DONT_CARE
);
1999 for (int sort
= 0; sort
< 7; ++sort
) {
2000 bool reverse
= (sort
& 1);
2003 enquire
.set_sort_by_relevance();
2006 enquire
.set_sort_by_value(0, reverse
);
2009 enquire
.set_sort_by_value_then_relevance(0, reverse
);
2012 enquire
.set_sort_by_relevance_then_value(0, reverse
);
2016 Xapian::MSet mset
= enquire
.get_mset(0, 100, 500);
2017 TEST_MSET_SIZE(mset
, 60);
2018 TEST_EQUAL(mset
.get_matches_lower_bound(), 60);
2019 TEST_EQUAL(mset
.get_matches_estimated(), 60);
2020 TEST_EQUAL(mset
.get_matches_upper_bound(), 60);
2021 TEST_EQUAL(mset
.get_uncollapsed_matches_lower_bound(), 60);
2022 TEST_EQUAL(mset
.get_uncollapsed_matches_estimated(), 60);
2023 TEST_EQUAL(mset
.get_uncollapsed_matches_upper_bound(), 60);
2025 mset
= enquire
.get_mset(0, 50, 100);
2026 TEST_MSET_SIZE(mset
, 50);
2027 TEST_EQUAL(mset
.get_matches_lower_bound(), 60);
2028 TEST_EQUAL(mset
.get_matches_estimated(), 60);
2029 TEST_EQUAL(mset
.get_matches_upper_bound(), 60);
2030 TEST_EQUAL(mset
.get_uncollapsed_matches_lower_bound(), 60);
2031 TEST_EQUAL(mset
.get_uncollapsed_matches_estimated(), 60);
2032 TEST_EQUAL(mset
.get_uncollapsed_matches_upper_bound(), 60);
2034 mset
= enquire
.get_mset(0, 10, 50);
2035 TEST_MSET_SIZE(mset
, 10);
2036 TEST_REL(mset
.get_matches_lower_bound(),>=,50);
2037 TEST_REL(mset
.get_uncollapsed_matches_lower_bound(),>=,50);
2044 // tests all document postlists
2045 DEFINE_TESTCASE(allpostlist1
, backend
) {
2046 Xapian::Database
db(get_database("apitest_manydocs"));
2047 Xapian::PostingIterator i
= db
.postlist_begin("");
2049 while (i
!= db
.postlist_end("")) {
2056 i
= db
.postlist_begin("");
2058 while (i
!= db
.postlist_end("")) {
2072 static void test_emptyterm1_helper(Xapian::Database
& db
)
2074 // Don't bother with postlist_begin() because allpostlist tests cover that.
2075 TEST_EXCEPTION(Xapian::InvalidArgumentError
, db
.positionlist_begin(1, ""));
2076 TEST_EQUAL(db
.get_doccount(), db
.get_termfreq(""));
2077 TEST_EQUAL(db
.get_doccount() != 0, db
.term_exists(""));
2078 TEST_EQUAL(db
.get_doccount(), db
.get_collection_freq(""));
2081 // tests results of passing an empty term to various methods
2082 DEFINE_TESTCASE(emptyterm1
, backend
) {
2083 Xapian::Database
db(get_database("apitest_manydocs"));
2084 TEST_EQUAL(db
.get_doccount(), 512);
2085 test_emptyterm1_helper(db
);
2087 db
= get_database("apitest_onedoc");
2088 TEST_EQUAL(db
.get_doccount(), 1);
2089 test_emptyterm1_helper(db
);
2091 db
= get_database("");
2092 TEST_EQUAL(db
.get_doccount(), 0);
2093 test_emptyterm1_helper(db
);
2098 // Test for alldocs postlist with a sparse database.
2099 DEFINE_TESTCASE(alldocspl1
, writable
) {
2100 Xapian::WritableDatabase db
= get_writable_database();
2101 Xapian::Document doc
;
2103 doc
.add_value(0, "5");
2104 db
.replace_document(5, doc
);
2106 Xapian::PostingIterator i
= db
.postlist_begin("");
2107 TEST(i
!= db
.postlist_end(""));
2109 TEST_EQUAL(i
.get_doclength(), 0);
2110 TEST_EQUAL(i
.get_unique_terms(), 0);
2111 TEST_EQUAL(i
.get_wdf(), 1);
2113 TEST(i
== db
.postlist_end(""));
2118 // Test reading and writing a modified alldocspostlist.
2119 DEFINE_TESTCASE(alldocspl2
, writable
) {
2120 Xapian::PostingIterator i
, end
;
2122 Xapian::WritableDatabase db
= get_writable_database();
2123 Xapian::Document doc
;
2125 doc
.add_value(0, "5");
2126 db
.replace_document(5, doc
);
2128 // Test iterating before committing the changes.
2129 i
= db
.postlist_begin("");
2130 end
= db
.postlist_end("");
2133 TEST_EQUAL(i
.get_doclength(), 0);
2134 TEST_EQUAL(i
.get_unique_terms(), 0);
2135 TEST_EQUAL(i
.get_wdf(), 1);
2141 // Test iterating after committing the changes.
2142 i
= db
.postlist_begin("");
2143 end
= db
.postlist_end("");
2146 TEST_EQUAL(i
.get_doclength(), 0);
2147 TEST_EQUAL(i
.get_unique_terms(), 0);
2148 TEST_EQUAL(i
.get_wdf(), 1);
2152 // Add another document.
2153 doc
= Xapian::Document();
2155 doc
.add_value(0, "7");
2156 db
.replace_document(7, doc
);
2158 // Test iterating through before committing the changes.
2159 i
= db
.postlist_begin("");
2160 end
= db
.postlist_end("");
2163 TEST_EQUAL(i
.get_doclength(), 0);
2164 TEST_EQUAL(i
.get_unique_terms(), 0);
2165 TEST_EQUAL(i
.get_wdf(), 1);
2169 TEST_EQUAL(i
.get_doclength(), 0);
2170 TEST_EQUAL(i
.get_unique_terms(), 0);
2171 TEST_EQUAL(i
.get_wdf(), 1);
2175 // Delete the first document.
2176 db
.delete_document(5);
2178 // Test iterating through before committing the changes.
2179 i
= db
.postlist_begin("");
2180 end
= db
.postlist_end("");
2183 TEST_EQUAL(i
.get_doclength(), 0);
2184 TEST_EQUAL(i
.get_unique_terms(), 0);
2185 TEST_EQUAL(i
.get_wdf(), 1);
2189 // Test iterating through after committing the changes, and dropping the
2190 // reference to the main DB.
2192 i
= db
.postlist_begin("");
2193 end
= db
.postlist_end("");
2198 TEST_EQUAL(i
.get_doclength(), 0);
2199 TEST_EQUAL(i
.get_unique_terms(), 0);
2200 TEST_EQUAL(i
.get_wdf(), 1);
2207 // Feature test for Query::OP_SCALE_WEIGHT.
2208 DEFINE_TESTCASE(scaleweight1
, backend
) {
2209 Xapian::Database
db(get_database("apitest_phrase"));
2210 Xapian::Enquire
enq(db
);
2211 Xapian::QueryParser qp
;
2213 static const char * queries
[] = {
2216 "leave milk on fridge",
2217 "ordered milk operator",
2218 "ordered phrase operator",
2219 "leave \"milk on fridge\"",
2221 "leave \"milk notpresent\"",
2224 static const double multipliers
[] = {
2225 -1000000, -2.5, -1, -0.5, 0, 0.5, 1, 2.5, 1000000,
2229 for (const char **qstr
= queries
; *qstr
; ++qstr
) {
2231 Xapian::Query query1
= qp
.parse_query(*qstr
);
2232 tout
<< "query1: " << query1
.get_description() << endl
;
2233 for (const double *multp
= multipliers
; multp
[0] != multp
[1]; ++multp
) {
2234 double mult
= *multp
;
2236 TEST_EXCEPTION(Xapian::InvalidArgumentError
,
2237 Xapian::Query(Xapian::Query::OP_SCALE_WEIGHT
,
2241 Xapian::Query
query2(Xapian::Query::OP_SCALE_WEIGHT
, query1
, mult
);
2242 tout
<< "query2: " << query2
.get_description() << endl
;
2244 enq
.set_query(query1
);
2245 Xapian::MSet mset1
= enq
.get_mset(0, 20);
2246 enq
.set_query(query2
);
2247 Xapian::MSet mset2
= enq
.get_mset(0, 20);
2249 TEST_EQUAL(mset1
.size(), mset2
.size());
2251 Xapian::MSetIterator i1
, i2
;
2253 for (i1
= mset1
.begin(), i2
= mset2
.begin();
2254 i1
!= mset1
.end() && i2
!= mset2
.end(); ++i1
, ++i2
) {
2255 TEST_EQUAL_DOUBLE(i1
.get_weight() * mult
, i2
.get_weight());
2256 TEST_EQUAL(*i1
, *i2
);
2259 // Weights in mset2 are 0; so it should be sorted by docid.
2260 vector
<Xapian::docid
> ids1
;
2261 vector
<Xapian::docid
> ids2
;
2262 for (i1
= mset1
.begin(), i2
= mset2
.begin();
2263 i1
!= mset1
.end() && i2
!= mset2
.end(); ++i1
, ++i2
) {
2264 TEST_NOT_EQUAL_DOUBLE(i1
.get_weight(), 0);
2265 TEST_EQUAL_DOUBLE(i2
.get_weight(), 0);
2266 ids1
.push_back(*i1
);
2267 ids2
.push_back(*i2
);
2269 sort(ids1
.begin(), ids1
.end());
2270 TEST_EQUAL(ids1
, ids2
);
2277 // Test Query::OP_SCALE_WEIGHT being used to multiply some of the weights of a
2279 DEFINE_TESTCASE(scaleweight2
, backend
) {
2280 Xapian::Database
db(get_database("apitest_phrase"));
2281 Xapian::Enquire
enq(db
);
2282 Xapian::MSetIterator i
;
2284 Xapian::Query
query1("fridg");
2285 Xapian::Query
query2(Xapian::Query::OP_SCALE_WEIGHT
, query1
, 2.5);
2286 Xapian::Query
query3("milk");
2287 Xapian::Query
query4(Xapian::Query::OP_SCALE_WEIGHT
, query3
, 0);
2288 Xapian::Query
query5(Xapian::Query::OP_OR
, query2
, query4
);
2290 // query5 should first return the same results as query1, in the same
2291 // order, and then return the results of query3 which aren't also results
2292 // of query1, in ascending docid order. We test that this happens.
2294 // First, build a vector of docids matching the first part of the query,
2295 // and append the non-duplicate docids matching the second part of the
2297 vector
<Xapian::docid
> ids1
;
2298 set
<Xapian::docid
> idsin1
;
2299 vector
<Xapian::docid
> ids3
;
2301 enq
.set_query(query1
);
2302 Xapian::MSet mset1
= enq
.get_mset(0, 20);
2303 enq
.set_query(query3
);
2304 Xapian::MSet mset3
= enq
.get_mset(0, 20);
2305 TEST_NOT_EQUAL(mset1
.size(), 0);
2306 for (i
= mset1
.begin(); i
!= mset1
.end(); ++i
) {
2310 TEST_NOT_EQUAL(mset3
.size(), 0);
2311 for (i
= mset3
.begin(); i
!= mset3
.end(); ++i
) {
2312 if (idsin1
.find(*i
) != idsin1
.end())
2316 sort(ids3
.begin(), ids3
.end());
2317 ids1
.insert(ids1
.end(), ids3
.begin(), ids3
.end());
2319 // Now, run the combined query and build a vector of the matching docids.
2320 vector
<Xapian::docid
> ids5
;
2321 enq
.set_query(query5
);
2322 Xapian::MSet mset5
= enq
.get_mset(0, 20);
2323 for (i
= mset5
.begin(); i
!= mset5
.end(); ++i
) {
2327 TEST_EQUAL(ids1
, ids5
);
2331 // Regression test for bug fixed in 1.0.5 - this test would failed under
2332 // valgrind because it used an uninitialised value.
2333 DEFINE_TESTCASE(bm25weight1
, backend
) {
2334 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
2335 enquire
.set_weighting_scheme(Xapian::BM25Weight(1, 25, 1, 0.01, 0.5));
2336 enquire
.set_query(Xapian::Query("word"));
2338 Xapian::MSet mset
= enquire
.get_mset(0, 25);
2343 // Feature test for TradWeight.
2344 DEFINE_TESTCASE(tradweight1
, backend
) {
2345 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
2346 enquire
.set_weighting_scheme(Xapian::TradWeight());
2347 enquire
.set_query(Xapian::Query("word"));
2349 Xapian::MSet mset
= enquire
.get_mset(0, 25);
2350 TEST_EQUAL(mset
.size(), 2);
2352 enquire
.set_weighting_scheme(Xapian::TradWeight(0));
2353 enquire
.set_query(Xapian::Query("this"));
2355 mset
= enquire
.get_mset(0, 25);
2356 TEST_EQUAL(mset
.size(), 6);
2358 // Check that TradWeight(0) means wdf and doc length really don't affect
2359 // the weights as stated in the documentation.
2360 TEST_EQUAL(mset
[0].get_weight(), mset
[5].get_weight());
2365 // Test TradWeight when weighting documents using an RSet.
2366 // Simply changed the weighting scheme used by rset2 testcase.
2367 DEFINE_TESTCASE(tradweight4
, backend
) {
2368 Xapian::Database
mydb(get_database("apitest_rset"));
2369 Xapian::Enquire
enquire(mydb
);
2370 Xapian::Query myquery
= query(Xapian::Query::OP_OR
, "cuddly", "people");
2372 enquire
.set_query(myquery
);
2373 enquire
.set_weighting_scheme(Xapian::TradWeight());
2375 Xapian::MSet mymset1
= enquire
.get_mset(0, 10);
2377 Xapian::RSet myrset
;
2378 myrset
.add_document(2);
2380 Xapian::MSet mymset2
= enquire
.get_mset(0, 10, &myrset
);
2382 mset_expect_order(mymset1
, 1, 2);
2383 // Document 2 should have higher weight than document 1 despite the wdf of
2384 // "people" being 1 because "people" indexes a document in the RSet whereas
2385 // "cuddly" (wdf=2) does not.
2386 mset_expect_order(mymset2
, 2, 1);
2391 // Feature test for Database::get_uuid().
2392 DEFINE_TESTCASE(uuid1
, backend
&& !multi
) {
2393 SKIP_TEST_FOR_BACKEND("inmemory");
2394 Xapian::Database db
= get_database("apitest_simpledata");
2395 string uuid1
= db
.get_uuid();
2396 TEST_EQUAL(uuid1
.size(), 36);
2398 // A database with no sub-databases has an empty UUID.
2399 Xapian::Database db2
;
2400 TEST(db2
.get_uuid().empty());
2402 db2
.add_database(db
);
2403 TEST_EQUAL(uuid1
, db2
.get_uuid());
2405 // Multi-database has multiple UUIDs (we don't define the format exactly
2406 // so this assumes something about the implementation).
2407 db2
.add_database(db
);
2408 TEST_EQUAL(uuid1
+ ":" + uuid1
, db2
.get_uuid());
2410 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
2411 // This relies on InMemory databases not supporting uuids.
2412 // A multi-database containing a database with no uuid has no uuid.
2413 db2
.add_database(Xapian::Database(string(), Xapian::DB_BACKEND_INMEMORY
));
2414 TEST(db2
.get_uuid().empty());