2 * @brief Query-related tests.
4 /* Copyright (C) 2008,2009,2012,2013,2015,2016,2017 Olly Betts
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 #include "api_query.h"
28 #include "testsuite.h"
29 #include "testutils.h"
35 /// Regression test - in 1.0.10 and earlier "" was included in the list.
36 DEFINE_TESTCASE(queryterms1
, !backend
) {
37 Xapian::Query query
= Xapian::Query::MatchAll
;
38 TEST(query
.get_terms_begin() == query
.get_terms_end());
39 query
= Xapian::Query(query
.OP_AND_NOT
, query
, Xapian::Query("fair"));
40 TEST_EQUAL(*query
.get_terms_begin(), "fair");
44 DEFINE_TESTCASE(matchall2
, !backend
) {
45 TEST_STRINGS_EQUAL(Xapian::Query::MatchAll
.get_description(),
46 "Query(<alldocuments>)");
50 DEFINE_TESTCASE(matchnothing1
, !backend
) {
51 TEST_STRINGS_EQUAL(Xapian::Query::MatchNothing
.get_description(),
53 vector
<Xapian::Query
> subqs
;
54 subqs
.push_back(Xapian::Query("foo"));
55 subqs
.push_back(Xapian::Query::MatchNothing
);
56 Xapian::Query
q(Xapian::Query::OP_AND
, subqs
.begin(), subqs
.end());
57 TEST_STRINGS_EQUAL(q
.get_description(), "Query()");
59 Xapian::Query
q2(Xapian::Query::OP_AND
,
60 Xapian::Query("foo"), Xapian::Query::MatchNothing
);
61 TEST_STRINGS_EQUAL(q2
.get_description(), "Query()");
65 DEFINE_TESTCASE(overload1
, !backend
) {
67 q
= Xapian::Query("foo") & Xapian::Query("bar");
68 TEST_STRINGS_EQUAL(q
.get_description(), "Query((foo AND bar))");
69 q
= Xapian::Query("foo") &~ Xapian::Query("bar");
70 TEST_STRINGS_EQUAL(q
.get_description(), "Query((foo AND_NOT bar))");
71 q
= ~Xapian::Query("bar");
72 TEST_STRINGS_EQUAL(q
.get_description(), "Query((<alldocuments> AND_NOT bar))");
73 q
= Xapian::Query("foo") & Xapian::Query::MatchNothing
;
74 TEST_STRINGS_EQUAL(q
.get_description(), "Query()");
75 q
= Xapian::Query("foo") | Xapian::Query("bar");
76 TEST_STRINGS_EQUAL(q
.get_description(), "Query((foo OR bar))");
77 q
= Xapian::Query("foo") | Xapian::Query::MatchNothing
;
78 TEST_STRINGS_EQUAL(q
.get_description(), "Query(foo)");
79 q
= Xapian::Query("foo") ^ Xapian::Query("bar");
80 TEST_STRINGS_EQUAL(q
.get_description(), "Query((foo XOR bar))");
81 q
= Xapian::Query("foo") ^ Xapian::Query::MatchNothing
;
82 TEST_STRINGS_EQUAL(q
.get_description(), "Query(foo)");
83 q
= 1.25 * (Xapian::Query("one") | Xapian::Query("two"));
84 TEST_STRINGS_EQUAL(q
.get_description(), "Query(1.25 * (one OR two))");
85 q
= (Xapian::Query("one") & Xapian::Query("two")) * 42;
86 TEST_STRINGS_EQUAL(q
.get_description(), "Query(42 * (one AND two))");
87 q
= Xapian::Query("one") / 2.0;
88 TEST_STRINGS_EQUAL(q
.get_description(), "Query(0.5 * one)");
92 /** Regression test and feature test.
94 * This threw AssertionError in 1.0.9 and earlier (bug#201) and gave valgrind
95 * errors in 1.0.11 and earlier (bug#349).
97 * Currently the OR-subquery case is supported, other operators aren't.
99 DEFINE_TESTCASE(possubqueries1
, writable
) {
100 Xapian::WritableDatabase db
= get_writable_database();
101 Xapian::Document doc
;
102 doc
.add_posting("a", 1);
103 doc
.add_posting("b", 2);
104 doc
.add_posting("c", 3);
105 db
.add_document(doc
);
107 Xapian::Query
a_or_b(Xapian::Query::OP_OR
,
110 Xapian::Query
near(Xapian::Query::OP_NEAR
, a_or_b
, a_or_b
);
111 // As of 1.3.0, we no longer rearrange queries at this point, so check
113 TEST_STRINGS_EQUAL(near
.get_description(),
114 "Query(((a OR b) NEAR 2 (a OR b)))");
115 Xapian::Query
phrase(Xapian::Query::OP_PHRASE
, a_or_b
, a_or_b
);
116 TEST_STRINGS_EQUAL(phrase
.get_description(),
117 "Query(((a OR b) PHRASE 2 (a OR b)))");
119 Xapian::Query
a_and_b(Xapian::Query::OP_AND
,
122 Xapian::Query
a_near_b(Xapian::Query::OP_NEAR
,
125 Xapian::Query
a_phrs_b(Xapian::Query::OP_PHRASE
,
128 Xapian::Query
c("c");
130 // FIXME: The plan is to actually try to support the cases below, but
131 // for now at least ensure they are cleanly rejected.
132 Xapian::Enquire
enq(db
);
134 TEST_EXCEPTION(Xapian::UnimplementedError
,
135 Xapian::Query
q(Xapian::Query::OP_NEAR
, a_and_b
, c
);
137 (void)enq
.get_mset(0, 10));
139 TEST_EXCEPTION(Xapian::UnimplementedError
,
140 Xapian::Query
q(Xapian::Query::OP_NEAR
, a_near_b
, c
);
142 (void)enq
.get_mset(0, 10));
144 TEST_EXCEPTION(Xapian::UnimplementedError
,
145 Xapian::Query
q(Xapian::Query::OP_NEAR
, a_phrs_b
, c
);
147 (void)enq
.get_mset(0, 10));
149 TEST_EXCEPTION(Xapian::UnimplementedError
,
150 Xapian::Query
q(Xapian::Query::OP_PHRASE
, a_and_b
, c
);
152 (void)enq
.get_mset(0, 10));
154 TEST_EXCEPTION(Xapian::UnimplementedError
,
155 Xapian::Query
q(Xapian::Query::OP_PHRASE
, a_near_b
, c
);
157 (void)enq
.get_mset(0, 10));
159 TEST_EXCEPTION(Xapian::UnimplementedError
,
160 Xapian::Query
q(Xapian::Query::OP_PHRASE
, a_phrs_b
, c
);
162 (void)enq
.get_mset(0, 10));
167 /// Test that XOR handles all remaining subqueries running out at the same
169 DEFINE_TESTCASE(xor3
, backend
) {
170 Xapian::Database db
= get_database("apitest_simpledata");
172 static const char * const subqs
[] = {
173 "hack", "which", "paragraph", "is", "return"
175 // Document where the subqueries run out *does* match XOR:
176 Xapian::Query
q(Xapian::Query::OP_XOR
, subqs
, subqs
+ 5);
177 Xapian::Enquire
enq(db
);
179 Xapian::MSet mset
= enq
.get_mset(0, 10);
181 TEST_EQUAL(mset
.size(), 3);
182 TEST_EQUAL(*mset
[0], 4);
183 TEST_EQUAL(*mset
[1], 2);
184 TEST_EQUAL(*mset
[2], 3);
186 // Document where the subqueries run out *does not* match XOR:
187 q
= Xapian::Query(Xapian::Query::OP_XOR
, subqs
, subqs
+ 4);
189 mset
= enq
.get_mset(0, 10);
191 TEST_EQUAL(mset
.size(), 4);
192 TEST_EQUAL(*mset
[0], 5);
193 TEST_EQUAL(*mset
[1], 4);
194 TEST_EQUAL(*mset
[2], 2);
195 TEST_EQUAL(*mset
[3], 3);
200 /// Check encoding of non-UTF8 terms in query descriptions.
201 DEFINE_TESTCASE(nonutf8termdesc1
, !backend
) {
202 TEST_EQUAL(Xapian::Query("\xc0\x80\xf5\x80\x80\x80\xfe\xff").get_description(),
203 "Query(\\xc0\\x80\\xf5\\x80\\x80\\x80\\xfe\\xff)");
204 TEST_EQUAL(Xapian::Query(string("\x00\x1f", 2)).get_description(),
205 "Query(\\x00\\x1f)");
206 // Check that backslashes are encoded so output isn't ambiguous.
207 TEST_EQUAL(Xapian::Query("back\\slash").get_description(),
208 "Query(back\\x5cslash)");
209 // Check that \x7f is escaped.
210 TEST_EQUAL(Xapian::Query("D\x7f_\x7f~").get_description(),
211 "Query(D\\x7f_\\x7f~)");
215 /// Test introspection on Query objects.
216 DEFINE_TESTCASE(queryintro1
, !backend
) {
217 TEST_EQUAL(Xapian::Query::MatchAll
.get_type(), Xapian::Query::LEAF_MATCH_ALL
);
218 TEST_EQUAL(Xapian::Query::MatchAll
.get_num_subqueries(), 0);
219 TEST_EQUAL(Xapian::Query::MatchNothing
.get_type(), Xapian::Query::LEAF_MATCH_NOTHING
);
220 TEST_EQUAL(Xapian::Query::MatchNothing
.get_num_subqueries(), 0);
223 q
= Xapian::Query(q
.OP_AND_NOT
, Xapian::Query::MatchAll
, Xapian::Query("fair"));
224 TEST_EQUAL(q
.get_type(), q
.OP_AND_NOT
);
225 TEST_EQUAL(q
.get_num_subqueries(), 2);
226 TEST_EQUAL(q
.get_subquery(0).get_type(), q
.LEAF_MATCH_ALL
);
227 TEST_EQUAL(q
.get_subquery(1).get_type(), q
.LEAF_TERM
);
229 q
= Xapian::Query("foo", 2, 1);
230 TEST_EQUAL(q
.get_leaf_wqf(), 2);
231 TEST_EQUAL(q
.get_leaf_pos(), 1);
233 q
= Xapian::Query("bar");
234 TEST_EQUAL(q
.get_leaf_wqf(), 1);
235 TEST_EQUAL(q
.get_leaf_pos(), 0);
237 q
= Xapian::Query("foo") & Xapian::Query("bar");
238 TEST_EQUAL(q
.get_type(), q
.OP_AND
);
240 q
= Xapian::Query("foo") &~ Xapian::Query("bar");
241 TEST_EQUAL(q
.get_type(), q
.OP_AND_NOT
);
243 q
= ~Xapian::Query("bar");
244 TEST_EQUAL(q
.get_type(), q
.OP_AND_NOT
);
246 q
= Xapian::Query("foo") | Xapian::Query("bar");
247 TEST_EQUAL(q
.get_type(), q
.OP_OR
);
249 q
= Xapian::Query("foo") ^ Xapian::Query("bar");
250 TEST_EQUAL(q
.get_type(), q
.OP_XOR
);
252 q
= 1.25 * (Xapian::Query("one") | Xapian::Query("two"));
253 TEST_EQUAL(q
.get_type(), q
.OP_SCALE_WEIGHT
);
254 TEST_EQUAL(q
.get_num_subqueries(), 1);
255 TEST_EQUAL(q
.get_subquery(0).get_type(), q
.OP_OR
);
257 q
= Xapian::Query("one") / 2.0;
258 TEST_EQUAL(q
.get_type(), q
.OP_SCALE_WEIGHT
);
259 TEST_EQUAL(q
.get_num_subqueries(), 1);
260 TEST_EQUAL(q
.get_subquery(0).get_type(), q
.LEAF_TERM
);
262 q
= Xapian::Query(q
.OP_NEAR
, Xapian::Query("a"), Xapian::Query("b"));
263 TEST_EQUAL(q
.get_type(), q
.OP_NEAR
);
264 TEST_EQUAL(q
.get_num_subqueries(), 2);
265 TEST_EQUAL(q
.get_subquery(0).get_type(), q
.LEAF_TERM
);
266 TEST_EQUAL(q
.get_subquery(1).get_type(), q
.LEAF_TERM
);
268 q
= Xapian::Query(q
.OP_PHRASE
, Xapian::Query("c"), Xapian::Query("d"));
269 TEST_EQUAL(q
.get_type(), q
.OP_PHRASE
);
270 TEST_EQUAL(q
.get_num_subqueries(), 2);
271 TEST_EQUAL(q
.get_subquery(0).get_type(), q
.LEAF_TERM
);
272 TEST_EQUAL(q
.get_subquery(1).get_type(), q
.LEAF_TERM
);
277 /// Regression test for bug introduced in 1.3.1 and fixed in 1.3.3.
278 // We were incorrectly converting a term which indexed all docs and was used
279 // in an unweighted phrase into an all docs postlist, so check that this
280 // case actually works.
281 DEFINE_TESTCASE(phrasealldocs1
, backend
) {
282 Xapian::Database db
= get_database("apitest_declen");
284 static const char * const phrase
[] = { "this", "is", "the" };
285 q
= Xapian::Query(q
.OP_AND_NOT
,
286 Xapian::Query("paragraph"),
287 Xapian::Query(q
.OP_PHRASE
, phrase
, phrase
+ 3));
288 Xapian::Enquire
enq(db
);
290 Xapian::MSet mset
= enq
.get_mset(0, 10);
291 TEST_EQUAL(mset
.size(), 3);
296 struct wildcard_testcase
{
297 const char * pattern
;
298 Xapian::termcount max_expansion
;
300 const char * terms
[4];
303 #define WILDCARD_EXCEPTION { 0, 0, 0, "" }
305 wildcard_testcase wildcard1_testcases
[] = {
306 // Tries to expand to 7 terms.
307 { "th", 6, 'E', WILDCARD_EXCEPTION
},
308 { "thou", 1, 'E', { "though", 0, 0, 0 } },
309 { "s", 2, 'F', { "say", "search", 0, 0 } },
310 { "s", 2, 'M', { "simpl", "so", 0, 0 } },
311 { 0, 0, 0, { 0, 0, 0, 0 } }
314 DEFINE_TESTCASE(wildcard1
, backend
) {
315 // FIXME: The counting of terms the wildcard expands to is per subdatabase,
316 // so the wildcard may expand to more terms than the limit if some aren't
317 // in all subdatabases. Also WILDCARD_LIMIT_MOST_FREQUENT uses the
318 // frequency from the subdatabase, and so may select different terms in
320 SKIP_TEST_FOR_BACKEND("multi");
321 Xapian::Database db
= get_database("apitest_simpledata");
322 Xapian::Enquire
enq(db
);
323 const Xapian::Query::op o
= Xapian::Query::OP_WILDCARD
;
325 const wildcard_testcase
* p
= wildcard1_testcases
;
327 tout
<< p
->pattern
<< endl
;
328 const char * const * tend
= p
->terms
+ 4;
329 while (tend
[-1] == NULL
) --tend
;
330 bool expect_exception
= (tend
- p
->terms
== 4 && tend
[-1][0] == '\0');
334 switch (p
->max_type
) {
336 max_type
= Xapian::Query::WILDCARD_LIMIT_ERROR
;
339 max_type
= Xapian::Query::WILDCARD_LIMIT_FIRST
;
342 max_type
= Xapian::Query::WILDCARD_LIMIT_MOST_FREQUENT
;
347 q
= Xapian::Query(o
, p
->pattern
, p
->max_expansion
, max_type
);
349 q
= Xapian::Query(o
, p
->pattern
, p
->max_expansion
);
353 Xapian::MSet mset
= enq
.get_mset(0, 10);
354 TEST(!expect_exception
);
355 q
= Xapian::Query(q
.OP_SYNONYM
, p
->terms
, tend
);
357 Xapian::MSet mset2
= enq
.get_mset(0, 10);
358 TEST_EQUAL(mset
.size(), mset2
.size());
359 TEST(mset_range_is_same(mset
, 0, mset2
, 0, mset
.size()));
360 } catch (const Xapian::WildcardError
&) {
361 TEST(expect_exception
);
369 /// Regression test for #696, fixed in 1.3.4.
370 DEFINE_TESTCASE(wildcard2
, backend
) {
371 // FIXME: The counting of terms the wildcard expands to is per subdatabase,
372 // so the wildcard may expand to more terms than the limit if some aren't
373 // in all subdatabases. Also WILDCARD_LIMIT_MOST_FREQUENT uses the
374 // frequency from the subdatabase, and so may select different terms in
376 SKIP_TEST_FOR_BACKEND("multi");
377 Xapian::Database db
= get_database("apitest_simpledata");
378 Xapian::Enquire
enq(db
);
379 const Xapian::Query::op o
= Xapian::Query::OP_WILDCARD
;
381 const int max_type
= Xapian::Query::WILDCARD_LIMIT_MOST_FREQUENT
;
382 Xapian::Query
q0(o
, "w", 2, max_type
);
383 Xapian::Query
q(o
, "s", 2, max_type
);
384 Xapian::Query
q2(o
, "t", 2, max_type
);
385 q
= Xapian::Query(q
.OP_OR
, q0
, q
);
386 q
= Xapian::Query(q
.OP_OR
, q
, q2
);
388 Xapian::MSet mset
= enq
.get_mset(0, 10);
389 TEST_EQUAL(mset
.size(), 6);
394 DEFINE_TESTCASE(dualprefixwildcard1
, backend
) {
395 Xapian::Database db
= get_database("apitest_simpledata");
396 Xapian::Query
q(Xapian::Query::OP_SYNONYM
,
397 Xapian::Query(Xapian::Query::OP_WILDCARD
, "fo"),
398 Xapian::Query(Xapian::Query::OP_WILDCARD
, "Sfo"));
399 tout
<< q
.get_description() << endl
;
400 Xapian::Enquire
enq(db
);
402 TEST_EQUAL(enq
.get_mset(0, 5).size(), 2);
406 struct positional_testcase
{
408 const char * terms
[4];
409 Xapian::docid result
;
413 positional_testcase loosephrase1_testcases
[] = {
414 { 5, { "expect", "to", "mset", 0 }, 0 },
415 { 5, { "word", "well", "the", 0 }, 2 },
416 { 5, { "if", "word", "doesnt", 0 }, 0 },
417 { 5, { "at", "line", "three", 0 }, 0 },
418 { 5, { "paragraph", "other", "the", 0 }, 0 },
419 { 5, { "other", "the", "with", 0 }, 0 },
420 { 0, { 0, 0, 0, 0 }, 0 }
423 /// Regression test for bug fixed in 1.3.3 and 1.2.21.
424 DEFINE_TESTCASE(loosephrase1
, backend
) {
425 Xapian::Database db
= get_database("apitest_simpledata");
426 Xapian::Enquire
enq(db
);
428 const positional_testcase
* p
= loosephrase1_testcases
;
430 const char * const * tend
= p
->terms
+ 4;
431 while (tend
[-1] == NULL
) --tend
;
432 Xapian::Query
q(Xapian::Query::OP_PHRASE
, p
->terms
, tend
, p
->window
);
434 Xapian::MSet mset
= enq
.get_mset(0, 10);
435 if (p
->result
== 0) {
438 TEST_EQUAL(mset
.size(), 1);
439 TEST_EQUAL(*mset
[0], p
->result
);
448 positional_testcase loosenear1_testcases
[] = {
449 { 4, { "test", "the", "with", 0 }, 1 },
450 { 4, { "expect", "word", "the", 0 }, 2 },
451 { 4, { "line", "be", "blank", 0 }, 1 },
452 { 2, { "banana", "banana", 0, 0 }, 0 },
453 { 3, { "banana", "banana", 0, 0 }, 0 },
454 { 2, { "word", "word", 0, 0 }, 2 },
455 { 4, { "work", "meant", "work", 0 }, 0 },
456 { 4, { "this", "one", "yet", "one" }, 0 },
457 { 0, { 0, 0, 0, 0 }, 0 }
460 /// Regression tests for bugs fixed in 1.3.3 and 1.2.21.
461 DEFINE_TESTCASE(loosenear1
, backend
) {
462 Xapian::Database db
= get_database("apitest_simpledata");
463 Xapian::Enquire
enq(db
);
465 const positional_testcase
* p
= loosenear1_testcases
;
467 const char * const * tend
= p
->terms
+ 4;
468 while (tend
[-1] == NULL
) --tend
;
469 Xapian::Query
q(Xapian::Query::OP_NEAR
, p
->terms
, tend
, p
->window
);
471 Xapian::MSet mset
= enq
.get_mset(0, 10);
472 if (p
->result
== 0) {
475 TEST_EQUAL(mset
.size(), 1);
476 TEST_EQUAL(*mset
[0], p
->result
);
484 /// Regression test for bug fixed in 1.3.6 - the first case segfaulted in 1.3.x.
485 DEFINE_TESTCASE(complexphrase1
, backend
) {
486 Xapian::Database db
= get_database("apitest_simpledata");
487 Xapian::Enquire
enq(db
);
488 Xapian::Query
query(Xapian::Query::OP_PHRASE
,
489 Xapian::Query("a") | Xapian::Query("b"),
491 enq
.set_query(query
);
492 TEST(enq
.get_mset(0, 10).empty());
493 Xapian::Query
query2(Xapian::Query::OP_PHRASE
,
494 Xapian::Query("a") | Xapian::Query("b"),
496 enq
.set_query(query2
);
497 TEST(enq
.get_mset(0, 10).empty());
501 /// Regression test for bug fixed in 1.3.6 - the first case segfaulted in 1.3.x.
502 DEFINE_TESTCASE(complexnear1
, backend
) {
503 Xapian::Database db
= get_database("apitest_simpledata");
504 Xapian::Enquire
enq(db
);
505 Xapian::Query
query(Xapian::Query::OP_NEAR
,
506 Xapian::Query("a") | Xapian::Query("b"),
508 enq
.set_query(query
);
509 TEST(enq
.get_mset(0, 10).empty());
510 Xapian::Query
query2(Xapian::Query::OP_NEAR
,
511 Xapian::Query("a") | Xapian::Query("b"),
513 enq
.set_query(query2
);
514 TEST(enq
.get_mset(0, 10).empty());
518 /// Check subqueries of MatchAll, MatchNothing and PostingSource are supported.
519 DEFINE_TESTCASE(complexphrase2
, backend
) {
520 Xapian::Database db
= get_database("apitest_simpledata");
521 Xapian::Enquire
enq(db
);
522 Xapian::ValueWeightPostingSource
ps(0);
523 Xapian::Query subqs
[3] = {
524 Xapian::Query(Xapian::Query::OP_PHRASE
,
527 Xapian::Query(Xapian::Query::OP_PHRASE
,
528 Xapian::Query("and"),
529 Xapian::Query::MatchAll
),
530 Xapian::Query(Xapian::Query::OP_PHRASE
,
532 Xapian::Query::MatchNothing
)
534 Xapian::Query
query(Xapian::Query::OP_OR
, subqs
, subqs
+ 3);
535 enq
.set_query(query
);
536 (void)enq
.get_mset(0, 10);
540 /// Check subqueries of MatchAll, MatchNothing and PostingSource are supported.
541 DEFINE_TESTCASE(complexnear2
, backend
) {
542 Xapian::Database db
= get_database("apitest_simpledata");
543 Xapian::Enquire
enq(db
);
544 Xapian::ValueWeightPostingSource
ps(0);
545 Xapian::Query subqs
[3] = {
546 Xapian::Query(Xapian::Query::OP_NEAR
,
549 Xapian::Query(Xapian::Query::OP_NEAR
,
550 Xapian::Query("and"),
551 Xapian::Query::MatchAll
),
552 Xapian::Query(Xapian::Query::OP_NEAR
,
554 Xapian::Query::MatchNothing
)
556 Xapian::Query
query(Xapian::Query::OP_OR
, subqs
, subqs
+ 3);
557 enq
.set_query(query
);
558 (void)enq
.get_mset(0, 10);
562 /// A zero estimated number of matches broke the code to round the estimate.
563 DEFINE_TESTCASE(zeroestimate1
, backend
) {
564 Xapian::Enquire
enquire(get_database("apitest_simpledata"));
565 Xapian::Query
phrase(Xapian::Query::OP_PHRASE
,
566 Xapian::Query("absolute"),
567 Xapian::Query("rubbish"));
568 enquire
.set_query(phrase
&~ Xapian::Query("queri"));
569 Xapian::MSet mset
= enquire
.get_mset(0, 0);
570 TEST_EQUAL(mset
.get_matches_estimated(), 0);
574 /// Feature test for OR under OP_PHRASE support added in 1.4.3.
575 DEFINE_TESTCASE(complexphrase3
, backend
) {
576 Xapian::Database db
= get_database("apitest_simpledata");
577 Xapian::Enquire
enq(db
);
578 Xapian::Query
query(Xapian::Query::OP_PHRASE
,
579 Xapian::Query("is") | Xapian::Query("as") | Xapian::Query("be"),
581 enq
.set_query(query
);
582 mset_expect_order(enq
.get_mset(0, 10), 1);
583 Xapian::Query
query2(Xapian::Query::OP_PHRASE
,
585 Xapian::Query("is") | Xapian::Query("as") | Xapian::Query("be"));
586 enq
.set_query(query2
);
587 mset_expect_order(enq
.get_mset(0, 10));
588 Xapian::Query
query3(Xapian::Query::OP_PHRASE
,
589 Xapian::Query("one") | Xapian::Query("with"),
590 Xapian::Query("the") | Xapian::Query("of") | Xapian::Query("line"));
591 enq
.set_query(query3
);
592 mset_expect_order(enq
.get_mset(0, 10), 1, 4, 5);
593 Xapian::Query
query4(Xapian::Query::OP_PHRASE
,
594 Xapian::Query("the") | Xapian::Query("of") | Xapian::Query("line"),
595 Xapian::Query("one") | Xapian::Query("with"));
596 enq
.set_query(query4
);
597 mset_expect_order(enq
.get_mset(0, 10));
601 /// Feature test for OR under OP_NEAR support added in 1.4.3.
602 DEFINE_TESTCASE(complexnear3
, backend
) {
603 Xapian::Database db
= get_database("apitest_simpledata");
604 Xapian::Enquire
enq(db
);
605 Xapian::Query
query(Xapian::Query::OP_NEAR
,
606 Xapian::Query("is") | Xapian::Query("as") | Xapian::Query("be"),
608 enq
.set_query(query
);
609 mset_expect_order(enq
.get_mset(0, 10), 1);
610 Xapian::Query
query2(Xapian::Query::OP_NEAR
,
612 Xapian::Query("is") | Xapian::Query("as") | Xapian::Query("be"));
613 enq
.set_query(query2
);
614 mset_expect_order(enq
.get_mset(0, 10), 1);
615 Xapian::Query
query3(Xapian::Query::OP_NEAR
,
616 Xapian::Query("one") | Xapian::Query("with"),
617 Xapian::Query("the") | Xapian::Query("of") | Xapian::Query("line"));
618 enq
.set_query(query3
);
619 mset_expect_order(enq
.get_mset(0, 10), 1, 4, 5);
620 Xapian::Query
query4(Xapian::Query::OP_NEAR
,
621 Xapian::Query("the") | Xapian::Query("of") | Xapian::Query("line"),
622 Xapian::Query("one") | Xapian::Query("with"));
623 enq
.set_query(query4
);
624 mset_expect_order(enq
.get_mset(0, 10), 1, 4, 5);
629 gen_subdbwithoutpos1_db(Xapian::WritableDatabase
& db
, const string
&)
631 Xapian::Document doc
;
632 doc
.add_term("this");
633 doc
.add_term("paragraph");
634 doc
.add_term("wibble", 5);
635 db
.add_document(doc
);
638 DEFINE_TESTCASE(subdbwithoutpos1
, generated
) {
639 Xapian::Database
db(get_database("apitest_simpledata"));
641 Xapian::Query
q(Xapian::Query::OP_PHRASE
,
642 Xapian::Query("this"),
643 Xapian::Query("paragraph"));
645 Xapian::Enquire
enq1(db
);
647 Xapian::MSet mset1
= enq1
.get_mset(0, 10);
648 TEST_EQUAL(mset1
.size(), 3);
650 Xapian::Database db2
=
651 get_database("subdbwithoutpos1", gen_subdbwithoutpos1_db
);
653 // If a database has no positional info, OP_PHRASE -> OP_AND.
654 Xapian::Enquire
enq2(db2
);
656 Xapian::MSet mset2
= enq2
.get_mset(0, 10);
657 TEST_EQUAL(mset2
.size(), 1);
659 // If one sub-database in a combined database has no positional info but
660 // other sub-databases do, then we shouldn't convert OP_PHRASE to OP_AND
661 // (but prior to 1.4.3 we did).
662 db
.add_database(db2
);
663 Xapian::Enquire
enq3(db
);
665 Xapian::MSet mset3
= enq3
.get_mset(0, 10);
666 TEST_EQUAL(mset3
.size(), 3);
667 // Regression test for bug introduced in 1.4.3 which led to a division by
668 // zero and then (at least on Linux) we got 1% here.
669 TEST_EQUAL(mset3
[0].get_percent(), 100);
671 // Regression test for https://trac.xapian.org/ticket/752
672 enq3
.set_query((Xapian::Query("this") & q
) | Xapian::Query("wibble"));
673 mset3
= enq3
.get_mset(0, 10);
674 TEST_EQUAL(mset3
.size(), 4);
679 // Regression test for bug fixed in 1.4.4 and 1.2.25.
680 DEFINE_TESTCASE(notandor1
, backend
) {
681 Xapian::Database
db(get_database("etext"));
683 Xapian::Query("the") &~ (Xapian::Query("friedrich") &
684 (Xapian::Query("day") | Xapian::Query("night")));
685 Xapian::Enquire
enq(db
);
688 Xapian::MSet mset
= enq
.get_mset(0, 10, db
.get_doccount());
689 TEST_EQUAL(mset
.get_matches_estimated(), 344);