xapian-core  2.0.0
honey_values.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2008,2009,2010,2011,2012,2016,2017,2018 Olly Betts
5  * Copyright (C) 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 "honey_values.h"
25 
26 #include "honey_cursor.h"
27 #include "honey_postlist.h"
28 #include "honey_postlisttable.h"
29 #include "honey_termlist.h"
30 #include "honey_termlisttable.h"
31 
32 #include "bitstream.h"
33 #include "debuglog.h"
35 #include "pack.h"
36 
37 #include "xapian/error.h"
38 #include "xapian/valueiterator.h"
39 
40 #include <algorithm>
41 #include <memory>
42 
43 using namespace Honey;
44 using namespace std;
45 
46 // FIXME:
47 // * multi-values?
48 // * values named instead of numbered?
49 
50 void
51 ValueChunkReader::assign(const char* p_, size_t len, Xapian::docid last_did)
52 {
53  p = p_;
54  end = p_ + len;
55  if (!unpack_uint(&p, end, &did))
56  throw Xapian::DatabaseCorruptError("Failed to unpack docid delta");
57  did = last_did - did;
58  if (!unpack_string(&p, end, value))
59  throw Xapian::DatabaseCorruptError("Failed to unpack first value");
60 }
61 
62 void
64 {
65  if (p == end) {
66  p = NULL;
67  return;
68  }
69 
70  Xapian::docid delta;
71  if (!unpack_uint(&p, end, &delta)) {
72  throw Xapian::DatabaseCorruptError("Failed to unpack streamed value "
73  "docid");
74  }
75  did += delta + 1;
76  if (!unpack_string(&p, end, value))
77  throw Xapian::DatabaseCorruptError("Failed to unpack streamed value");
78 }
79 
80 void
82 {
83  if (p == NULL || target <= did)
84  return;
85 
86  size_t value_len;
87  while (p != end) {
88  // Get the next docid
89  Xapian::docid delta;
90  if (rare(!unpack_uint(&p, end, &delta))) {
91  throw Xapian::DatabaseCorruptError("Failed to unpack streamed "
92  "value docid");
93  }
94  did += delta + 1;
95 
96  // Get the length of the string
97  if (rare(!unpack_uint(&p, end, &value_len))) {
98  throw Xapian::DatabaseCorruptError("Failed to unpack streamed "
99  "value length");
100  }
101 
102  // Check that it's not too long
103  if (rare(value_len > size_t(end - p))) {
104  throw Xapian::DatabaseCorruptError("Failed to unpack streamed "
105  "value");
106  }
107 
108  // Assign the value and return only if we've reached the target
109  if (did >= target) {
110  value.assign(p, value_len);
111  p += value_len;
112  return;
113  }
114  p += value_len;
115  }
116  p = NULL;
117 }
118 
119 void
121  const string& val)
122 {
123  auto i = changes.find(slot);
124  if (i == changes.end()) {
125  i = changes.insert(make_pair(slot, map<Xapian::docid, string>())).first;
126  }
127  i->second[did] = val;
128 }
129 
130 void
132 {
133  auto i = changes.find(slot);
134  if (i == changes.end()) {
135  i = changes.insert(make_pair(slot, map<Xapian::docid, string>())).first;
136  }
137  i->second[did] = string();
138 }
139 
142  Xapian::docid did,
143  string& chunk) const
144 {
145  LOGCALL(DB, Xapian::docid, "HoneyValueManager::get_chunk_containing_did", slot | did | chunk);
146  if (!cursor)
147  cursor.reset(postlist_table.cursor_get());
148  if (!cursor) RETURN(0);
149 
150  bool exact = cursor->find_entry_ge(make_valuechunk_key(slot, did));
151  if (!exact) {
152  // The chunk doesn't end with docid did, so we need to get
153  // the last docid in the chunk from the key to return.
154  did = docid_from_key(slot, cursor->current_key);
155  }
156 
157  cursor->read_tag();
158  chunk = cursor->current_tag;
159  // FIXME: fails, not sure why: swap(chunk, cursor->current_tag);
160 
161  RETURN(did);
162 }
163 
164 static const size_t CHUNK_SIZE_THRESHOLD = 2000;
165 
166 namespace Honey {
167 
170 
172 
173  string ctag;
174 
176 
177  string tag;
178 
180 
182 
184 
186 
187  void append_to_stream(Xapian::docid did, const string& value) {
188  Assert(did);
189  if (!tag.empty()) {
190  AssertRel(did,>,prev_did);
191  pack_uint(tag, did - prev_did - 1);
192  }
193  prev_did = did;
194  new_last_did = did;
195  pack_string(tag, value);
196  if (tag.size() >= CHUNK_SIZE_THRESHOLD) write_tag();
197  }
198 
199  void write_tag() {
200  // If the last docid has changed, delete the old entry.
201  if (last_did && new_last_did != last_did) {
202  table.del(make_valuechunk_key(slot, last_did));
203  }
204  if (!tag.empty()) {
205  table.add(make_valuechunk_key(slot, new_last_did), tag);
206  }
207  last_did = 0;
208  tag.resize(0);
209  }
210 
211  public:
213  : table(table_), slot(slot_), last_did(0), last_allowed_did(0) { }
214 
216  while (!reader.at_end()) {
217  // FIXME: use skip_to and some splicing magic instead?
218  append_to_stream(reader.get_docid(), reader.get_value());
219  reader.next();
220  }
221  write_tag();
222  }
223 
224  void update(Xapian::docid did, const string& value) {
225  if (last_allowed_did && did > last_allowed_did) {
226  // The next change needs to go in a later existing chunk than the
227  // one we're currently updating, so we copy over the rest of the
228  // entries from the current chunk, write out the updated chunk and
229  // drop through to the case below will read in that later chunk.
230  // FIXME: use some string splicing magic instead of this loop.
231  while (!reader.at_end()) {
232  // last_allowed_did should be an upper bound for this chunk.
233  AssertRel(reader.get_docid(),<=,last_allowed_did);
234  append_to_stream(reader.get_docid(), reader.get_value());
235  reader.next();
236  }
237  write_tag();
238  last_allowed_did = 0;
239  }
240  if (last_allowed_did == 0) {
241  last_allowed_did = HONEY_MAX_DOCID;
242  Assert(tag.empty());
243  new_last_did = 0;
244  unique_ptr<HoneyCursor> cursor(table.cursor_get());
245  if (cursor->find_entry_ge(make_valuechunk_key(slot, did))) {
246  // We found an exact match, so the last docid is the one
247  // we looked for.
248  last_did = did;
249  } else {
250  Assert(!cursor->after_end());
251  // Otherwise we need to unpack it from the key we found.
252  // We may have found a non-value-chunk entry in which case
253  // docid_from_key() returns 0.
254  last_did = docid_from_key(slot, cursor->current_key);
255  }
256 
257  // If there are no further chunks, then the last docid that can go
258  // in this chunk is the highest valid docid. If there are further
259  // chunks then it's one less than the first docid of the next
260  // chunk.
261  if (last_did) {
262  // We found a value chunk.
263  cursor->read_tag();
264  // FIXME:swap(cursor->current_tag, ctag);
265  ctag = cursor->current_tag;
266  reader.assign(ctag.data(), ctag.size(), last_did);
267  }
268  if (cursor->next()) {
269  const string& key = cursor->current_key;
270  Xapian::docid next_last_did = docid_from_key(slot, key);
271  if (next_last_did) {
272  cursor->read_tag();
273  Xapian::docid delta;
274  const char* p = cursor->current_tag.data();
275  const char* e = p + cursor->current_tag.size();
276  if (!unpack_uint(&p, e, &delta)) {
277  throw Xapian::DatabaseCorruptError("Failed to unpack "
278  "docid delta");
279  }
280  Xapian::docid next_first_did = next_last_did - delta;
281  last_allowed_did = next_first_did - 1;
282  }
283  Assert(last_allowed_did);
284  AssertRel(last_allowed_did,>=,last_did);
285  }
286  }
287 
288  // Copy over entries until we get to the one we want to
289  // add/modify/delete.
290  // FIXME: use skip_to and some splicing magic instead?
291  while (!reader.at_end() && reader.get_docid() < did) {
292  append_to_stream(reader.get_docid(), reader.get_value());
293  reader.next();
294  }
295  if (!reader.at_end() && reader.get_docid() == did) reader.next();
296  if (!value.empty()) {
297  // Add/update entry for did.
298  append_to_stream(did, value);
299  }
300  }
301 };
302 
303 }
304 
305 void
307 {
308  for (auto&& i : changes) {
309  Xapian::valueno slot = i.first;
310  Honey::ValueUpdater updater(postlist_table, slot);
311  for (auto&& j : i.second) {
312  updater.update(j.first, j.second);
313  }
314  }
315  changes.clear();
316 }
317 
318 string
320  map<Xapian::valueno, ValueStats>& val_stats)
321 {
323  if (it == doc.values_end()) {
324  // No document values.
325  auto i = slots.find(did);
326  if (i != slots.end()) {
327  // Document's values already added or modified in this batch.
328  i->second = string();
329  }
330  return string();
331  }
332 
333  Xapian::valueno count = doc.internal->values_count();
334  Xapian::VecCOW<Xapian::termpos> slotvec(count);
335 
336  Xapian::valueno first_slot = it.get_valueno();
337  Xapian::valueno last_slot = first_slot;
338  while (it != doc.values_end()) {
339  Xapian::valueno slot = it.get_valueno();
340  slotvec.push_back(slot);
341  const string& value = *it;
342 
343  // Update the statistics.
344  auto i = val_stats.insert(make_pair(slot, ValueStats()));
345  ValueStats& stats = i.first->second;
346  if (i.second) {
347  // There were no statistics stored already, so read them.
348  get_value_stats(slot, stats);
349  }
350 
351  // Now, modify the stored statistics.
352  if ((stats.freq)++ == 0) {
353  // If the value count was previously zero, set the upper and lower
354  // bounds to the newly added value.
355  stats.lower_bound = value;
356  stats.upper_bound = value;
357  } else {
358  // Otherwise, simply make sure they reflect the new value.
359  //
360  // Check the upper bound first, as for some common uses of value
361  // slots (dates) the values will tend to get larger not smaller
362  // over time.
363  int cmp = value.compare(stats.upper_bound);
364  if (cmp >= 0) {
365  if (cmp > 0) stats.upper_bound = value;
366  } else if (value < stats.lower_bound) {
367  stats.lower_bound = value;
368  }
369  }
370 
371  add_value(did, slot, value);
372  last_slot = slot;
373  ++it;
374  }
375 
376  if (!termlist_table.is_open()) {
377  return string();
378  }
379 
380  string enc;
381  pack_uint(enc, last_slot);
382  BitWriter slots_used(enc);
383  if (count > 1) {
384  slots_used.encode(first_slot, last_slot);
385  slots_used.encode(count - 2, last_slot - first_slot);
386  slots_used.encode_interpolative(slotvec, 0, slotvec.size() - 1);
387  }
388 
389  return slots_used.freeze();
390 }
391 
392 void
394  map<Xapian::valueno, ValueStats>& val_stats)
395 {
396  Assert(termlist_table.is_open());
397  auto it = slots.find(did);
398  string s;
399  if (it != slots.end()) {
400  swap(s, it->second);
401  } else {
402  // Get from table, making a swift exit if this document has no terms or
403  // values.
404  if (!termlist_table.get_exact_entry(termlist_table.make_key(did), s))
405  return;
406  slots.insert(make_pair(did, string()));
407  }
408 
409  const char* p = s.data();
410  const char* end = p + s.size();
411  size_t slot_enc_size;
412  if (!unpack_uint(&p, end, &slot_enc_size)) {
413  throw Xapian::DatabaseCorruptError("Termlist encoding corrupt");
414  }
415  if (slot_enc_size == 0)
416  return;
417 
418  end = p + slot_enc_size;
419  Xapian::valueno last_slot;
420  if (!unpack_uint(&p, end, &last_slot)) {
421  throw Xapian::DatabaseCorruptError("Slots used data corrupt");
422  }
423 
424  if (p != end) {
425  BitReader rd(p, end);
426  Xapian::valueno first_slot = rd.decode(last_slot);
427  Xapian::valueno slot_count = rd.decode(last_slot - first_slot) + 2;
428  rd.decode_interpolative(0, slot_count - 1, first_slot, last_slot);
429 
430  Xapian::valueno slot = first_slot;
431  while (slot != last_slot) {
432  auto i = val_stats.insert(make_pair(slot, ValueStats()));
433  ValueStats& stats = i.first->second;
434  if (i.second) {
435  // There were no statistics stored already, so read them.
436  get_value_stats(slot, stats);
437  }
438 
439  // Now, modify the stored statistics.
440  AssertRelParanoid(stats.freq, >, 0);
441  if (--(stats.freq) == 0) {
442  stats.lower_bound.resize(0);
443  stats.upper_bound.resize(0);
444  }
445 
446  remove_value(did, slot);
447 
448  slot = rd.decode_interpolative_next();
449  }
450  }
451 
452  Xapian::valueno slot = last_slot;
453  {
454  {
455  // FIXME: share code with above
456  auto i = val_stats.insert(make_pair(slot, ValueStats()));
457  ValueStats& stats = i.first->second;
458  if (i.second) {
459  // There were no statistics stored already, so read them.
460  get_value_stats(slot, stats);
461  }
462 
463  // Now, modify the stored statistics.
464  AssertRelParanoid(stats.freq, >, 0);
465  if (--(stats.freq) == 0) {
466  stats.lower_bound.resize(0);
467  stats.upper_bound.resize(0);
468  }
469 
470  remove_value(did, slot);
471  }
472  }
473 }
474 
475 string
477  const Xapian::Document& doc,
478  map<Xapian::valueno, ValueStats>& val_stats)
479 {
480  if (doc.get_docid() == did) {
481  // If we're replacing a document with itself, but the optimisation for
482  // this higher up hasn't kicked in (e.g. because we've added/replaced
483  // a document since this one was read) and the values haven't changed,
484  // then the call to delete_document() below will remove the values
485  // before the subsequent add_document() can read them.
486  //
487  // The simplest way to handle this is to force the document to read its
488  // values, which we only need to do this is the docid matches. Note
489  // that this check can give false positives as we don't also check the
490  // database, so for example replacing document 4 in one database with
491  // document 4 from another will unnecessarily trigger this, but forcing
492  // the values to be read is fairly harmless, and this is unlikely to be
493  // a common case. (Currently we will end up fetching the values
494  // anyway, but there's scope to change that in the future).
495  doc.internal->ensure_values_fetched();
496  }
497  delete_document(did, val_stats);
498  return add_document(did, doc, val_stats);
499 }
500 
501 string
503 {
504  auto i = changes.find(slot);
505  if (i != changes.end()) {
506  auto j = i->second.find(did);
507  if (j != i->second.end()) return j->second;
508  }
509 
510  // Read it from the table.
511  string chunk;
512  Xapian::docid last_did;
513  last_did = get_chunk_containing_did(slot, did, chunk);
514  if (last_did == 0) return string();
515 
516  ValueChunkReader reader(chunk.data(), chunk.size(), last_did);
517  reader.skip_to(did);
518  if (reader.at_end() || reader.get_docid() != did) return string();
519  return reader.get_value();
520 }
521 
522 void
523 HoneyValueManager::get_all_values(map<Xapian::valueno, string>& values,
524  Xapian::docid did) const
525 {
526  Assert(values.empty());
527  if (!termlist_table.is_open()) {
528  // Either the database has been closed, or else there's no termlist
529  // table. Check if the postlist table is open to determine which is
530  // the case.
531  if (!postlist_table.is_open())
533  throw Xapian::FeatureUnavailableError("Database has no termlist");
534  }
535 
536  string s;
537  if (!termlist_table.get_exact_entry(termlist_table.make_key(did), s))
538  return;
539 
540  const char* p = s.data();
541  const char* end = p + s.size();
542  size_t slot_enc_size = *p++;
543 
544  if ((slot_enc_size & 0x80) == 0) {
545  // If the top bit is clear we have a 7-bit bitmap of slots used.
546  Xapian::valueno slot = 0;
547  while (slot_enc_size) {
548  if (slot_enc_size & 1) {
549  values.insert(make_pair(slot, get_value(did, slot)));
550  }
551  ++slot;
552  slot_enc_size >>= 1;
553  }
554  return;
555  }
556 
557  slot_enc_size &= 0x7f;
558  if (slot_enc_size == 0) {
559  if (!unpack_uint(&p, end, &slot_enc_size)) {
560  throw Xapian::DatabaseCorruptError("Termlist encoding corrupt");
561  }
562  }
563 
564  end = p + slot_enc_size;
565  Xapian::valueno last_slot;
566  if (!unpack_uint(&p, end, &last_slot)) {
567  throw Xapian::DatabaseCorruptError("Slots used data corrupt");
568  }
569 
570  if (p != end) {
571  BitReader rd(p, end);
572  Xapian::valueno first_slot = rd.decode(last_slot);
573  Xapian::valueno slot_count = rd.decode(last_slot - first_slot) + 2;
574  rd.decode_interpolative(0, slot_count - 1, first_slot, last_slot);
575 
576  Xapian::valueno slot = first_slot;
577  while (slot != last_slot) {
578  values.insert(make_pair(slot, get_value(did, slot)));
579  slot = rd.decode_interpolative_next();
580  }
581  }
582  values.insert(make_pair(last_slot, get_value(did, last_slot)));
583 }
584 
585 void
587 {
588  LOGCALL_VOID(DB, "HoneyValueManager::get_value_stats", slot);
589  // Invalidate the cache first in case an exception is thrown.
590  mru_slot = Xapian::BAD_VALUENO;
591  get_value_stats(slot, mru_valstats);
592  mru_slot = slot;
593 }
594 
595 void
597  ValueStats& stats) const
598 {
599  LOGCALL_VOID(DB, "HoneyValueManager::get_value_stats", slot | Literal("[stats]"));
600 
601  string tag;
602  if (postlist_table.get_exact_entry(Honey::make_valuestats_key(slot), tag)) {
603  const char* pos = tag.data();
604  const char* end = pos + tag.size();
605 
606  if (!unpack_uint(&pos, end, &(stats.freq))) {
607  if (pos == 0) {
608  throw Xapian::DatabaseCorruptError("Incomplete stats item in "
609  "value table");
610  }
611  throw Xapian::RangeError("Frequency statistic in value table is "
612  "too large");
613  }
614  if (!unpack_string(&pos, end, stats.lower_bound)) {
615  if (pos == 0) {
616  throw Xapian::DatabaseCorruptError("Incomplete stats item in "
617  "value table");
618  }
619  throw Xapian::RangeError("Lower bound in value table is too "
620  "large");
621  }
622  size_t len = end - pos;
623  if (len == 0) {
624  stats.upper_bound = stats.lower_bound;
625  } else {
626  stats.upper_bound.assign(pos, len);
627  }
628  } else {
629  stats.clear();
630  }
631 }
632 
633 void
634 HoneyValueManager::set_value_stats(map<Xapian::valueno, ValueStats>& val_stats)
635 {
636  LOGCALL_VOID(DB, "HoneyValueManager::set_value_stats", val_stats);
637  for (auto&& i : val_stats) {
638  string key = Honey::make_valuestats_key(i.first);
639  const ValueStats& stats = i.second;
640  if (stats.freq != 0) {
641  postlist_table.add(key, encode_valuestats(stats.freq,
642  stats.lower_bound,
643  stats.upper_bound));
644  } else {
645  postlist_table.del(key);
646  }
647  }
648  val_stats.clear();
649  mru_slot = Xapian::BAD_VALUENO;
650 }
Classes to encode/decode a bitstream.
HoneyCursor * cursor_get() const
Definition: honey_table.cc:454
void add(std::string_view key, const char *val, size_t val_size, bool compressed=false)
Definition: honey_table.cc:74
bool del(const std::string &)
Definition: honey_table.h:671
static void throw_database_closed()
Definition: honey_table.h:689
void add_value(Xapian::docid did, Xapian::valueno slot, const std::string &val)
void get_all_values(std::map< Xapian::valueno, std::string > &values, Xapian::docid did) const
std::string get_value(Xapian::docid did, Xapian::valueno slot) const
void remove_value(Xapian::docid did, Xapian::valueno slot)
void set_value_stats(std::map< Xapian::valueno, ValueStats > &val_stats)
Write the updated statistics to the table.
std::string add_document(Xapian::docid did, const Xapian::Document &doc, std::map< Xapian::valueno, ValueStats > &val_stats)
void get_value_stats(Xapian::valueno slot) const
Get the statistics for value slot slot.
void delete_document(Xapian::docid did, std::map< Xapian::valueno, ValueStats > &val_stats)
std::string replace_document(Xapian::docid did, const Xapian::Document &doc, std::map< Xapian::valueno, ValueStats > &val_stats)
Xapian::docid get_chunk_containing_did(Xapian::valueno slot, Xapian::docid did, std::string &chunk) const
Move the cursor to the chunk containing did.
void assign(const char *p_, size_t len, Xapian::docid last_did)
Definition: honey_values.cc:51
const std::string & get_value() const
Definition: honey_values.h:248
void skip_to(Xapian::docid target)
Definition: honey_values.cc:81
Xapian::docid get_docid() const
Definition: honey_values.h:246
void append_to_stream(Xapian::docid did, const string &value)
Xapian::valueno slot
Xapian::docid last_did
ValueUpdater(HoneyPostListTable &table_, Xapian::valueno slot_)
ValueChunkReader reader
Xapian::docid last_allowed_did
Xapian::docid prev_did
HoneyPostListTable & table
void update(Xapian::docid did, const string &value)
Xapian::docid new_last_did
Read a stream created by BitWriter.
Definition: bitstream.h:66
Xapian::termpos decode(Xapian::termpos outof, bool force=false)
Definition: bitstream.cc:178
void decode_interpolative(int j, int k, Xapian::termpos pos_j, Xapian::termpos pos_k)
Perform interpolative decoding between elements between j and k.
Definition: bitstream.cc:229
Xapian::termpos decode_interpolative_next()
Perform on-demand interpolative decoding.
Definition: bitstream.cc:239
Create a stream to which non-byte-aligned values can be written.
Definition: bitstream.h:34
void encode(Xapian::termpos value, Xapian::termpos outof)
Encode value, known to be less than outof.
Definition: bitstream.cc:92
std::string & freeze()
Finish encoding and return the encoded data as a std::string.
Definition: bitstream.h:51
void encode_interpolative(const Xapian::VecCOW< Xapian::termpos > &pos, int j, int k)
Perform interpolative encoding of pos elements between j and k.
Definition: bitstream.cc:158
DatabaseCorruptError indicates database corruption was detected.
Definition: error.h:397
Class representing a document.
Definition: document.h:64
Xapian::docid get_docid() const
Get the document ID this document came from.
Definition: document.cc:69
Xapian::Internal::intrusive_ptr_nonnull< Internal > internal
Definition: document.h:67
ValueIterator values_begin() const
Start iterating the values in this document.
Definition: document.cc:208
ValueIterator values_end() const noexcept
End iterator corresponding to values_begin().
Definition: document.h:259
Indicates an attempt to use a feature which is unavailable.
Definition: error.h:707
RangeError indicates an attempt to access outside the bounds of a container.
Definition: error.h:959
Class for iterating over document values.
Definition: valueiterator.h:39
Xapian::valueno get_valueno() const
Return the value slot number for the current position.
Suitable for "simple" type T.
Definition: smallvector.h:62
void push_back(T elt)
Definition: smallvector.h:190
size_type size() const
Definition: smallvector.h:135
#define rare(COND)
Definition: config.h:607
PositionList * p
Xapian::termpos pos
Debug logging macros.
#define RETURN(...)
Definition: debuglog.h:484
#define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS)
Definition: debuglog.h:478
#define LOGCALL_VOID(CATEGORY, FUNC, PARAMS)
Definition: debuglog.h:479
Abstract base class for a document.
Hierarchy of classes which Xapian can throw as exceptions.
enc
Definition: header.h:76
HoneyCursor class.
#define HONEY_MAX_DOCID
The largest docid value supported by honey.
Definition: honey_defs.h:64
PostList in a honey database.
Subclass of HoneyTable which holds postlists.
A TermList in a honey database.
Subclass of HoneyTable which holds termlists.
static const size_t CHUNK_SIZE_THRESHOLD
HoneyValueManager class.
static std::string encode_valuestats(Xapian::doccount freq, const std::string &lbound, const std::string &ubound)
Definition: honey_values.h:96
std::string make_valuechunk_key(Xapian::valueno slot, Xapian::docid last_did)
Generate a key for a value stream chunk.
Definition: honey_values.h:39
Xapian::docid docid_from_key(const std::string &key)
std::string make_valuestats_key(Xapian::valueno slot)
Definition: honey_values.h:83
const valueno BAD_VALUENO
Reserved value to indicate "no valueno".
Definition: types.h:100
unsigned valueno
The number for a value slot in a document.
Definition: types.h:90
unsigned XAPIAN_DOCID_BASE_TYPE docid
A unique identifier for a document.
Definition: types.h:51
#define AssertRelParanoid(A, REL, B)
Definition: omassert.h:130
#define AssertRel(A, REL, B)
Definition: omassert.h:123
#define Assert(COND)
Definition: omassert.h:122
Pack types into strings and unpack them again.
bool unpack_string(const char **p, const char *end, std::string &result)
Decode a std::string from a string.
Definition: pack.h:468
bool unpack_uint(const char **p, const char *end, U *result)
Decode an unsigned integer from a string.
Definition: pack.h:346
void pack_uint(std::string &s, U value)
Append an encoded unsigned integer to a string.
Definition: pack.h:315
void pack_string(std::string &s, std::string_view value)
Append an encoded std::string to a string.
Definition: pack.h:442
Definition: pretty.h:48
Class to hold statistics for a given slot.
Definition: valuestats.h:28
std::string lower_bound
A lower bound on the values stored in the given value slot.
Definition: valuestats.h:36
std::string upper_bound
An upper bound on the values stored in the given value slot.
Definition: valuestats.h:40
void clear()
Clear the statistics.
Definition: valuestats.h:47
Xapian::doccount freq
The number of documents which have a (non-empty) value stored in the slot.
Definition: valuestats.h:32
Class for iterating over document values.