xapian-core  1.4.25
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, backend && !remote) {
120  Xapian::Database db = get_database("sortfunctor2",
121  [](Xapian::WritableDatabase& wdb,
122  const string&) {
123  Xapian::Document doc;
124  doc.add_term("foo");
125  doc.add_value(0, "ABB");
126  wdb.add_document(doc);
127  doc.add_value(0, "ABC");
128  wdb.add_document(doc);
129  doc.add_value(0, string("ABC", 4));
130  wdb.add_document(doc);
131  doc.add_value(0, "ABCD");
132  wdb.add_document(doc);
133  doc.add_value(0, "ABC\xff");
134  wdb.add_document(doc);
135  });
136 
137  Xapian::Enquire enquire(db);
138  enquire.set_query(Xapian::Query("foo"));
139 
140  {
142  sorter.add_value(0);
143  enquire.set_sort_by_key(&sorter, true);
144  Xapian::MSet mset = enquire.get_mset(0, 10);
145  mset_expect_order(mset, 5, 4, 3, 2, 1);
146  }
147 
148  {
150  sorter.add_value(0, true);
151  enquire.set_sort_by_key(&sorter, true);
152  Xapian::MSet mset = enquire.get_mset(0, 10);
153  mset_expect_order(mset, 1, 2, 3, 4, 5);
154  }
155 
156  {
158  sorter.add_value(0);
159  sorter.add_value(1);
160  enquire.set_sort_by_key(&sorter, true);
161  Xapian::MSet mset = enquire.get_mset(0, 10);
162  mset_expect_order(mset, 5, 4, 3, 2, 1);
163  }
164 
165  {
167  sorter.add_value(0, true);
168  sorter.add_value(1);
169  enquire.set_sort_by_key(&sorter, true);
170  Xapian::MSet mset = enquire.get_mset(0, 10);
171  mset_expect_order(mset, 1, 2, 3, 4, 5);
172  }
173 
174  {
176  sorter.add_value(0);
177  sorter.add_value(1, true);
178  enquire.set_sort_by_key(&sorter, true);
179  Xapian::MSet mset = enquire.get_mset(0, 10);
180  mset_expect_order(mset, 5, 4, 3, 2, 1);
181  }
182 
183  {
185  sorter.add_value(0, true);
186  sorter.add_value(1, true);
187  enquire.set_sort_by_key(&sorter, true);
188  Xapian::MSet mset = enquire.get_mset(0, 10);
189  mset_expect_order(mset, 1, 2, 3, 4, 5);
190  }
191 }
192 
193 // Test sort functor with some empty values.
194 DEFINE_TESTCASE(sortfunctor3, backend && !remote && valuestats) {
195  Xapian::Database db(get_database("apitest_sortrel"));
196  Xapian::Enquire enquire(db);
197  enquire.set_query(Xapian::Query("woman"));
198 
199  // Value 10 is set to 'a' for 1, 3, 4, 5, 8, 9, and not set otherwise.
200  {
201  // Test default sort order - missing values come first.
203  sorter.add_value(10);
204 
205  enquire.set_sort_by_key(&sorter, false);
206  Xapian::MSet mset = enquire.get_mset(0, 10);
207  mset_expect_order(mset, 2, 6, 7, 1, 3, 4, 5, 8, 9);
208  }
209 
210  {
211  // Use a default value to put the missing values to the end.
213  sorter.add_value(10, false, db.get_value_upper_bound(10) + '\xff');
214 
215  enquire.set_sort_by_key(&sorter, false);
216  Xapian::MSet mset = enquire.get_mset(0, 10);
217  mset_expect_order(mset, 1, 3, 4, 5, 8, 9, 2, 6, 7);
218  }
219 
220  {
221  // Test using a default value and sorting in reverse order
223  sorter.add_value(10, false, db.get_value_upper_bound(10) + '\xff');
224 
225  enquire.set_sort_by_key(&sorter, true);
226  Xapian::MSet mset = enquire.get_mset(0, 10);
227  mset_expect_order(mset, 2, 6, 7, 1, 3, 4, 5, 8, 9);
228  }
229 
230  {
231  // Test using a default value and generating reverse order keys
233  sorter.add_value(10, true, db.get_value_upper_bound(10) + '\xff');
234 
235  enquire.set_sort_by_key(&sorter, false);
236  Xapian::MSet mset = enquire.get_mset(0, 10);
237  mset_expect_order(mset, 2, 6, 7, 1, 3, 4, 5, 8, 9);
238  }
239 
240  {
241  // Test using a default value, generating reverse order keys, and
242  // sorting in reverse order
244  sorter.add_value(10, true, db.get_value_upper_bound(10) + '\xff');
245 
246  enquire.set_sort_by_key(&sorter, true);
247  Xapian::MSet mset = enquire.get_mset(0, 10);
248  mset_expect_order(mset, 1, 3, 4, 5, 8, 9, 2, 6, 7);
249  }
250 }
251 
253  public:
254  std::string operator() (const Xapian::Document &) const
255  {
256  FAIL_TEST("NeverUseMeKeyMaker was called");
257  }
258 };
259 
261 DEFINE_TESTCASE(changesorter1, backend && !remote) {
262  Xapian::Enquire enquire(get_database("apitest_simpledata"));
263  enquire.set_query(Xapian::Query("word"));
264  NeverUseMeKeyMaker sorter;
265 
266  enquire.set_sort_by_key(&sorter, true);
267  enquire.set_sort_by_value(0, true);
268  Xapian::MSet mset = enquire.get_mset(0, 25);
269  TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
270 
271  enquire.set_sort_by_key(&sorter, true);
272  enquire.set_sort_by_value_then_relevance(0, true);
273  mset = enquire.get_mset(0, 25);
274  TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
275 
276  enquire.set_sort_by_key(&sorter, true);
277  enquire.set_sort_by_relevance_then_value(0, true);
278  mset = enquire.get_mset(0, 25);
279  TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
280 
281  enquire.set_sort_by_key(&sorter, true);
282  enquire.set_sort_by_relevance();
283  mset = enquire.get_mset(0, 25);
284  TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
285 
286  // Check that NeverUseMeKeyMaker::operator() would actually cause a test
287  // failure if called.
288  try {
289  sorter(Xapian::Document());
290  FAIL_TEST("NeverUseMeKeyMaker::operator() didn't throw TestFail");
291  } catch (const TestFail &) {
292  }
293 }
294 
296 DEFINE_TESTCASE(sortfunctorempty1, backend && !remote) {
297  Xapian::Enquire enquire(get_database("apitest_sortrel"));
298  enquire.set_query(Xapian::Query("woman"));
299 
300  {
301  int i;
302  Xapian::MultiValueKeyMaker sorter(&i, &i);
303 
304  enquire.set_sort_by_key(&sorter, true);
305  Xapian::MSet mset = enquire.get_mset(0, 10);
306  mset_expect_order(mset, 1, 2, 3, 4, 5, 6, 7, 8, 9);
307  }
308 }
309 
310 DEFINE_TESTCASE(multivaluekeymaker1, !backend) {
311  static const int keys[] = { 0, 1, 2, 3 };
312  Xapian::MultiValueKeyMaker sorter(keys, keys + 4);
313 
314  Xapian::Document doc;
315  TEST(sorter(doc).empty());
316 
317  doc.add_value(1, "foo");
318  TEST_EQUAL(sorter(doc), string("\0\0foo", 5));
319  doc.add_value(1, string("f\0o", 3));
320  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo", 6));
321  doc.add_value(3, "xyz");
322  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz", 13));
323 
324  // An empty slot at the end, in reverse order, is terminated with \xff\xff
325  sorter.add_value(4, true);
326  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff", 17));
327 
328  // An empty slot at the end, in ascending order, has no effect
329  sorter.add_value(0);
330  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff", 17));
331 
332  // An empty slot at the end, with a default value
333  sorter.add_value(0, false, "hi");
334  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff\0\0hi",
335  21));
336 
337  // An empty slot at the end, with a default value, in reverse sort order
338  sorter.add_value(0, true, "hi");
339  TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff\0\0hi"
340  "\0\0\x97\x96\xff\xff", 27));
341 }
342 
343 DEFINE_TESTCASE(sortfunctorremote1, remote) {
344  Xapian::Enquire enquire(get_database(string()));
345  NeverUseMeKeyMaker sorter;
346  enquire.set_query(Xapian::Query("word"));
347  enquire.set_sort_by_key(&sorter, true);
349  Xapian::MSet mset = enquire.get_mset(0, 10);
350  );
351 }
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
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:789
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:624
MSetIterator end() const
Return iterator pointing to just after the last item in this MSet.
Definition: mset.h:629
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