Make all read-only data arrays static and const
[xapian.git] / xapian-core / tests / api_sorting.cc
blob408d663a61dff96d00ce595ed63b2fb981333052
1 /** @file api_sorting.cc
2 * @brief tests of MSet sorting
3 */
4 /* Copyright (C) 2007,2008,2009,2012 Olly Betts
5 * Copyright (C) 2010 Richard Boulton
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.
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.
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
22 #include <config.h>
24 #include "api_sorting.h"
26 #include <xapian.h>
28 #include "apitest.h"
29 #include "testutils.h"
31 using namespace std;
33 DEFINE_TESTCASE(sortfunctor1, backend && !remote) {
34 Xapian::Enquire enquire(get_database("apitest_sortrel"));
35 enquire.set_query(Xapian::Query("woman"));
38 static const int keys[] = { 3, 1 };
39 Xapian::MultiValueKeyMaker sorter(keys, keys + 2);
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);
47 Xapian::MultiValueKeyMaker sorter;
48 sorter.add_value(3);
49 sorter.add_value(1, true);
51 enquire.set_sort_by_key(&sorter, true);
52 Xapian::MSet mset = enquire.get_mset(0, 10);
53 mset_expect_order(mset, 7, 6, 2, 8, 9, 4, 5, 1, 3);
57 Xapian::MultiValueKeyMaker sorter;
58 sorter.add_value(100); // Value 100 isn't set.
59 sorter.add_value(3);
60 sorter.add_value(1, true);
62 enquire.set_sort_by_key(&sorter, true);
63 Xapian::MSet mset = enquire.get_mset(0, 10);
64 mset_expect_order(mset, 7, 6, 2, 8, 9, 4, 5, 1, 3);
68 Xapian::MultiValueKeyMaker sorter;
69 sorter.add_value(10); // Value 10 isn't always set.
70 sorter.add_value(1, true);
72 enquire.set_sort_by_key(&sorter, true);
73 Xapian::MSet mset = enquire.get_mset(0, 10);
74 mset_expect_order(mset, 8, 9, 4, 5, 1, 3, 7, 6, 2);
77 return true;
80 /// Test reverse sort functor.
81 DEFINE_TESTCASE(sortfunctor2, writable && !remote) {
82 Xapian::WritableDatabase db = get_writable_database();
83 Xapian::Document doc;
84 doc.add_term("foo");
85 doc.add_value(0, "ABB");
86 db.add_document(doc);
87 doc.add_value(0, "ABC");
88 db.add_document(doc);
89 doc.add_value(0, string("ABC", 4));
90 db.add_document(doc);
91 doc.add_value(0, "ABCD");
92 db.add_document(doc);
93 doc.add_value(0, "ABC\xff");
94 db.add_document(doc);
96 Xapian::Enquire enquire(db);
97 enquire.set_query(Xapian::Query("foo"));
100 Xapian::MultiValueKeyMaker sorter;
101 sorter.add_value(0);
102 enquire.set_sort_by_key(&sorter, true);
103 Xapian::MSet mset = enquire.get_mset(0, 10);
104 mset_expect_order(mset, 5, 4, 3, 2, 1);
108 Xapian::MultiValueKeyMaker sorter;
109 sorter.add_value(0, true);
110 enquire.set_sort_by_key(&sorter, true);
111 Xapian::MSet mset = enquire.get_mset(0, 10);
112 mset_expect_order(mset, 1, 2, 3, 4, 5);
116 Xapian::MultiValueKeyMaker sorter;
117 sorter.add_value(0);
118 sorter.add_value(1);
119 enquire.set_sort_by_key(&sorter, true);
120 Xapian::MSet mset = enquire.get_mset(0, 10);
121 mset_expect_order(mset, 5, 4, 3, 2, 1);
125 Xapian::MultiValueKeyMaker sorter;
126 sorter.add_value(0, true);
127 sorter.add_value(1);
128 enquire.set_sort_by_key(&sorter, true);
129 Xapian::MSet mset = enquire.get_mset(0, 10);
130 mset_expect_order(mset, 1, 2, 3, 4, 5);
134 Xapian::MultiValueKeyMaker sorter;
135 sorter.add_value(0);
136 sorter.add_value(1, true);
137 enquire.set_sort_by_key(&sorter, true);
138 Xapian::MSet mset = enquire.get_mset(0, 10);
139 mset_expect_order(mset, 5, 4, 3, 2, 1);
143 Xapian::MultiValueKeyMaker sorter;
144 sorter.add_value(0, true);
145 sorter.add_value(1, true);
146 enquire.set_sort_by_key(&sorter, true);
147 Xapian::MSet mset = enquire.get_mset(0, 10);
148 mset_expect_order(mset, 1, 2, 3, 4, 5);
151 return true;
154 // Test sort functor with some empty values.
155 DEFINE_TESTCASE(sortfunctor3, backend && !remote && valuestats) {
156 Xapian::Database db(get_database("apitest_sortrel"));
157 Xapian::Enquire enquire(db);
158 enquire.set_query(Xapian::Query("woman"));
160 // Value 10 is set to 'a' for 1, 3, 4, 5, 8, 9, and not set otherwise.
162 // Test default sort order - missing values come first.
163 Xapian::MultiValueKeyMaker sorter;
164 sorter.add_value(10);
166 enquire.set_sort_by_key(&sorter, false);
167 Xapian::MSet mset = enquire.get_mset(0, 10);
168 mset_expect_order(mset, 2, 6, 7, 1, 3, 4, 5, 8, 9);
172 // Use a default value to put the missing values to the end.
173 Xapian::MultiValueKeyMaker sorter;
174 sorter.add_value(10, false, db.get_value_upper_bound(10) + '\xff');
176 enquire.set_sort_by_key(&sorter, false);
177 Xapian::MSet mset = enquire.get_mset(0, 10);
178 mset_expect_order(mset, 1, 3, 4, 5, 8, 9, 2, 6, 7);
182 // Test using a default value and sorting in reverse order
183 Xapian::MultiValueKeyMaker sorter;
184 sorter.add_value(10, false, db.get_value_upper_bound(10) + '\xff');
186 enquire.set_sort_by_key(&sorter, true);
187 Xapian::MSet mset = enquire.get_mset(0, 10);
188 mset_expect_order(mset, 2, 6, 7, 1, 3, 4, 5, 8, 9);
192 // Test using a default value and generating reverse order keys
193 Xapian::MultiValueKeyMaker sorter;
194 sorter.add_value(10, true, db.get_value_upper_bound(10) + '\xff');
196 enquire.set_sort_by_key(&sorter, false);
197 Xapian::MSet mset = enquire.get_mset(0, 10);
198 mset_expect_order(mset, 2, 6, 7, 1, 3, 4, 5, 8, 9);
202 // Test using a default value, generating reverse order keys, and
203 // sorting in reverse order
204 Xapian::MultiValueKeyMaker sorter;
205 sorter.add_value(10, true, db.get_value_upper_bound(10) + '\xff');
207 enquire.set_sort_by_key(&sorter, true);
208 Xapian::MSet mset = enquire.get_mset(0, 10);
209 mset_expect_order(mset, 1, 3, 4, 5, 8, 9, 2, 6, 7);
212 return true;
215 class NeverUseMeKeyMaker : public Xapian::KeyMaker {
216 public:
217 std::string operator() (const Xapian::Document &) const
219 FAIL_TEST("NeverUseMeKeyMaker was called");
223 /// Regression test for changing away from a sorter.
224 DEFINE_TESTCASE(changesorter1, backend && !remote) {
225 Xapian::Enquire enquire(get_database("apitest_simpledata"));
226 enquire.set_query(Xapian::Query("word"));
227 NeverUseMeKeyMaker sorter;
229 enquire.set_sort_by_key(&sorter, true);
230 enquire.set_sort_by_value(0, true);
231 Xapian::MSet mset = enquire.get_mset(0, 25);
232 TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
234 enquire.set_sort_by_key(&sorter, true);
235 enquire.set_sort_by_value_then_relevance(0, true);
236 mset = enquire.get_mset(0, 25);
237 TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
239 enquire.set_sort_by_key(&sorter, true);
240 enquire.set_sort_by_relevance_then_value(0, true);
241 mset = enquire.get_mset(0, 25);
242 TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
244 enquire.set_sort_by_key(&sorter, true);
245 enquire.set_sort_by_relevance();
246 mset = enquire.get_mset(0, 25);
247 TEST_EQUAL(mset.size(), 2); // Check that search is still doing something.
249 // Check that NeverUseMeKeyMaker::operator() would actually cause a test
250 // failure if called.
251 try {
252 sorter(Xapian::Document());
253 FAIL_TEST("NeverUseMeKeyMaker::operator() didn't throw TestFail");
254 } catch (const TestFail &) {
257 return true;
260 /// Regression test - an empty MultiValueSorter hung in 1.0.9 and earlier.
261 DEFINE_TESTCASE(sortfunctorempty1, backend && !remote) {
262 Xapian::Enquire enquire(get_database("apitest_sortrel"));
263 enquire.set_query(Xapian::Query("woman"));
266 int i;
267 Xapian::MultiValueKeyMaker sorter(&i, &i);
269 enquire.set_sort_by_key(&sorter, true);
270 Xapian::MSet mset = enquire.get_mset(0, 10);
271 mset_expect_order(mset, 1, 2, 3, 4, 5, 6, 7, 8, 9);
274 return true;
277 DEFINE_TESTCASE(multivaluekeymaker1, !backend) {
278 static const int keys[] = { 0, 1, 2, 3 };
279 Xapian::MultiValueKeyMaker sorter(keys, keys + 4);
281 Xapian::Document doc;
282 TEST(sorter(doc).empty());
284 doc.add_value(1, "foo");
285 TEST_EQUAL(sorter(doc), string("\0\0foo", 5));
286 doc.add_value(1, string("f\0o", 3));
287 TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo", 6));
288 doc.add_value(3, "xyz");
289 TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz", 13));
291 // An empty slot at the end, in reverse order, is terminated with \xff\xff
292 sorter.add_value(4, true);
293 TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff", 17));
295 // An empty slot at the end, in ascending order, has no effect
296 sorter.add_value(0);
297 TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff", 17));
299 // An empty slot at the end, with a default value
300 sorter.add_value(0, false, "hi");
301 TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff\0\0hi",
302 21));
304 // An empty slot at the end, with a default value, in reverse sort order
305 sorter.add_value(0, true, "hi");
306 TEST_EQUAL(sorter(doc), string("\0\0f\0\xffo\0\0\0\0xyz\0\0\xff\xff\0\0hi"
307 "\0\0\x97\x96\xff\xff", 27));
309 return true;
312 DEFINE_TESTCASE(sortfunctorremote1, remote) {
313 Xapian::Enquire enquire(get_database(string()));
314 NeverUseMeKeyMaker sorter;
315 enquire.set_query(Xapian::Query("word"));
316 enquire.set_sort_by_key(&sorter, true);
317 TEST_EXCEPTION(Xapian::UnimplementedError,
318 Xapian::MSet mset = enquire.get_mset(0, 10);
320 return true;
323 DEFINE_TESTCASE(replace_weights1, backend) {
324 Xapian::Database mydb(get_database("apitest_onedoc"));
325 Xapian::Enquire enquire(mydb);
326 enquire.set_query(Xapian::Query("word"));
327 Xapian::MSet mymset = enquire.get_mset(0, 10);
328 // old_max_possible, max_attained = 0.269763689697702
329 double old_max_possible = mymset.get_max_possible();
330 const double new_weight = 0.125;
331 static const double weights[] = {new_weight};
332 mymset.replace_weights(begin(weights), end(weights));
333 Xapian::MSetIterator i = mymset.begin();
334 TEST(i != mymset.end());
335 TEST_EQUAL_DOUBLE(i.get_weight(), new_weight);
336 TEST_EQUAL_DOUBLE(mymset.get_max_attained(), new_weight);
337 TEST_EQUAL_DOUBLE(mymset.get_max_possible(), old_max_possible);
338 return true;
341 DEFINE_TESTCASE(replace_weights2, backend) {
342 Xapian::Database mydb(get_database("apitest_onedoc"));
343 Xapian::Enquire enquire(mydb);
344 enquire.set_query(Xapian::Query("word"));
345 Xapian::MSet mymset = enquire.get_mset(0, 10);
346 static const double weights[] = {1.0, 2.0};
347 TEST_EXCEPTION(Xapian::InvalidArgumentError,
348 mymset.replace_weights(begin(weights), end(weights)));
349 return true;
352 DEFINE_TESTCASE(sort_existing_mset_by_relevance, backend) {
353 Xapian::Database db = get_database("apitest_simpledata");
354 Xapian::Enquire enquire(db);
355 enquire.set_query(Xapian::Query("word"));
356 Xapian::MSet mymset = enquire.get_mset(0, 10);
357 // old max_possible = 1.17367567757238, max_attained = 1.04648168717725
358 static const Xapian::docid docids[] = {*mymset[0], *mymset[1]};
359 static const double weights[] = {1.18, 1.19};
360 mymset.replace_weights(begin(weights), end(weights));
361 mymset.sort_by_relevance();
362 // The order of documents should have been reversed.
363 int k = 1;
364 for (Xapian::MSetIterator m = mymset.begin(); m != mymset.end();
365 ++m, --k) {
366 TEST_EQUAL(*m, docids[k]);
367 TEST_EQUAL_DOUBLE(m.get_weight(), weights[k]);
369 TEST_EQUAL_DOUBLE(mymset.get_max_attained(), weights[1]);
370 TEST_EQUAL_DOUBLE(mymset.get_max_possible(), weights[1]);
371 return true;