xapian-core  1.4.26
api_opvalue.cc
Go to the documentation of this file.
1 
4 /* Copyright 2007,2008,2009,2010,2010,2011,2017,2019 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 DEFINE_TESTCASE(valuerange1, backend) {
40  Xapian::Database db(get_database("apitest_phrase"));
41  Xapian::Enquire enq(db);
42  static const char * const vals[] = {
43  "", " ", "a", "aa", "abcd", "e", "g", "h", "hzz", "i", "l", "z"
44  };
45  for (auto start : vals) {
46  for (auto end : vals) {
48  enq.set_query(query);
49  Xapian::MSet mset = enq.get_mset(0, 20);
50  // Check that documents in the MSet match the value range filter.
51  set<Xapian::docid> matched;
53  for (i = mset.begin(); i != mset.end(); ++i) {
54  matched.insert(*i);
55  string value = db.get_document(*i).get_value(1);
56  TEST_REL(value,>=,start);
57  TEST_REL(value,<=,end);
58  }
59  // Check that documents not in the MSet don't match the value range filter.
60  for (Xapian::docid j = db.get_lastdocid(); j != 0; --j) {
61  if (matched.find(j) == matched.end()) {
62  string value = db.get_document(j).get_value(1);
63  tout << value << " < '" << start << "' or > '" << end << "'\n";
64  TEST(value < start || value > end);
65  }
66  }
67  }
68  }
69 }
70 
71 // Regression test for Query::OP_VALUE_LE - used to return document IDs for
72 // non-existent documents.
73 DEFINE_TESTCASE(valuerange2, backend) {
74  Xapian::Database db = get_database("valuerange2",
76  const string&) {
77  Xapian::Document doc;
78  doc.set_data("5");
79  doc.add_value(0, "5");
80  wdb.replace_document(5, doc);
81  });
82  Xapian::Enquire enq(db);
83 
85  enq.set_query(query);
86  Xapian::MSet mset = enq.get_mset(0, 20);
87 
88  TEST_EQUAL(mset.size(), 1);
89  TEST_EQUAL(*(mset[0]), 5);
90 }
91 
92 static void
94 {
95  Xapian::Document doc;
96  doc.add_value(0, "BOOK");
97  db.add_document(doc);
98  doc.add_value(0, "VOLUME");
99  db.add_document(doc);
100 }
101 
102 // Check that lower and upper bounds are used.
103 DEFINE_TESTCASE(valuerange5, backend) {
104  Xapian::Database db = get_database("valuerange5", make_valuerange5);
105 
106  // If the lower bound is empty, either the specified value slot is
107  // never used in the database, or the backend doesn't track value bounds.
108  // Neither should be true here.
109  TEST(!db.get_value_lower_bound(0).empty());
110 
111  Xapian::Enquire enq(db);
112 
113  Xapian::Query query(Xapian::Query::OP_VALUE_RANGE, 0, "APPLE", "BANANA");
114  enq.set_query(query);
115  Xapian::MSet mset = enq.get_mset(0, 0);
117 
118  Xapian::Query query2(Xapian::Query::OP_VALUE_RANGE, 0, "WALRUS", "ZEBRA");
119  enq.set_query(query2);
120  mset = enq.get_mset(0, 0);
122 }
123 
124 static void
126 {
127  Xapian::Document doc;
128  db.add_document(doc);
129  doc.add_value(0, "SINGULAR");
130  db.add_document(doc);
131  db.add_document(doc);
132 }
133 
134 // Check handling of bounds when bounds are equal.
135 DEFINE_TESTCASE(valuerange6, backend) {
136  const auto OP_VALUE_RANGE = Xapian::Query::OP_VALUE_RANGE;
137  Xapian::Database db = get_database("singularvalue", make_singularvalue_db);
138 
139  Xapian::Enquire enq(db);
140 
142  query = Xapian::Query(OP_VALUE_RANGE, 0, "SATSUMA", "SLOE");
143  enq.set_query(query);
144  Xapian::MSet mset = enq.get_mset(0, 0);
148 
149  query = Xapian::Query(OP_VALUE_RANGE, 0, "PEACH", "PLUM");
150  enq.set_query(query);
151  mset = enq.get_mset(0, 0);
155 
156  query = Xapian::Query(OP_VALUE_RANGE, 0, "PEACH", "PEACH");
157  enq.set_query(query);
158  mset = enq.get_mset(0, 0);
162 
163  query = Xapian::Query(OP_VALUE_RANGE, 0, "PEACH", "PEACHERINE");
164  enq.set_query(query);
165  mset = enq.get_mset(0, 0);
169 
170  query = Xapian::Query(OP_VALUE_RANGE, 0, "SING", "SINGULARITY");
171  enq.set_query(query);
172  mset = enq.get_mset(0, 0);
176 
177  query = Xapian::Query(OP_VALUE_RANGE, 0, "SING", "SINGULAR");
178  enq.set_query(query);
179  mset = enq.get_mset(0, 0);
183 
184  query = Xapian::Query(OP_VALUE_RANGE, 0, "SINGULAR", "SINGULARITY");
185  enq.set_query(query);
186  mset = enq.get_mset(0, 0);
190 
191  query = Xapian::Query(OP_VALUE_RANGE, 0, "SINGULAR", "SINGULAR");
192  enq.set_query(query);
193  mset = enq.get_mset(0, 0);
197 
198  query = Xapian::Query(OP_VALUE_RANGE, 0, "SINGULARITY", "SINGULARITY");
199  enq.set_query(query);
200  mset = enq.get_mset(0, 0);
204 
205  query = Xapian::Query(OP_VALUE_RANGE, 0, "SINGULARITY", "SINGULARITIES");
206  enq.set_query(query);
207  mset = enq.get_mset(0, 0);
211 
212  query = Xapian::Query(OP_VALUE_RANGE, 0, "SINGULARITY", "SINNER");
213  enq.set_query(query);
214  mset = enq.get_mset(0, 0);
218 
219  query = Xapian::Query(OP_VALUE_RANGE, 0, "SINGULARITY", "ZEBRA");
220  enq.set_query(query);
221  mset = enq.get_mset(0, 0);
225 
226  query = Xapian::Query(OP_VALUE_RANGE, 0, "SINGE", "SINGER");
227  enq.set_query(query);
228  mset = enq.get_mset(0, 0);
232 
233  // Check no assertions when slot is empty. Regression test for bug
234  // introduced and fixed between 1.4.5 and 1.4.6.
235  query = Xapian::Query(OP_VALUE_RANGE, 1, "MONK", "MONKEY");
236  enq.set_query(query);
237  mset = enq.get_mset(0, 0);
241 }
242 
243 static void
245 {
246  Xapian::Document doc;
247  db.add_document(doc);
248  doc.add_value(0, "ZERO");
249  db.add_document(doc);
250  doc.add_value(0, string("ZERO\0", 5));
251  db.add_document(doc);
252 }
253 
254 // Check handling of bounds when low is a prefix of high.
255 DEFINE_TESTCASE(valuerange7, backend) {
256  const auto OP_VALUE_RANGE = Xapian::Query::OP_VALUE_RANGE;
257  Xapian::Database db = get_database("valprefixbounds", make_valprefixbounds_db);
258 
259  Xapian::Enquire enq(db);
260 
262  query = Xapian::Query(OP_VALUE_RANGE, 0, "ZAP", "ZOO");
263  enq.set_query(query);
264  Xapian::MSet mset = enq.get_mset(0, 0);
268 
269  query = Xapian::Query(OP_VALUE_RANGE, 0, "ZAP", "ZERO");
270  enq.set_query(query);
271  mset = enq.get_mset(0, 0);
273  if (db.size() > 1) {
274  // The second shard will just have one document with "ZERO" in the slot
275  // so we can tell there's exactly one match there, and the first shard
276  // has one "ZERO\0" and one empty entry, so we can tell that can't
277  // match.
280  } else {
283  }
284 }
285 
286 // Feature test for Query::OP_VALUE_GE.
287 DEFINE_TESTCASE(valuege1, backend) {
288  Xapian::Database db(get_database("apitest_phrase"));
289  Xapian::Enquire enq(db);
290  static const char * const vals[] = {
291  "", " ", "a", "aa", "abcd", "e", "g", "h", "hzz", "i", "l", "z"
292  };
293  for (auto start : vals) {
295  enq.set_query(query);
296  Xapian::MSet mset = enq.get_mset(0, 20);
297  // Check that documents in the MSet match the value range filter.
298  set<Xapian::docid> matched;
300  for (i = mset.begin(); i != mset.end(); ++i) {
301  matched.insert(*i);
302  string value = db.get_document(*i).get_value(1);
303  tout << "'" << start << "' <= '" << value << "'\n";
304  TEST_REL(value,>=,start);
305  }
306  // Check that documents not in the MSet don't match the value range
307  // filter.
308  for (Xapian::docid j = db.get_lastdocid(); j != 0; --j) {
309  if (matched.find(j) == matched.end()) {
310  string value = db.get_document(j).get_value(1);
311  tout << value << " < '" << start << "'\n";
312  TEST_REL(value,<,start);
313  }
314  }
315  }
316 }
317 
318 // Regression test for Query::OP_VALUE_GE - used to segfault if check() got
319 // called.
320 DEFINE_TESTCASE(valuege2, backend) {
321  Xapian::Database db(get_database("apitest_phrase"));
322  Xapian::Enquire enq(db);
324  Xapian::Query("what"),
326  enq.set_query(query);
327  Xapian::MSet mset = enq.get_mset(0, 20);
328 }
329 
330 // Feature test for Query::OP_VALUE_LE.
331 DEFINE_TESTCASE(valuele1, backend) {
332  Xapian::Database db(get_database("apitest_phrase"));
333  Xapian::Enquire enq(db);
334  static const char * const vals[] = {
335  "", " ", "a", "aa", "abcd", "e", "g", "h", "hzz", "i", "l", "z"
336  };
337  for (auto end : vals) {
339  enq.set_query(query);
340  Xapian::MSet mset = enq.get_mset(0, 20);
341  // Check that documents in the MSet match the value range filter.
342  set<Xapian::docid> matched;
344  for (i = mset.begin(); i != mset.end(); ++i) {
345  matched.insert(*i);
346  string value = db.get_document(*i).get_value(1);
347  TEST_REL(value,<=,end);
348  }
349  // Check that documents not in the MSet don't match the value range
350  // filter.
351  for (Xapian::docid j = db.get_lastdocid(); j != 0; --j) {
352  if (matched.find(j) == matched.end()) {
353  string value = db.get_document(j).get_value(1);
354  TEST_REL(value,>,end);
355  }
356  }
357  }
358 }
359 
360 // Check that Query(OP_VALUE_GE, 0, "") -> Query::MatchAll.
361 DEFINE_TESTCASE(valuege3, !backend) {
364 }
365 
366 // Test Query::OP_VALUE_GE in a query which causes its skip_to() to be used.
367 DEFINE_TESTCASE(valuege4, backend) {
368  Xapian::Database db(get_database("apitest_phrase"));
369  Xapian::Enquire enq(db);
370 
371  // This query should put the ValueGePostList on the LHS of the AND because
372  // it has a lower estimated termfreq than the term "fridg". As a result,
373  // the skip_to() method is used to advance the ValueGePostList.
375  Xapian::Query("fridg"),
377  enq.set_query(query);
378  Xapian::MSet mset = enq.get_mset(0, 20);
379 }
380 
381 // Test Query::OP_VALUE_RANGE in a query which causes its check() to be used.
382 DEFINE_TESTCASE(valuerange3, backend) {
383  Xapian::Database db(get_database("apitest_phrase"));
384  Xapian::Enquire enq(db);
386  Xapian::Query("what"),
388  "aa", "z"));
389  enq.set_query(query);
390  Xapian::MSet mset = enq.get_mset(0, 20);
391 }
392 
393 // Test Query::OP_VALUE_RANGE in a query which causes its skip_to() to be used.
394 DEFINE_TESTCASE(valuerange4, backend) {
395  Xapian::Database db(get_database("apitest_phrase"));
396  Xapian::Enquire enq(db);
398  Xapian::Query("fridg"),
400  "aa", "z"));
401  enq.set_query(query);
402  Xapian::MSet mset = enq.get_mset(0, 20);
403 }
404 
406 DEFINE_TESTCASE(valuerangematchesub1, backend) {
407  Xapian::Database db(get_database("etext"));
408  Xapian::Enquire enq(db);
409  // Values present in slot 10 range from 'e' to 'w'.
411  "h", "i"));
412  enq.set_query(query);
413  Xapian::MSet mset = enq.get_mset(0, 0);
414  // The upper bound used to be db.size().
417  // The estimate used to be db.size() / 2, now it's calculated
418  // proportional to the possible range.
419  TEST_REL(mset.get_matches_estimated(), <=, db.get_doccount() / 3);
420 }
static void make_singularvalue_db(Xapian::WritableDatabase &db, const string &)
Definition: api_opvalue.cc:125
Xapian::doccount size() const
Return number of items in this MSet object.
Definition: omenquire.cc:318
Xapian::Document get_document(Xapian::docid did) const
Get a document from the database, given its document id.
Definition: omdatabase.cc:490
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
#define TEST(a)
Test a condition, without an additional explanation for failure.
Definition: testsuite.h:275
This class is used to access a database, or a group of databases.
Definition: database.h:68
static const Xapian::Query MatchAll
A query matching all documents.
Definition: query.h:75
Xapian::doccount get_matches_lower_bound() const
Lower bound on the total number of matching documents.
Definition: omenquire.cc:246
Xapian::docid get_lastdocid() const
Get the highest document id which has been used in the database.
Definition: omdatabase.cc:279
a generic test suite engine
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:938
void replace_document(Xapian::docid did, const Xapian::Document &document)
Replace a given document in the database.
Definition: omdatabase.cc:952
Xapian::doccount get_doccount() const
Get the number of documents in the database.
Definition: omdatabase.cc:267
test functionality of the Xapian API
DEFINE_TESTCASE(valuerange1, backend)
Definition: api_opvalue.cc:39
Xapian::doccount get_matches_upper_bound() const
Upper bound on the total number of matching documents.
Definition: omenquire.cc:262
#define TEST_REL(A, REL, B)
Test a relation holds,e.g. TEST_REL(a,>,b);.
Definition: testmacros.h:32
This class provides read/write access to a database.
Definition: database.h:789
std::ostringstream tout
The debug printing stream.
Definition: testsuite.cc:104
Iterator over a Xapian::MSet.
Definition: mset.h:368
Public interfaces for the Xapian library.
Match only documents where a value slot is >= a given value.
Definition: query.h:223
Match only documents where a value slot is within a given range.
Definition: query.h:158
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
Match only documents where a value slot is <= a given value.
Definition: query.h:231
void set_query(const Xapian::Query &query, Xapian::termcount qlen=0)
Set the query to run.
Definition: omenquire.cc:793
size_t size() const
Return number of shards in this Database object.
Definition: database.h:93
Match only documents which all subqueries match.
Definition: query.h:84
static Xapian::Query query(Xapian::Query::op op, const string &t1=string(), const string &t2=string(), const string &t3=string(), const string &t4=string(), const string &t5=string(), const string &t6=string(), const string &t7=string(), const string &t8=string(), const string &t9=string(), const string &t10=string())
Definition: api_anydb.cc:63
Xapian::Database get_database(const string &dbname)
Definition: apitest.cc:48
Xapian::doccount get_matches_estimated() const
Estimate of the total number of matching documents.
Definition: omenquire.cc:253
std::string get_description() const
Return a string describing this object.
Definition: query.cc:232
This class provides an interface to the information retrieval system for the purpose of searching...
Definition: enquire.h:152
std::string get_value_lower_bound(Xapian::valueno slot) const
Get a lower bound on the values stored in the given value slot.
Definition: omdatabase.cc:368
Xapian-specific test helper functions and macros.
#define TEST_STRINGS_EQUAL(a, b)
Test for equality of two strings.
Definition: testsuite.h:287
static void make_valuerange5(Xapian::WritableDatabase &db, const string &)
Definition: api_opvalue.cc:93
unsigned XAPIAN_DOCID_BASE_TYPE docid
A unique identifier for a document.
Definition: types.h:52
Class representing a query.
Definition: query.h:46
#define TEST_EQUAL(a, b)
Test for equality of two things.
Definition: testsuite.h:278
static void make_valprefixbounds_db(Xapian::WritableDatabase &db, const string &)
Definition: api_opvalue.cc:244
void set_data(const std::string &data)
Set data stored in the document.
Definition: omdocument.cc:78
std::string get_value(Xapian::valueno slot) const
Get value by number.
Definition: omdocument.cc:64
Xapian::doccount get_value_freq(Xapian::valueno slot) const
Return the frequency of a given value slot.
Definition: omdatabase.cc:355
A handle representing a document in a Xapian database.
Definition: document.h:61