xapian-core  2.0.0
mset.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2017,2024,2025 Olly Betts
5  * Copyright (C) 2018 Uppinder Chugh
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 "msetinternal.h"
25 #include "xapian/mset.h"
26 
27 // FIXME: Clustering API needs work: #include "xapian/cluster.h"
28 
30 #include "net/serialise.h"
31 #include "matcher/msetcmp.h"
32 #include "omassert.h"
33 #include "pack.h"
34 #include "roundestimate.h"
35 #include "serialise-double.h"
36 #include "str.h"
38 
39 #include <algorithm>
40 #include <cfloat>
41 #include <string>
42 #include <string_view>
43 #include <unordered_set>
44 
45 using namespace std;
46 
47 namespace Xapian {
48 
49 MSet::MSet(const MSet&) = default;
50 
51 MSet&
52 MSet::operator=(const MSet&) = default;
53 
54 MSet::MSet(MSet&&) = default;
55 
56 MSet&
57 MSet::operator=(MSet&&) = default;
58 
59 MSet::MSet() : internal(new MSet::Internal) {}
60 
61 MSet::MSet(Internal* internal_) : internal(internal_) {}
62 
64 
65 void
67 {
68  internal->fetch(first, last);
69 }
70 
71 void
73 {
74  internal->set_item_weight(i, weight);
75 }
76 
77 #if 0 // FIXME: Diversification API needs work.
86 static double
87 evaluate_dmset(const vector<Xapian::docid>& dmset,
88  const Xapian::ClusterSet& cset,
89  double factor1,
90  double factor2,
91  const Xapian::MSet& mset,
92  const vector<double>& dissimilarity)
93 {
94  double score_1 = 0, score_2 = 0;
95 
96  // FIXME: We could compute score_1 once then adjust for each candidate
97  // change.
98  // Seems hard to do similar for score_2 though.
99  for (auto mset_index : dmset)
100  score_1 += mset[mset_index].get_weight();
101 
102  auto cset_size = cset.size();
103  for (Xapian::doccount c = 0; c < cset_size; ++c) {
104  double min_dist = numeric_limits<double>::max();
105  unsigned int pos = 1;
106  for (auto mset_index : dmset) {
107  // FIXME: Pre-compute 1.0 / log(2.0 + i) for i = [0, dmset.size()) ?
108  double weight = dissimilarity[mset_index * cset_size + c];
109  weight /= log(1.0 + pos);
110  min_dist = min(min_dist, weight);
111  ++pos;
112  }
113  score_2 += min_dist;
114  }
115 
116  return factor2 * score_2 - factor1 * score_1;
117 }
118 
119 void
120 MSet::diversify_(Xapian::doccount k,
122  double factor1,
123  double factor2)
124 {
125  // Ensured by inlined caller.
126  AssertRel(k, >=, 2);
127 
128  auto mset_size = size();
129  if (mset_size <= k) {
130  // Picking k documents would pick the whole MSet so nothing to do.
131  //
132  // Since k >= 2, this means we don't try to diversify an MSet with
133  // 2 documents (for which reordering can't usefully improve diversity
134  // since the only possible change is to swap the order of the 2
135  // documents).
136  return;
137  }
138 
140  std::vector<Xapian::doccount> main_dmset;
141  main_dmset.reserve(k);
142 
143  Xapian::doccount count = 0;
144  TermListGroup tlg(*this);
145  std::vector<Xapian::Point> points;
146  points.reserve(mset_size);
147  for (MSetIterator it = begin(); it != end(); ++it) {
148  Xapian::Document doc = it.get_document();
149  doc.internal->set_index(count);
150  points.push_back(Xapian::Point(tlg, doc));
151  // Initial top-k diversified documents
152  if (count < k) {
153  // The initial diversified document set is the top-k documents from
154  // the MSet.
155  main_dmset.push_back(count);
156  }
157  ++count;
158  }
159 
160  // Cluster the MSet into k clusters.
162 
175  // Pre-compute all the dissimilarity values.
176  auto cset_size = cset.size();
177  std::vector<double> dissimilarity;
178  dissimilarity.reserve(cset_size * points.size());
179  {
181  for (const auto& point : points) {
182  for (unsigned int c = 0; c < cset_size; ++c) {
183  double dist = d.similarity(point, cset[c].get_centroid());
184  dissimilarity.push_back(1.0 - dist);
185  }
186  }
187  }
188 
189  // Build topc, which contains the union of the top-r relevant documents of
190  // each cluster.
191  vector<Xapian::docid> topc;
192  for (Xapian::doccount c = 0; c < cset_size; ++c) {
193  // FIXME: This is supposed to pick the `r` most relevant documents, but
194  // actually seems to pick those with the lowest docids.
195  auto documents = cset[c].get_documents();
196  auto limit = std::min(r, documents.size());
197  for (Xapian::doccount d = 0; d < limit; ++d) {
198  auto mset_index = documents[d].internal->get_index();
199  topc.push_back(mset_index);
200  }
201  }
202 
203  vector<Xapian::doccount> curr_dmset = main_dmset;
204 
205  while (true) {
206  bool found_better_dmset = false;
207  for (unsigned int i = 0; i < main_dmset.size(); ++i) {
208  auto curr_doc = main_dmset[i];
209  double best_score = evaluate_dmset(curr_dmset, cset,
210  factor1, factor2,
211  *this, dissimilarity);
212  bool found_better_doc = false;
213 
214  for (unsigned int j = 0; j < topc.size(); ++j) {
215  // Continue if candidate document from topc already
216  // exists in curr_dmset. FIXME: Linear search!
217  auto candidate_doc = find(curr_dmset.begin(), curr_dmset.end(),
218  topc[j]);
219  if (candidate_doc != curr_dmset.end()) {
220  continue;
221  }
222 
223  auto temp_doc = curr_dmset[i];
224  curr_dmset[i] = topc[j];
225  double score = evaluate_dmset(curr_dmset, cset,
226  factor1, factor2,
227  *this, dissimilarity);
228 
229  if (score < best_score) {
230  curr_doc = curr_dmset[i];
231  best_score = score;
232  found_better_doc = true;
233  }
234 
235  curr_dmset[i] = temp_doc;
236  }
237  if (found_better_doc) {
238  curr_dmset[i] = curr_doc;
239  found_better_dmset = true;
240  }
241  }
242 
243  // Terminate algorithm when there's no change in current
244  // document matchset
245  if (!found_better_dmset)
246  break;
247 
248  main_dmset = curr_dmset;
249  }
250 
251  // Reorder the results to reflect the diversification. To do this we need
252  // to partition the MSet so the promoted documents come first (in original
253  // MSet order), followed by the non-promoted documents (also in original
254  // MSet order).
255  unordered_set<Xapian::docid> promoted{k};
256  for (auto mset_index : main_dmset) {
257  promoted.insert(internal->items[mset_index].get_docid());
258  }
259 
260  stable_partition(internal->items.begin(), internal->items.end(),
261  [&](const Result& result) {
262  return promoted.count(result.get_docid());
263  });
264 }
265 #endif
266 
267 void
269 {
270  std::sort(internal->items.begin(), internal->items.end(),
272 }
273 
274 int
275 MSet::convert_to_percent(double weight) const
276 {
277  return internal->convert_to_percent(weight);
278 }
279 
281 MSet::get_termfreq(std::string_view term) const
282 {
283  // Check the cached data for query terms first.
284  Xapian::doccount termfreq;
285  if (usual(internal->stats && internal->stats->get_stats(term, termfreq))) {
286  return termfreq;
287  }
288 
289  if (rare(!internal->enquire)) {
290  // Consistent with get_termfreq() on an empty database which always
291  // returns 0.
292  return 0;
293  }
294 
295  // Fall back to asking the database via enquire.
296  return internal->enquire->get_termfreq(term);
297 }
298 
299 double
300 MSet::get_termweight(std::string_view term) const
301 {
302  // A term not in the query has no termweight, so 0.0 makes sense as the
303  // answer in such cases.
304  double weight = 0.0;
305  if (usual(internal->stats)) {
306  (void)internal->stats->get_termweight(term, weight);
307  }
308  return weight;
309 }
310 
313 {
314  return internal->first;
315 }
316 
319 {
320  return internal->matches_lower_bound;
321 }
322 
325 {
326  // Doing this here avoids calculating if the estimate is never looked at,
327  // though does mean we recalculate if this method is called more than once.
331 }
332 
335 {
336  return internal->matches_upper_bound;
337 }
338 
341 {
342  return internal->uncollapsed_lower_bound;
343 }
344 
347 {
348  // Doing this here avoids calculating if the estimate is never looked at,
349  // though does mean we recalculate if this method is called more than once.
353 }
354 
357 {
358  return internal->uncollapsed_upper_bound;
359 }
360 
361 double
363 {
364  return internal->max_attained;
365 }
366 
367 double
369 {
370  return internal->max_possible;
371 }
372 
374 MSet::size() const
375 {
376  return internal->items.size();
377 }
378 
379 std::string
380 MSet::snippet(std::string_view text,
381  size_t length,
382  const Xapian::Stem& stemmer,
383  unsigned flags,
384  std::string_view hi_start,
385  std::string_view hi_end,
386  std::string_view omit) const
387 {
388  // The actual implementation is in queryparser/termgenerator_internal.cc.
389  return internal->snippet(text, length, stemmer, flags,
390  hi_start, hi_end, omit);
391 }
392 
393 std::string
395 {
396  return internal->get_description();
397 }
398 
399 Document
401 {
402  if (index >= items.size()) {
403  string msg = "Requested index ";
404  msg += str(index);
405  msg += " in MSet of size ";
406  msg += str(items.size());
407  throw Xapian::RangeError(msg);
408  }
409  Assert(enquire);
410  return enquire->get_document(items[index].get_docid());
411 }
412 
413 void
415 {
416  if (items.empty() || !enquire) {
417  return;
418  }
419  if (last > items.size() - 1) {
420  last = items.size() - 1;
421  }
422  if (first_ <= last) {
423  Xapian::doccount n = last - first_;
424  for (Xapian::doccount i = 0; i <= n; ++i) {
425  enquire->request_document(items[i].get_docid());
426  }
427  }
428 }
429 
430 void
432 {
433  // max_attained is updated assuming that set_item_weight is called on every
434  // MSet item from 0 up. While assigning new weights max_attained is updated
435  // as the maximum of the new weights set till Xapian::doccount i.
436  if (i == 0)
437  max_attained = weight;
438  else
439  max_attained = max(max_attained, weight);
440  // Ideally the max_possible should be the maximum possible weight that
441  // can be assigned by the reranking algorithm, but since it is not always
442  // possible to calculate the max possible weight for a reranking algorithm
443  // we use this approach.
444  max_possible = max(max_possible, max_attained);
445  items[i].set_weight(weight);
446 }
447 
448 int
450 {
451  int percent;
452  if (percent_scale_factor == 0.0) {
453  // For an unweighted search, give all matches 100%.
454  percent = 100;
455  } else if (weight <= 0.0) {
456  // Some weighting schemes can return zero relevance while matching,
457  // so give such matches 0%.
458  percent = 0;
459  } else {
460  // Adding on 100 * DBL_EPSILON was a hack to work around excess
461  // precision (e.g. on x86 when not using SSE), but this code seems like
462  // it's generally asking for problems with floating point rounding
463  // issues - maybe we ought to carry through the matching and total
464  // number of subqueries and calculate using those instead.
465  //
466  // There are corresponding hacks in matcher/matcher.cc.
467  percent = int(weight * percent_scale_factor + 100.0 * DBL_EPSILON);
468  if (percent <= 0) {
469  // Make any non-zero weight give a non-zero percentage.
470  percent = 1;
471  } else if (percent > 100) {
472  // Make sure we don't ever exceed 100%.
473  percent = 100;
474  }
475  // FIXME: Ideally we should also make sure any non-exact match gives
476  // < 100%.
477  }
478  return percent;
479 }
480 
481 void
483  Xapian::doccount n_shards)
484 {
485  for (auto& result : items) {
486  result.unshard_docid(shard, n_shards);
487  }
488 }
489 
490 void
491 MSet::Internal::merge_stats(const Internal* o, bool collapsing)
492 {
493  if (snippet_bg_relevance.empty()) {
494  snippet_bg_relevance = o->snippet_bg_relevance;
495  } else {
496  Assert(snippet_bg_relevance == o->snippet_bg_relevance);
497  }
498  if (collapsing) {
499  matches_lower_bound = max(matches_lower_bound, o->matches_lower_bound);
500  // matches_estimated will get adjusted later in this case.
501  } else {
502  matches_lower_bound += o->matches_lower_bound;
503  }
504  matches_estimated += o->matches_estimated;
505  matches_upper_bound += o->matches_upper_bound;
506  uncollapsed_lower_bound += o->uncollapsed_lower_bound;
507  uncollapsed_estimated += o->uncollapsed_estimated;
508  uncollapsed_upper_bound += o->uncollapsed_upper_bound;
509  max_possible = max(max_possible, o->max_possible);
510  if (o->max_attained > max_attained) {
511  max_attained = o->max_attained;
512  percent_scale_factor = o->percent_scale_factor;
513  }
514 }
515 
516 string
518 {
519  string result;
520 
521  result += serialise_double(max_possible);
522  result += serialise_double(max_attained);
523 
524  result += serialise_double(percent_scale_factor);
525 
526  pack_uint(result, first);
527  // Send back the raw matches_* values. MSet::get_matches_estimated()
528  // rounds the estimate lazily, but when we merge MSet objects we really
529  // want to merge based on the raw estimates.
530  //
531  // It is also cleaner that a round-trip through serialisation gives you an
532  // object which is as close to the original as possible.
533  pack_uint(result, matches_lower_bound);
534  pack_uint(result, matches_estimated);
535  pack_uint(result, matches_upper_bound);
536  pack_uint(result, uncollapsed_lower_bound);
537  pack_uint(result, uncollapsed_estimated);
538  pack_uint(result, uncollapsed_upper_bound);
539 
540  pack_uint(result, items.size());
541  for (auto&& item : items) {
542  result += serialise_double(item.get_weight());
543  pack_uint(result, item.get_docid());
544  pack_string(result, item.get_sort_key());
545  pack_string(result, item.get_collapse_key());
546  pack_uint(result, item.get_collapse_count());
547  }
548 
549  if (stats)
550  result += serialise_stats(*stats);
551 
552  return result;
553 }
554 
555 void
556 MSet::Internal::unserialise(const char * p, const char * p_end)
557 {
558  items.clear();
559 
560  max_possible = unserialise_double(&p, p_end);
561  max_attained = unserialise_double(&p, p_end);
562 
563  percent_scale_factor = unserialise_double(&p, p_end);
564 
565  size_t msize;
566  if (!unpack_uint(&p, p_end, &first) ||
567  !unpack_uint(&p, p_end, &matches_lower_bound) ||
568  !unpack_uint(&p, p_end, &matches_estimated) ||
569  !unpack_uint(&p, p_end, &matches_upper_bound) ||
570  !unpack_uint(&p, p_end, &uncollapsed_lower_bound) ||
571  !unpack_uint(&p, p_end, &uncollapsed_estimated) ||
572  !unpack_uint(&p, p_end, &uncollapsed_upper_bound) ||
573  !unpack_uint(&p, p_end, &msize)) {
575  }
576  for ( ; msize; --msize) {
577  double wt = unserialise_double(&p, p_end);
578  Xapian::docid did;
579  string sort_key, key;
580  Xapian::doccount collapse_cnt;
581  if (!unpack_uint(&p, p_end, &did) ||
582  !unpack_string(&p, p_end, sort_key) ||
583  !unpack_string(&p, p_end, key) ||
584  !unpack_uint(&p, p_end, &collapse_cnt)) {
586  }
587  items.emplace_back(wt, did, std::move(key), collapse_cnt,
588  std::move(sort_key));
589  }
590 
591  if (p != p_end) {
592  stats.reset(new Xapian::Weight::Internal());
593  unserialise_stats(p, p_end, *stats);
594  }
595 }
596 
597 string
599 {
600  string desc = "MSet(matches_lower_bound=";
601  desc += str(matches_lower_bound);
602  desc += ", matches_estimated=";
603  desc += str(matches_estimated);
604  desc += ", matches_upper_bound=";
605  desc += str(matches_upper_bound);
606  if (uncollapsed_lower_bound != matches_lower_bound) {
607  desc += ", uncollapsed_lower_bound=";
608  desc += str(uncollapsed_lower_bound);
609  }
610  if (uncollapsed_estimated != matches_estimated) {
611  desc += ", uncollapsed_estimated=";
612  desc += str(uncollapsed_estimated);
613  }
614  if (uncollapsed_upper_bound != matches_upper_bound) {
615  desc += ", uncollapsed_upper_bound=";
616  desc += str(uncollapsed_upper_bound);
617  }
618  if (first != 0) {
619  desc += ", first=";
620  desc += str(first);
621  }
622  if (max_possible > 0) {
623  desc += ", max_possible=";
624  desc += str(max_possible);
625  }
626  if (max_attained > 0) {
627  desc += ", max_attained=";
628  desc += str(max_attained);
629  }
630  desc += ", [";
631  bool comma = false;
632  for (auto&& item : items) {
633  if (comma) {
634  desc += ", ";
635  } else {
636  comma = true;
637  }
638  desc += item.get_description();
639  }
640  desc += "])";
641  return desc;
642 }
643 
644 }
A result in an MSet.
Definition: result.h:30
Class for storing the results returned by the Clusterer.
Definition: cluster.h:452
Xapian::doccount size() const
Return the number of clusters.
Class for calculating the cosine distance between two documents.
Definition: cluster.h:538
double similarity(const PointType &a, const PointType &b) const override
Calculates and returns the cosine similarity using the formula cos(theta) = a.b/(|a|*|b|)
Class representing a document.
Definition: document.h:64
Xapian::Internal::intrusive_ptr_nonnull< Internal > internal
Definition: document.h:67
LCD clusterer: This clusterer implements the LCD clustering algorithm adapted from Modelling efficien...
Definition: cluster.h:662
ClusterSet cluster(const MSet &mset) override
Implements the LCD clustering algorithm.
Xapian::MSet internals.
Definition: msetinternal.h:44
Xapian::doccount uncollapsed_upper_bound
Definition: msetinternal.h:76
std::string serialise() const
Serialise this object.
Definition: mset.cc:517
int convert_to_percent(double weight) const
Definition: mset.cc:449
Xapian::Internal::intrusive_ptr< const Enquire::Internal > enquire
Definition: msetinternal.h:64
std::unordered_map< std::string, double > snippet_bg_relevance
Relevance weights for non-query terms for generating snippets.
Definition: msetinternal.h:56
std::string get_description() const
Return a string describing this object.
Definition: mset.cc:598
std::unique_ptr< Xapian::Weight::Internal > stats
For looking up query term frequencies and weights.
Definition: msetinternal.h:62
std::vector< Result > items
The items in the MSet.
Definition: msetinternal.h:59
Xapian::doccount uncollapsed_lower_bound
Definition: msetinternal.h:72
Xapian::doccount matches_estimated
Definition: msetinternal.h:68
void unshard_docids(Xapian::doccount shard, Xapian::doccount n_shards)
Definition: mset.cc:482
void unserialise(const char *p, const char *p_end)
Unserialise a serialised Xapian::MSet::Internal object.
Definition: mset.cc:556
Xapian::Document get_document(Xapian::doccount index) const
Definition: mset.cc:400
void merge_stats(const Internal *o, bool collapsing)
Definition: mset.cc:491
void fetch(Xapian::doccount first, Xapian::doccount last) const
Definition: mset.cc:414
void set_item_weight(Xapian::doccount i, double weight)
Definition: mset.cc:431
Xapian::doccount matches_lower_bound
Definition: msetinternal.h:66
double percent_scale_factor
Scale factor to convert weights to percentages.
Definition: msetinternal.h:85
Xapian::doccount matches_upper_bound
Definition: msetinternal.h:70
Xapian::doccount uncollapsed_estimated
Definition: msetinternal.h:74
Class representing a list of search results.
Definition: mset.h:46
Xapian::Internal::intrusive_ptr_nonnull< Internal > internal
Definition: mset.h:78
Xapian::doccount get_termfreq(std::string_view term) const
Get the termfreq of a term.
Definition: mset.cc:281
void sort_by_relevance()
Sorts the list of documents in MSet according to their weights.
Definition: mset.cc:268
void set_item_weight(Xapian::doccount i, double wt)
Update the weight corresponding to the document indexed at position i with wt.
Definition: mset.cc:72
Xapian::doccount size() const
Return number of items in this MSet object.
Definition: mset.cc:374
MSet()
Default constructor.
Definition: mset.cc:59
double get_max_possible() const
The maximum possible weight any document could achieve.
Definition: mset.cc:368
void fetch_(Xapian::doccount first, Xapian::doccount last) const
Definition: mset.cc:66
Xapian::doccount get_uncollapsed_matches_upper_bound() const
Upper bound on the total number of matching documents before collapsing.
Definition: mset.cc:356
friend class MSetIterator
Definition: mset.h:47
Xapian::doccount get_uncollapsed_matches_estimated() const
Estimate of the total number of matching documents before collapsing.
Definition: mset.cc:346
Xapian::doccount get_uncollapsed_matches_lower_bound() const
Lower bound on the total number of matching documents before collapsing.
Definition: mset.cc:340
int convert_to_percent(double weight) const
Convert a weight to a percentage.
Definition: mset.cc:275
std::string get_description() const
Return a string describing this object.
Definition: mset.cc:394
~MSet()
Destructor.
Definition: mset.cc:63
Xapian::doccount get_firstitem() const
Rank of first item in this MSet.
Definition: mset.cc:312
double get_termweight(std::string_view term) const
Get the term weight of a term.
Definition: mset.cc:300
Xapian::doccount get_matches_upper_bound() const
Upper bound on the total number of matching documents.
Definition: mset.cc:334
MSetIterator begin() const
Return iterator pointing to the first item in this MSet.
Definition: mset.h:786
std::string snippet(std::string_view text, size_t length=500, const Xapian::Stem &stemmer=Xapian::Stem(), unsigned flags=SNIPPET_BACKGROUND_MODEL|SNIPPET_EXHAUSTIVE, std::string_view hi_start="<b>", std::string_view hi_end="</b>", std::string_view omit="...") const
Generate a snippet.
Definition: mset.cc:380
double get_max_attained() const
The maximum weight attained by any document.
Definition: mset.cc:362
Xapian::doccount get_matches_lower_bound() const
Lower bound on the total number of matching documents.
Definition: mset.cc:318
MSetIterator end() const
Return iterator pointing to just after the last item in this MSet.
Definition: mset.h:791
Xapian::doccount get_matches_estimated() const
Estimate of the total number of matching documents.
Definition: mset.cc:324
Class to represent a document as a point in the Vector Space Model.
Definition: cluster.h:320
RangeError indicates an attempt to access outside the bounds of a container.
Definition: error.h:959
Class representing a stemming algorithm.
Definition: stem.h:74
Class to hold statistics for a given collection.
#define usual(COND)
Definition: config.h:608
#define rare(COND)
Definition: config.h:607
string term
PositionList * p
Xapian::termpos pos
Append a string to an object description, escaping invalid UTF-8.
Abstract base class for a document.
Class representing a list of search results.
MSetCmp get_msetcmp_function(Xapian::Enquire::Internal::sort_setting sort_by, bool sort_forward, bool sort_val_reverse)
Select the appropriate msetcmp function.
Definition: msetcmp.cc:100
Result comparison functions.
Xapian::MSet internals.
void sort(_RandomAccessIterator first, _RandomAccessIterator last, _Compare comp)
Definition: heap.h:277
string str(int value)
Convert int to std::string.
Definition: str.cc:91
The Xapian namespace contains public interfaces for the Xapian library.
Definition: compactor.cc:82
unsigned XAPIAN_DOCID_BASE_TYPE doccount
A count of documents.
Definition: types.h:37
unsigned XAPIAN_DOCID_BASE_TYPE docid
A unique identifier for a document.
Definition: types.h:51
Various assertion macros.
#define AssertRel(A, REL, B)
Definition: omassert.h:123
#define Assert(COND)
Definition: omassert.h:122
void unpack_throw_serialisation_error(const char *p)
Throw appropriate SerialisationError.
Definition: pack.cc:29
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
Round a bounded estimate to an appropriate number of S.F.
Xapian::doccount round_estimate(T lb, T ub, T est)
Round a bounded estimate to an appropriate number of S.F.
Definition: roundestimate.h:37
string serialise_double(double v)
Serialise a double to a string.
double unserialise_double(const char **p, const char *end)
Unserialise a double serialised by serialise_double.
functions to serialise and unserialise a double
string serialise_stats(const Xapian::Weight::Internal &stats)
Serialise a stats object.
Definition: serialise.cc:42
void unserialise_stats(const char *p, const char *p_end, Xapian::Weight::Internal &stat)
Unserialise a serialised stats object.
Definition: serialise.cc:92
functions to convert classes to strings and back
static Xapian::Stem stemmer
Definition: stemtest.cc:42
Convert types to std::string.