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