xapian-core  1.4.22
api_sorting.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2007,2008,2009,2012,2017 Olly Betts
5  * Copyright (C) 2010 Richard Boulton
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include <config.h>
23 
24 #include "api_sorting.h"
25 
26 #include <xapian.h>
27 
28 #include "apitest.h"
29 #include "testutils.h"
30 
31 using namespace std;
32 
33 DEFINE_TESTCASE(sortfunctor1, backend && !remote) {
34  Xapian::Enquire enquire(get_database("apitest_sortrel"));
35  enquire.set_query(Xapian::Query("woman"));
36 
37  {
38  static const int keys[] = { 3, 1 };
39  Xapian::MultiValueKeyMaker sorter(keys, keys + 2);
40 
41  enquire.set_sort_by_key(&sorter, true);
42  Xapian::MSet mset = enquire.get_mset(0, 10);
43  mset_expect_order(mset, 2, 6, 7, 1, 3, 4, 5, 8, 9);
44 
45  for (auto m = mset.begin(); m != mset.end(); ++m) {
46  const string& data = m.get_document().get_data();
47  string exp;
48  exp += data[3];
49  exp += string(2, '\0');
50  exp += data[1];
51  TEST_EQUAL(m.get_sort_key(), exp);
52  }
53  }
54 
55  {
57  sorter.add_value(3);
58  sorter.add_value(1, true);
59 
60  enquire.set_sort_by_key(&sorter, true);
61  Xapian::MSet mset = enquire.get_mset(0, 10);
62  mset_expect_order(mset, 7, 6, 2, 8, 9, 4, 5, 1, 3);
63 
64  for (auto m = mset.begin(); m != mset.end(); ++m) {
65  const string& data = m.get_document().get_data();
66  string exp;
67  exp += data[3];
68  exp += string(2, '\0');
69  exp += char(0xff - data[1]);
70  exp += string(2, '\xff');
71  TEST_EQUAL(m.get_sort_key(), exp);
72  }
73  }
74 
75  {
77  sorter.add_value(100); // Value 100 isn't set.
78  sorter.add_value(3);
79  sorter.add_value(1, true);
80 
81  enquire.set_sort_by_key(&sorter, true);
82  Xapian::MSet mset = enquire.get_mset(0, 10);
83  mset_expect_order(mset, 7, 6, 2, 8, 9, 4, 5, 1, 3);
84 
85  for (auto m = mset.begin(); m != mset.end(); ++m) {
86  const string& data = m.get_document().get_data();
87  string exp;
88  exp += string(2, '\0');
89  exp += data[3];
90  exp += string(2, '\0');
91  exp += char(0xff - data[1]);
92  exp += string(2, '\xff');
93  TEST_EQUAL(m.get_sort_key(), exp);
94  }
95  }
96 
97  {
99  sorter.add_value(10); // Value 10 isn't always set.
100  sorter.add_value(1, true);
101 
102  enquire.set_sort_by_key(&sorter, true);
103  Xapian::MSet mset = enquire.get_mset(0, 10);
104  mset_expect_order(mset, 8, 9, 4, 5, 1, 3, 7, 6, 2);
105 
106  for (auto m = mset.begin(); m != mset.end(); ++m) {
107  const string& data = m.get_document().get_data();
108  string exp;
109  if (data.size() > 10) exp += data[10];
110  exp += string(2, '\0');
111  exp += char(0xff - data[1]);
112  exp += string(2, '\xff');
113  TEST_EQUAL(m.get_sort_key(), exp);
114  }
115  }
116 }
117 
119 DEFINE_TESTCASE(sortfunctor2, writable && !remote) {
121  Xapian::Document doc;
122  doc.add_term("foo");
123  doc.add_value(0, "ABB");
124  db.add_document(doc);
125  doc.add_value(0, "ABC");
126  db.add_document(doc);
127  doc.add_value(0, string("ABC", 4));
128  db.add_document(doc);
129  doc.add_value(0, "ABCD");
130  db.add_document(doc);
131  doc.add_value(0, "ABC\xff");
132  db.add_document(doc);
133 
134  Xapian::Enquire enquire(db);
135  enquire.set_query(Xapian::Query("foo"));
136 
137  {
139  sorter.add_value(0);
140  enquire.set_sort_by_key(&sorter, true);
141  Xapian::MSet mset = enquire.get_mset(0, 10);
142  mset_expect_order(mset, 5, 4, 3, 2, 1);
143  }
144 
145  {
147  sorter.add_value(0, true);
148  enquire.set_sort_by_key(&sorter, true);
149  Xapian::MSet mset = enquire.get_mset(0, 10);
150  mset_expect_order(mset, 1, 2, 3, 4, 5);
151  }
152 
153  {
155  sorter.add_value(0);
156  sorter.add_value(1);
157  enquire.set_sort_by_key(&sorter, true);
158  Xapian::MSet mset = enquire.get_mset(0, 10);
159  mset_expect_order(mset, 5, 4, 3, 2, 1);
160  }
161 
162  {
164  sorter.add_value(0, true);
165  sorter.add_value(1);
166  enquire.set_sort_by_key(&sorter, true);
167  Xapian::MSet mset = enquire.get_mset(0, 10);
168  mset_expect_order(mset, 1, 2, 3, 4, 5);
169  }
170 
171  {
173  sorter.add_value(0);
174  sorter.add_value(1, true);
175  enquire.set_sort_by_key(&sorter, true);
176  Xapian::MSet mset = enquire.get_mset(0, 10);
177  mset_expect_order(mset, 5, 4, 3, 2, 1);
178  }
179 
180  {
182  sorter.add_value(0, true);
183  sorter.add_value(1, true);
184  enquire.set_sort_by_key(&sorter, true);
185  Xapian::MSet mset = enquire.get_mset(0, 10);
186  mset_expect_order(mset, 1, 2, 3, 4, 5);
187  }
188 }
189 
190 // Test sort functor with some empty values.
191 DEFINE_TESTCASE(sortfunctor3, backend && !remote && valuestats) {
192  Xapian::Database db(get_database("apitest_sortrel"));
193  Xapian::Enquire enquire(db);
194  enquire.set_query(Xapian::Query("woman"));
195 
196  // Value 10 is set to 'a' for 1, 3, 4, 5, 8, 9, and not set otherwise.
197  {
198  // Test default sort order - missing values come first.
200  sorter.add_value(10);
201 
202  enquire.set_sort_by_key(&sorter, false);
203  Xapian::MSet mset = enquire.get_mset(0, 10);
204  mset_expect_order(mset, 2, 6, 7, 1, 3, 4, 5, 8, 9);
205  }
206 
207  {
208  // Use a default value to put the missing values to the end.
210  sorter.add_value(10, false, db.get_value_upper_bound(10) + '\xff');
211 
212  enquire.set_sort_by_key(&sorter, false);
213  Xapian::MSet mset = enquire.get_mset(0, 10);
214  mset_expect_order(mset, 1, 3, 4, 5, 8, 9, 2, 6, 7);
215  }
216 
217  {
218  // Test using a default value and sorting in reverse order
220  sorter.add_value(10, false, db.get_value_upper_bound(10) + '\xff');
221 
222  enquire.set_sort_by_key(&sorter, true);
223  Xapian::MSet mset = enquire.get_mset(0, 10);
224  mset_expect_order(mset, 2, 6, 7, 1, 3, 4, 5, 8, 9);
225  }
226 
227  {
228  // Test using a default value and generating reverse order keys
230  sorter.add_value(10, true, db.get_value_upper_bound(10) + '\xff');
231 
232  enquire.set_sort_by_key(&sorter, false);
233  Xapian::MSet mset = enquire.get_mset(0, 10);
234  mset_expect_order(mset, 2, 6, 7, 1, 3, 4, 5, 8, 9);
235  }
236 
237  {
238  // Test using a default value, generating reverse order keys, and
239  // sorting in reverse order
241  sorter.add_value(10, true, db.get_value_upper_bound(10) + '\xff');
242 
243  enquire.set_sort_by_key(&sorter, true);
244  Xapian::MSet mset = enquire.get_mset(0, 10);
245  mset_expect_order(mset, 1, 3, 4, 5, 8, 9, 2, 6, 7);
246  }
247 }
248 
250  public:
251  std::string operator() (const Xapian::Document &) const
252  {
253  FAIL_TEST("NeverUseMeKeyMaker was called");
254  }
255 };
256 
258 DEFINE_TESTCASE(changesorter1, backend && !remote) {
259  Xapian::Enquire enquire(get_database("apitest_simpledata"));
260  enquire.set_query(Xapian::Query("word"));
261  NeverUseMeKeyMaker sorter;
262 
263  enquire.set_sort_by_key(&sorter, true);
264  enquire.set_sort_by_value(0, true);
265  Xapian::MSet mset = enquire.get_mset(0, 25);
266  TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
267 
268  enquire.set_sort_by_key(&sorter, true);
269  enquire.set_sort_by_value_then_relevance(0, true);
270  mset = enquire.get_mset(0, 25);
271  TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
272 
273  enquire.set_sort_by_key(&sorter, true);
274  enquire.set_sort_by_relevance_then_value(0, true);
275  mset = enquire.get_mset(0, 25);
276  TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
277 
278  enquire.set_sort_by_key(&sorter, true);
279  enquire.set_sort_by_relevance();
280  mset = enquire.get_mset(0, 25);
281  TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
282 
283  // Check that NeverUseMeKeyMaker::operator() would actually cause a test
284  // failure if called.
285  try {
286  sorter(Xapian::Document());
287  FAIL_TEST("NeverUseMeKeyMaker::operator() didn't throw TestFail");
288  } catch (const TestFail &) {
289  }
290 }
291 
293 DEFINE_TESTCASE(sortfunctorempty1, backend && !remote) {
294  Xapian::Enquire enquire(get_database("apitest_sortrel"));
295  enquire.set_query(Xapian::Query("woman"));
296 
297  {
298  int i;
299  Xapian::MultiValueKeyMaker sorter(&i, &i);
300 
301  enquire.set_sort_by_key(&sorter, true);
302  Xapian::MSet mset = enquire.get_mset(0, 10);
303  mset_expect_order(mset, 1, 2, 3, 4, 5, 6, 7, 8, 9);
304  }
305 }
306 
307 DEFINE_TESTCASE(multivaluekeymaker1, !backend) {
308  static const int keys[] = { 0, 1, 2, 3 };
309  Xapian::MultiValueKeyMaker sorter(keys, keys + 4);
310 
311  Xapian::Document doc;
312  TEST(sorter(doc).empty());
313 
314  doc.add_value(1, "foo");
315  TEST_EQUAL(sorter(doc), string("\0\0foo", 5));
316  doc.add_value(1, string("f\0o", 3));
317  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo", 6));
318  doc.add_value(3, "xyz");
319  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz", 13));
320 
321  // An empty slot at the end, in reverse order, is terminated with \xff\xff
322  sorter.add_value(4, true);
323  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff", 17));
324 
325  // An empty slot at the end, in ascending order, has no effect
326  sorter.add_value(0);
327  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff", 17));
328 
329  // An empty slot at the end, with a default value
330  sorter.add_value(0, false, "hi");
331  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff\0\0hi",
332  21));
333 
334  // An empty slot at the end, with a default value, in reverse sort order
335  sorter.add_value(0, true, "hi");
336  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff\0\0hi"
337  "\0\0\x97\x96\xff\xff", 27));
338 }
339 
340 DEFINE_TESTCASE(sortfunctorremote1, remote) {
341  Xapian::Enquire enquire(get_database(string()));
342  NeverUseMeKeyMaker sorter;
343  enquire.set_query(Xapian::Query("word"));
344  enquire.set_sort_by_key(&sorter, true);
346  Xapian::MSet mset = enquire.get_mset(0, 10);
347  );
348 }
Xapian::doccount size() const
Return number of items in this MSet object.
Definition: omenquire.cc:318
Xapian::docid add_document(const Xapian::Document &document)
Add a new document to the database.
Definition: omdatabase.cc:902
void add_value(Xapian::valueno slot, const std::string &value)
Add a new value.
Definition: omdocument.cc:107
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
void set_sort_by_relevance()
Set the sorting to be by relevance only.
Definition: omenquire.cc:863
#define TEST(a)
Test a condition, without an additional explanation for failure.
Definition: testsuite.h:275
Class which is thrown when a test case fails.
Definition: testsuite.h:46
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
void add_value(Xapian::valueno slot, bool reverse=false, const std::string &defvalue=std::string())
Add a value slot to the list to build a key from.
Definition: keymaker.h:143
Xapian::WritableDatabase get_writable_database(const string &dbname)
Definition: apitest.cc:87
Class representing a list of search results.
Definition: mset.h:44
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
std::string get_value_upper_bound(Xapian::valueno slot) const
Get an upper bound on the values stored in the given value slot.
Definition: omdatabase.cc:386
test functionality of the Xapian API
KeyMaker subclass which combines several values.
Definition: keymaker.h:104
This class provides read/write access to a database.
Definition: database.h:785
static bool keys
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
#define TEST_EXCEPTION(TYPE, CODE)
Check that CODE throws exactly Xapian exception TYPE.
Definition: testutils.h:109
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
void set_query(const Xapian::Query &query, Xapian::termcount qlen=0)
Set the query to run.
Definition: omenquire.cc:793
#define FAIL_TEST(MSG)
Fail the current testcase with message MSG.
Definition: testsuite.h:68
Xapian::Database get_database(const string &dbname)
Definition: apitest.cc:48
This class provides an interface to the information retrieval system for the purpose of searching...
Definition: enquire.h:152
Xapian-specific test helper functions and macros.
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
DEFINE_TESTCASE(sortfunctor1, backend &&!remote)
Definition: api_sorting.cc:33
Class representing a query.
Definition: query.h:46
#define TEST_EQUAL(a, b)
Test for equality of two things.
Definition: testsuite.h:278
A handle representing a document in a Xapian database.
Definition: document.h:61
void set_sort_by_key(Xapian::KeyMaker *sorter, bool reverse)
Set the sorting to be by key generated from values only.
Definition: omenquire.cc:896
Virtual base class for key making functors.
Definition: keymaker.h:41
UnimplementedError indicates an attempt to use an unimplemented feature.
Definition: error.h:325
void add_term(const std::string &tname, Xapian::termcount wdfinc=1)
Add a term to the document, without positional information.
Definition: omdocument.cc:140