1 /** @file api_sorting.cc
2 * @brief tests of MSet sorting
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
24 #include "api_sorting.h"
29 #include "testutils.h"
33 DEFINE_TESTCASE(sortfunctor1
,backend
&& !remote
) {
34 Xapian::Enquire
enquire(get_database("apitest_sortrel"));
35 enquire
.set_query(Xapian::Query("woman"));
38 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
;
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.
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);
80 /// Test reverse sort functor.
81 DEFINE_TESTCASE(sortfunctor2
,writable
&& !remote
) {
82 Xapian::WritableDatabase db
= get_writable_database();
85 doc
.add_value(0, "ABB");
87 doc
.add_value(0, "ABC");
89 doc
.add_value(0, string("ABC", 4));
91 doc
.add_value(0, "ABCD");
93 doc
.add_value(0, "ABC\xff");
96 Xapian::Enquire
enquire(db
);
97 enquire
.set_query(Xapian::Query("foo"));
100 Xapian::MultiValueKeyMaker sorter
;
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
;
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);
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
;
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);
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);
215 class NeverUseMeKeyMaker
: public Xapian::KeyMaker
{
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.
252 sorter(Xapian::Document());
253 FAIL_TEST("NeverUseMeKeyMaker::operator() didn't throw TestFail");
254 } catch (const TestFail
&) {
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"));
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);
277 DEFINE_TESTCASE(multivaluekeymaker1
,!backend
) {
278 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
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",
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));
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);