xapian-core  2.0.0
api_metadata.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2007,2009,2011,2024 Olly Betts
5  * Copyright (C) 2007,2008,2009 Lemur Consulting Ltd
6  *
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.
11  *
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.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see
19  * <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 
24 #include "api_metadata.h"
25 
26 #include <xapian.h>
27 
28 #include "apitest.h"
29 #include "testsuite.h"
30 #include "testutils.h"
31 
32 #include <string>
33 
34 using namespace std;
35 
36 // Test metadata methods for non-writable databases.
37 DEFINE_TESTCASE(metadata1, metadata) {
38  Xapian::Database db = get_database("metadata1",
40  const string&)
41  {
42  wdb.set_metadata("empty", "");
43  wdb.set_metadata("foo", "bar");
44  wdb.set_metadata("\0"s, "\0"s);
45  wdb.set_metadata("\0fo"s, "\0xx"s);
46  wdb.set_metadata("f\0o"s, "x\0x"s);
47  wdb.set_metadata("fo\0"s, "xx\0"s);
48  });
49  TEST_EQUAL(db.get_doccount(), 0);
50  TEST_EQUAL(db.get_metadata("empty"), "");
51  TEST_EQUAL(db.get_metadata("unset"), "");
52  TEST_EQUAL(db.get_metadata("foo"), "bar");
53 
54  // Check for transparent handling of zero bytes.
55  TEST_EQUAL(db.get_metadata("\0"s), "\0"s);
56  TEST_EQUAL(db.get_metadata("\0fo"s), "\0xx"s);
57  TEST_EQUAL(db.get_metadata("f\0o"s), "x\0x"s);
58  TEST_EQUAL(db.get_metadata("fo\0"s), "xx\0"s);
59 
61 }
62 
63 // Basic test of metadata methods.
64 DEFINE_TESTCASE(metadata2, metadata && writable) {
66 
67  TEST_EQUAL(db.get_metadata("foo"), "");
68  db.set_metadata("foo", "bar");
69  TEST_EQUAL(db.get_metadata("foo"), "bar");
70  db.set_metadata("foo", "baz");
71  TEST_EQUAL(db.get_doccount(), 0);
72  TEST_EQUAL(db.get_metadata("foo"), "baz");
73  db.set_metadata("foo", "");
74  TEST_EQUAL(db.get_metadata("foo"), "");
75 
76  TEST_EQUAL(db.get_doccount(), 0);
77 
78  // Check for transparent handling of zero bytes.
79  db.set_metadata("foo", "value of foo");
80  db.set_metadata("foo\0bar"sv, "\0"sv);
81  db.set_metadata("foo\0"sv, "foo\0bar"sv);
82 
83  TEST_EQUAL(db.get_metadata("foo"), "value of foo");
84  TEST_EQUAL(db.get_metadata("foo\0bar"sv), "\0"sv);
85  TEST_EQUAL(db.get_metadata("foo\0"sv), "foo\0bar"sv);
86 
87  db.commit();
88 
89  TEST_EQUAL(db.get_metadata("foo"), "value of foo");
90  TEST_EQUAL(db.get_metadata("foo\0bar"sv), "\0"sv);
91  TEST_EQUAL(db.get_metadata("foo\0"sv), "foo\0bar"sv);
92 }
93 
94 // Test that metadata gets applied at same time as other changes.
95 DEFINE_TESTCASE(metadata3, metadata && writable && !inmemory) {
96  // get_writable_database_as_database() not implemented for inmemory.
99 
100  TEST_EQUAL(db.get_metadata("foo"), "");
101  db.set_metadata("foo", "bar");
102  TEST_EQUAL(db.get_metadata("foo"), "bar");
103  TEST_EQUAL(dbr.get_metadata("foo"), "");
104  db.commit();
105  TEST_EQUAL(dbr.get_metadata("foo"), "");
106  TEST(dbr.reopen());
107  TEST_EQUAL(db.get_metadata("foo"), "bar");
108  TEST_EQUAL(dbr.get_metadata("foo"), "bar");
109  TEST_EQUAL(dbr.get_doccount(), 0);
110 
112  db.set_metadata("foo", "baz");
113  TEST_EQUAL(db.get_doccount(), 1);
114  TEST_EQUAL(db.get_metadata("foo"), "baz");
115  db.commit();
116 
117  TEST_EQUAL(dbr.get_metadata("foo"), "bar");
118  TEST(dbr.reopen());
119  TEST_EQUAL(dbr.get_metadata("foo"), "baz");
120 
121  db.set_metadata("foo", "");
122  TEST_EQUAL(db.get_metadata("foo"), "");
123  db.commit();
124  TEST_EQUAL(dbr.get_metadata("foo"), "baz");
125  TEST(dbr.reopen());
126  TEST_EQUAL(dbr.get_metadata("foo"), "");
127 
128  TEST_EQUAL(db.get_doccount(), 1);
129 }
130 
131 // Test the empty metadata keys give an error correctly.
132 DEFINE_TESTCASE(metadata4, metadata && writable) {
134 
138 }
139 
140 // Regression test for adding a piece of metadata on its own before adding
141 // other things.
142 DEFINE_TESTCASE(metadata5, metadata && writable && !inmemory) {
143  // get_writable_database_as_database() not implemented for inmemory.
145 
146  db.set_metadata("foo", "foo");
147  db.commit();
148 
149  Xapian::Document doc;
150  doc.add_posting("foo", 1);
151  db.add_document(doc);
152 
154 }
155 
156 // Test metadata iterators.
157 DEFINE_TESTCASE(metadata6, writable) {
159 
160  // Check that iterator on empty database returns nothing.
162  iter = db.metadata_keys_begin();
163  TEST_EQUAL(iter, db.metadata_keys_end());
164 
165  // FIXME: inmemory doesn't implement metadata iterators yet, except in the
166  // trivial case of there being no keys to iterate.
167  SKIP_TEST_FOR_BACKEND("inmemory");
168 
169  try {
170  db.set_metadata("foo", "val");
171  } catch (const Xapian::UnimplementedError &) {
172  SKIP_TEST("Metadata not supported by this backend");
173  }
174  db.commit();
175 
176  // Check iterator on a database with only metadata items.
177  iter = db.metadata_keys_begin();
178  TEST(iter != db.metadata_keys_end());
179  TEST_EQUAL(*iter, "foo");
180  ++iter;
181  TEST(iter == db.metadata_keys_end());
182 
183  // Check iterator on a database with metadata items and documents.
184  Xapian::Document doc;
185  doc.add_posting("foo", 1);
186  db.add_document(doc);
187  db.commit();
188 
189  iter = db.metadata_keys_begin();
190  TEST(iter != db.metadata_keys_end());
191  TEST_EQUAL(*iter, "foo");
192  ++iter;
193  TEST(iter == db.metadata_keys_end());
194 
195  // Check iterator on a database with documents but no metadata. Also
196  // checks that setting metadata to empty stops the iterator returning it.
197  db.set_metadata("foo", "");
198  db.commit();
199  iter = db.metadata_keys_begin();
200  TEST(iter == db.metadata_keys_end());
201 
202  // Check use of a prefix, and skip_to.
203  db.set_metadata("a", "val");
204  db.set_metadata("foo", "val");
205  db.set_metadata("foo1", "val");
206  db.set_metadata("foo2", "val");
207  db.set_metadata("z", "val");
208  db.commit();
209 
210  iter = db.metadata_keys_begin();
211  TEST(iter != db.metadata_keys_end());
212  TEST_EQUAL(*iter, "a");
213  ++iter;
214  TEST(iter != db.metadata_keys_end());
215  TEST_EQUAL(*iter, "foo");
216  ++iter;
217  TEST(iter != db.metadata_keys_end());
218  TEST_EQUAL(*iter, "foo1");
219  ++iter;
220  TEST(iter != db.metadata_keys_end());
221  TEST_EQUAL(*iter, "foo2");
222  ++iter;
223  TEST(iter != db.metadata_keys_end());
224  TEST_EQUAL(*iter, "z");
225  ++iter;
226  TEST(iter == db.metadata_keys_end());
227 
228  iter = db.metadata_keys_begin("foo");
229  TEST(iter != db.metadata_keys_end("foo"));
230  TEST_EQUAL(*iter, "foo");
231  ++iter;
232  TEST(iter != db.metadata_keys_end("foo"));
233  TEST_EQUAL(*iter, "foo1");
234  ++iter;
235  TEST(iter != db.metadata_keys_end("foo"));
236  TEST_EQUAL(*iter, "foo2");
237  ++iter;
238  TEST(iter == db.metadata_keys_end("foo"));
239 
240  iter = db.metadata_keys_begin("foo1");
241  TEST(iter != db.metadata_keys_end("foo1"));
242  TEST_EQUAL(*iter, "foo1");
243  ++iter;
244  TEST(iter == db.metadata_keys_end("foo1"));
245 
246  iter = db.metadata_keys_begin();
247  TEST(iter != db.metadata_keys_end());
248  TEST_EQUAL(*iter, "a");
249 
250  // Skip to "" should move to the first key.
251  iter.skip_to("");
252  TEST(iter != db.metadata_keys_end());
253  TEST_EQUAL(*iter, "a");
254 
255  // This skip_to should skip the "foo" key.
256  iter.skip_to("foo1");
257  TEST(iter != db.metadata_keys_end());
258  TEST_EQUAL(*iter, "foo1");
259 
260  // Check that skipping to the current key works.
261  iter.skip_to("foo1");
262  TEST(iter != db.metadata_keys_end());
263  TEST_EQUAL(*iter, "foo1");
264 
265  // Check that skip_to a key before the current one doesn't move forwards.
266  iter.skip_to("a");
267  TEST(iter != db.metadata_keys_end());
268  TEST_REL(*iter, <=, "foo1");
269 
270  // Make sure we're back on foo1.
271  iter.skip_to("foo1");
272  TEST(iter != db.metadata_keys_end());
273  TEST_EQUAL(*iter, "foo1");
274 
275  // Check that advancing after a skip_to() works correctly.
276  ++iter;
277  TEST(iter != db.metadata_keys_end());
278  TEST_EQUAL(*iter, "foo2");
279 
280  // Check that skipping to a key after the last key works.
281  iter.skip_to("zoo");
282  TEST(iter == db.metadata_keys_end());
283 }
284 
286 DEFINE_TESTCASE(writeread1, metadata && writable) {
288  db_w.set_metadata("1", "2");
289  string longitem(20000, 'j');
290  db_w.set_metadata("2", longitem);
291 
292  string readitem = db_w.get_metadata("2");
293  TEST_EQUAL(readitem, longitem);
294 }
DEFINE_TESTCASE(metadata1, metadata)
Definition: api_metadata.cc:37
Xapian::Database get_writable_database_as_database()
Definition: apitest.cc:126
Xapian::WritableDatabase get_writable_database(const string &dbname)
Definition: apitest.cc:86
Xapian::Database get_database(const string &dbname)
Definition: apitest.cc:47
test functionality of the Xapian API
#define SKIP_TEST_FOR_BACKEND(B)
Definition: apitest.h:84
An indexed database of documents.
Definition: database.h:75
Xapian::TermIterator metadata_keys_begin(std::string_view prefix={}) const
An iterator which returns all user-specified metadata keys.
Definition: database.cc:499
Xapian::TermIterator metadata_keys_end(std::string_view={}) const noexcept
End iterator corresponding to metadata_keys_begin().
Definition: database.h:576
Xapian::doccount get_doccount() const
Get the number of documents in the database.
Definition: database.cc:233
bool reopen()
Reopen the database at the latest available revision.
Definition: database.cc:93
std::string get_metadata(std::string_view key) const
Get the user-specified metadata associated with a given key.
Definition: database.cc:490
Class representing a document.
Definition: document.h:64
void add_posting(std::string_view term, Xapian::termpos term_pos, Xapian::termcount wdf_inc=1)
Add a posting for a term.
Definition: document.cc:111
InvalidArgumentError indicates an invalid parameter value was passed to the API.
Definition: error.h:229
Class for iterating over a list of terms.
Definition: termiterator.h:41
void skip_to(std::string_view term)
Advance the iterator to term term.
UnimplementedError indicates an attempt to use an unimplemented feature.
Definition: error.h:313
This class provides read/write access to a database.
Definition: database.h:964
void set_metadata(std::string_view key, std::string_view metadata)
Set the user-specified metadata associated with a given key.
Definition: database.cc:634
void commit()
Commit pending modifications.
Definition: database.cc:543
Xapian::docid add_document(const Xapian::Document &doc)
Add a document to the database.
Definition: database.cc:561
#define TEST_REL(A, REL, B)
Test a relation holds,e.g. TEST_REL(a,>,b);.
Definition: testmacros.h:35
a generic test suite engine
#define SKIP_TEST(MSG)
Skip the current testcase with message MSG.
Definition: testsuite.h:71
#define TEST_EQUAL(a, b)
Test for equality of two things.
Definition: testsuite.h:276
#define TEST(a)
Test a condition, without an additional explanation for failure.
Definition: testsuite.h:273
Xapian-specific test helper functions and macros.
#define TEST_EXCEPTION(TYPE, CODE)
Check that CODE throws exactly Xapian exception TYPE.
Definition: testutils.h:112
Public interfaces for the Xapian library.