Branch data Line data Source code
1 : : /** @file api_opvalue.cc
2 : : * @brief Tests of the OP_VALUE_* query operators.
3 : : */
4 : : /* Copyright 2007,2008,2009,2010,2010 Olly Betts
5 : : * Copyright 2008 Lemur Consulting Ltd
6 : : * Copyright 2010 Richard Boulton
7 : : *
8 : : * This program is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU General Public License as
10 : : * published by the Free Software Foundation; either version 2 of the
11 : : * License, or (at your option) any later version.
12 : : *
13 : : * This program is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : : * GNU General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU General Public License
19 : : * along with this program; if not, write to the Free Software
20 : : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 : : * USA
22 : : */
23 : :
24 : : #include <config.h>
25 : :
26 : : #include "api_opvalue.h"
27 : :
28 : : #include <xapian.h>
29 : :
30 : : #include "apitest.h"
31 : : #include "testsuite.h"
32 : : #include "testutils.h"
33 : :
34 : : #include <string>
35 : :
36 : : using namespace std;
37 : :
38 : : // Feature test for Query::OP_VALUE_RANGE.
39 : 13 : DEFINE_TESTCASE(valuerange1, backend) {
40 : 13 : Xapian::Database db(get_database("apitest_phrase"));
41 : 13 : Xapian::Enquire enq(db);
42 : : static const char * vals[] = {
43 : : "", " ", "a", "aa", "abcd", "e", "g", "h", "hzz", "i", "l", "z", NULL
44 : : };
45 [ + + ]: 169 : for (const char **start = vals; *start; ++start) {
46 [ + + ]: 2028 : for (const char **end = vals; *end; ++end) {
47 : 1872 : Xapian::Query query(Xapian::Query::OP_VALUE_RANGE, 1, *start, *end);
48 : 1872 : enq.set_query(query);
49 : 1872 : Xapian::MSet mset = enq.get_mset(0, 20);
50 : : // Check that documents in the MSet match the value range filter.
51 : 1872 : set<Xapian::docid> matched;
52 : 1872 : Xapian::MSetIterator i;
53 [ + + ]: 7774 : for (i = mset.begin(); i != mset.end(); ++i) {
54 : 5902 : matched.insert(*i);
55 : 5902 : string value = db.get_document(*i).get_value(1);
56 [ - + # # ]: 5902 : TEST_REL(value,>=,*start);
57 [ - + ][ # # ]: 5902 : TEST_REL(value,<=,*end);
58 : : }
59 : : // Check that documents not in the MSet don't match the value range filter.
60 [ + + ]: 33696 : for (Xapian::docid j = db.get_lastdocid(); j != 0; --j) {
61 [ + + ]: 31824 : if (matched.find(j) == matched.end()) {
62 : 25922 : string value = db.get_document(j).get_value(1);
63 : 25922 : tout << value << " < '" << *start << "' or > '" << *end << "'" << endl;
64 [ + + - + ]: 25922 : TEST(value < *start || value > *end);
[ - + ][ # # ]
65 : : }
66 : : }
67 : : }
68 : : }
69 : 13 : return true;
70 : : }
71 : :
72 : : // Regression test for Query::OP_VALUE_LE - used to return document IDs for
73 : : // non-existent documents.
74 : 10 : DEFINE_TESTCASE(valuerange2, writable) {
75 : 10 : Xapian::WritableDatabase db = get_writable_database();
76 : 10 : Xapian::Document doc;
77 : 10 : doc.set_data("5");
78 : 10 : doc.add_value(0, "5");
79 : 10 : db.replace_document(5, doc);
80 : 10 : Xapian::Enquire enq(db);
81 : :
82 : 10 : Xapian::Query query(Xapian::Query::OP_VALUE_LE, 0, "6");
83 : 10 : enq.set_query(query);
84 : 10 : Xapian::MSet mset = enq.get_mset(0, 20);
85 : :
86 [ - + # # ]: 10 : TEST_EQUAL(mset.size(), 1);
87 [ - + ][ # # ]: 10 : TEST_EQUAL(*(mset[0]), 5);
88 : 10 : return true;
89 : : }
90 : :
91 : : static void
92 : 3 : make_valuerange5(Xapian::WritableDatabase &db, const string &)
93 : : {
94 : 3 : Xapian::Document doc;
95 : 3 : doc.add_value(0, "BOOK");
96 : 3 : db.add_document(doc);
97 : 3 : doc.add_value(0, "VOLUME");
98 : 3 : db.add_document(doc);
99 : 3 : }
100 : :
101 : : // Check that lower and upper bounds are used.
102 : 3 : DEFINE_TESTCASE(valuerange5, generated) {
103 : 3 : Xapian::Database db = get_database("valuerange5", make_valuerange5);
104 [ + + ]: 3 : if (db.get_value_lower_bound(0).empty()) {
105 : : // This backend (i.e. flint) doesn't support value bounds, or else
106 : : // there are no instances of this value. In the former case,
107 : : // get_value_upper_bound() should throw an exception.
108 [ + - ][ - + ]: 2 : TEST_EXCEPTION(Xapian::UnimplementedError,
[ # # ][ - + ]
109 : : db.get_value_upper_bound(0));
110 : 1 : return true;
111 : : }
112 : :
113 : 2 : Xapian::Enquire enq(db);
114 : :
115 : 2 : Xapian::Query query(Xapian::Query::OP_VALUE_RANGE, 0, "APPLE", "BANANA");
116 : 2 : enq.set_query(query);
117 : 2 : Xapian::MSet mset = enq.get_mset(0, 0);
118 [ - + # # ]: 2 : TEST_EQUAL(mset.get_matches_estimated(), 0);
119 : :
120 : 2 : Xapian::Query query2(Xapian::Query::OP_VALUE_RANGE, 0, "WALRUS", "ZEBRA");
121 : 2 : enq.set_query(query2);
122 : 2 : mset = enq.get_mset(0, 0);
123 [ - + # # ]: 2 : TEST_EQUAL(mset.get_matches_estimated(), 0);
124 : :
125 : 3 : return true;
126 : : }
127 : :
128 : : // Feature test for Query::OP_VALUE_GE.
129 : 13 : DEFINE_TESTCASE(valuege1, backend) {
130 : 13 : Xapian::Database db(get_database("apitest_phrase"));
131 : 13 : Xapian::Enquire enq(db);
132 : : static const char * vals[] = {
133 : : "", " ", "a", "aa", "abcd", "e", "g", "h", "hzz", "i", "l", "z", NULL
134 : : };
135 [ + + ]: 169 : for (const char **start = vals; *start; ++start) {
136 : 156 : Xapian::Query query(Xapian::Query::OP_VALUE_GE, 1, *start);
137 : 156 : enq.set_query(query);
138 : 156 : Xapian::MSet mset = enq.get_mset(0, 20);
139 : : // Check that documents in the MSet match the value range filter.
140 : 156 : set<Xapian::docid> matched;
141 : 156 : Xapian::MSetIterator i;
142 [ + + ]: 1833 : for (i = mset.begin(); i != mset.end(); ++i) {
143 : 1677 : matched.insert(*i);
144 : 1677 : string value = db.get_document(*i).get_value(1);
145 : 1677 : tout << "'" << *start << "' <= '" << value << "'" << endl;
146 [ - + # # ]: 1677 : TEST_REL(value,>=,*start);
147 : : }
148 : : // Check that documents not in the MSet don't match the value range
149 : : // filter.
150 [ + + ]: 2808 : for (Xapian::docid j = db.get_lastdocid(); j != 0; --j) {
151 [ + + ]: 2652 : if (matched.find(j) == matched.end()) {
152 : 975 : string value = db.get_document(j).get_value(1);
153 : 975 : tout << value << " < '" << *start << "'" << endl;
154 [ - + # # ]: 975 : TEST_REL(value,<,*start);
155 : : }
156 : : }
157 : : }
158 : 13 : return true;
159 : : }
160 : :
161 : : // Regression test for Query::OP_VALUE_GE - used to segfault if check() got
162 : : // called.
163 : 13 : DEFINE_TESTCASE(valuege2, backend) {
164 : 13 : Xapian::Database db(get_database("apitest_phrase"));
165 : 13 : Xapian::Enquire enq(db);
166 : : Xapian::Query query(Xapian::Query::OP_AND,
167 : : Xapian::Query("what"),
168 : 13 : Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "aa"));
169 : 13 : enq.set_query(query);
170 : 13 : Xapian::MSet mset = enq.get_mset(0, 20);
171 : 13 : return true;
172 : : }
173 : :
174 : : // Feature test for Query::OP_VALUE_LE.
175 : 13 : DEFINE_TESTCASE(valuele1, backend) {
176 : 13 : Xapian::Database db(get_database("apitest_phrase"));
177 : 13 : Xapian::Enquire enq(db);
178 : : static const char * vals[] = {
179 : : "", " ", "a", "aa", "abcd", "e", "g", "h", "hzz", "i", "l", "z", NULL
180 : : };
181 [ + + ]: 169 : for (const char **end = vals; *end; ++end) {
182 : 156 : Xapian::Query query(Xapian::Query::OP_VALUE_LE, 1, *end);
183 : 156 : enq.set_query(query);
184 : 156 : Xapian::MSet mset = enq.get_mset(0, 20);
185 : : // Check that documents in the MSet match the value range filter.
186 : 156 : set<Xapian::docid> matched;
187 : 156 : Xapian::MSetIterator i;
188 [ + + ]: 1274 : for (i = mset.begin(); i != mset.end(); ++i) {
189 : 1118 : matched.insert(*i);
190 : 1118 : string value = db.get_document(*i).get_value(1);
191 [ - + # # ]: 1118 : TEST_REL(value,<=,*end);
192 : : }
193 : : // Check that documents not in the MSet don't match the value range
194 : : // filter.
195 [ + + ]: 2808 : for (Xapian::docid j = db.get_lastdocid(); j != 0; --j) {
196 [ + + ]: 2652 : if (matched.find(j) == matched.end()) {
197 : 1534 : string value = db.get_document(j).get_value(1);
198 [ - + # # ]: 1534 : TEST_REL(value,>,*end);
199 : : }
200 : : }
201 : : }
202 : 13 : return true;
203 : : }
204 : :
205 : : // Check that Query(OP_VALUE_GE, 0, "") -> Query::MatchAll.
206 : 1 : DEFINE_TESTCASE(valuege3, !backend) {
207 : 1 : Xapian::Query query(Xapian::Query::OP_VALUE_GE, 0, "");
208 [ - + ][ # # ]: 1 : TEST_STRINGS_EQUAL(query.get_description(), Xapian::Query::MatchAll.get_description());
209 : 1 : return true;
210 : : }
211 : :
212 : : // Test Query::OP_VALUE_GE in a query which causes its skip_to() to be used.
213 : 13 : DEFINE_TESTCASE(valuege4, backend) {
214 : 13 : Xapian::Database db(get_database("apitest_phrase"));
215 : 13 : Xapian::Enquire enq(db);
216 : :
217 : : // This query should put the ValueGePostList on the LHS of the AND because
218 : : // it has a lower estimated termfreq than the term "fridg". As a result,
219 : : // the skip_to() method is used to advance the ValueGePostList.
220 : : Xapian::Query query(Xapian::Query::OP_AND,
221 : : Xapian::Query("fridg"),
222 : 13 : Xapian::Query(Xapian::Query::OP_VALUE_GE, 1, "aa"));
223 : 13 : enq.set_query(query);
224 : 13 : Xapian::MSet mset = enq.get_mset(0, 20);
225 : 13 : return true;
226 : : }
227 : :
228 : : // Test Query::OP_VALUE_RANGE in a query which causes its check() to be used.
229 : 13 : DEFINE_TESTCASE(valuerange3, backend) {
230 : 13 : Xapian::Database db(get_database("apitest_phrase"));
231 : 13 : Xapian::Enquire enq(db);
232 : : Xapian::Query query(Xapian::Query::OP_AND,
233 : : Xapian::Query("what"),
234 : : Xapian::Query(Xapian::Query::OP_VALUE_RANGE, 1,
235 : 13 : "aa", "z"));
236 : 13 : enq.set_query(query);
237 : 13 : Xapian::MSet mset = enq.get_mset(0, 20);
238 : 13 : return true;
239 : : }
240 : :
241 : : // Test Query::OP_VALUE_RANGE in a query which causes its skip_to() to be used.
242 : 13 : DEFINE_TESTCASE(valuerange4, backend) {
243 : 13 : Xapian::Database db(get_database("apitest_phrase"));
244 : 13 : Xapian::Enquire enq(db);
245 : : Xapian::Query query(Xapian::Query::OP_AND,
246 : : Xapian::Query("fridg"),
247 : : Xapian::Query(Xapian::Query::OP_VALUE_RANGE, 1,
248 : 13 : "aa", "z"));
249 : 13 : enq.set_query(query);
250 : 13 : Xapian::MSet mset = enq.get_mset(0, 20);
251 : 13 : return true;
252 : : }
|