xapian-core  2.1.0
localsubmatch.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2006-2026 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 "localsubmatch.h"
25 
27 #include "backends/leafpostlist.h"
28 #include "clamp_cast.h"
29 #include "debuglog.h"
30 #include "extraweightpostlist.h"
31 #include "omassert.h"
32 #include "queryoptimiser.h"
33 #include "synonympostlist.h"
34 #include "api/termlist.h"
35 #include "weight/weightinternal.h"
36 
37 #include "xapian/error.h"
38 
39 #include <memory>
40 #include <string>
41 
42 using namespace std;
43 
50 class LazyWeight : public Xapian::Weight {
52 
54 
56 
58 
60 
61  double factor;
62 
64 
65  LazyWeight* clone() const override;
66 
67  void init(double factor_) override;
68 
69  public:
71  Xapian::Weight * real_wt_,
72  Xapian::Weight::Internal * stats_,
73  Xapian::termcount qlen_,
74  Xapian::termcount wqf__,
75  double factor_,
76  const Xapian::Database::Internal* shard_)
77  : pl(pl_),
78  real_wt(real_wt_),
79  stats(stats_),
80  qlen(qlen_),
81  wqf(wqf__),
82  factor(factor_),
83  shard(shard_)
84  { }
85 
86  std::string name() const override;
87 
88  std::string serialise() const override;
89  LazyWeight* unserialise(const std::string& serialised) const override;
90 
91  double get_sumpart(Xapian::termcount wdf,
92  Xapian::termcount doclen,
93  Xapian::termcount uniqterms,
94  Xapian::termcount wdfdocmax) const override;
95  double get_maxpart() const override;
96 
97  double get_sumextra(Xapian::termcount doclen,
98  Xapian::termcount uniqterms,
99  Xapian::termcount wdfdocmax) const override;
100  double get_maxextra() const override;
101 };
102 
103 LazyWeight *
105 {
106  throw Xapian::InvalidOperationError("LazyWeight::clone()");
107 }
108 
109 void
110 LazyWeight::init(double factor_)
111 {
112  (void)factor_;
113  throw Xapian::InvalidOperationError("LazyWeight::init()");
114 }
115 
116 string
118 {
119  string desc = "LazyWeight(";
120  desc += real_wt->name();
121  desc += ")";
122  return desc;
123 }
124 
125 string
127 {
128  throw Xapian::InvalidOperationError("LazyWeight::serialise()");
129 }
130 
131 LazyWeight *
132 LazyWeight::unserialise(const string &) const
133 {
134  throw Xapian::InvalidOperationError("LazyWeight::unserialise()");
135 }
136 
137 double
139  Xapian::termcount doclen,
140  Xapian::termcount uniqterms,
141  Xapian::termcount wdfdocmax) const
142 {
143  (void)wdf;
144  (void)doclen;
145  (void)uniqterms;
146  (void)wdfdocmax;
147  throw Xapian::InvalidOperationError("LazyWeight::get_sumpart()");
148 }
149 
150 double
152  Xapian::termcount uniqterms,
153  Xapian::termcount wdfdocmax) const
154 {
155  (void)doclen;
156  (void)uniqterms;
157  (void)wdfdocmax;
158  throw Xapian::InvalidOperationError("LazyWeight::get_sumextra()");
159 }
160 
161 double
163 {
164  // This gets called first for the case we care about.
165  return pl->resolve_lazy_termweight(real_wt, stats, qlen, wqf, factor, shard);
166 }
167 
168 double
170 {
171  throw Xapian::InvalidOperationError("LazyWeight::get_maxextra()");
172 }
173 
176  Xapian::termcount * total_subqs_ptr)
177 {
178  LOGCALL(MATCH, PostList *, "LocalSubMatch::get_postlist", matcher | total_subqs_ptr);
179 
180  if (query.empty() || db->get_doccount() == 0)
181  return {nullptr, nullptr}; // MatchNothing
182 
183  // Build the postlist tree for the query. This calls
184  // LocalSubMatch::open_post_list() for each term in the query.
185  PostListAndEstimate plest;
186  {
187  QueryOptimiser opt(*db, *this, matcher, shard_index);
188  double factor = wt_factory.is_bool_weight_() ? 0.0 : 1.0;
189  plest = query.internal->postlist(&opt, factor, NULL);
190  *total_subqs_ptr = opt.get_total_subqs();
191  }
192 
193  if (plest.pl) {
194  unique_ptr<Xapian::Weight> extra_wt(wt_factory.clone());
195  // Only uses term-independent stats.
196  extra_wt->init_(*total_stats, qlen, db);
197  if (extra_wt->get_maxextra() != 0.0) {
198  // There's a term-independent weight contribution, so we combine
199  // the postlist tree with an ExtraWeightPostList which adds in this
200  // contribution.
201  plest.pl = new ExtraWeightPostList(plest.pl, extra_wt.release(),
202  matcher);
203  }
204  }
205 
206  return plest;
207 }
208 
211  PostListAndEstimate or_pl,
212  double factor,
213  const TermFreqs& termfreqs)
214 {
215  LOGCALL(MATCH, PostListAndEstimate, "LocalSubMatch::make_synonym_postlist", pltree | or_pl | factor | termfreqs);
216  bool needs_doclen = wt_factory.get_sumpart_needs_doclength_();
217  unique_ptr<SynonymPostList> res(new SynonymPostList(or_pl.pl, pltree,
218  needs_doclen));
219  unique_ptr<Xapian::Weight> wt(wt_factory.clone());
220 
221  // We shortcut an empty shard and avoid creating a postlist tree for it,
222  // and all shards must be empty for collection_size to be zero.
223  Assert(total_stats->collection_size);
224  wt->init_(*total_stats, qlen, factor,
225  termfreqs.termfreq, termfreqs.reltermfreq, termfreqs.collfreq,
226  db);
227 
228  res->set_weight(wt.release());
229  RETURN({res.release(), std::move(or_pl.est)});
230 }
231 
234  Xapian::termcount wqf,
235  double factor,
236  bool need_positions,
237  bool compound_weight,
238  QueryOptimiser* qopt,
239  bool lazy_weight,
240  TermFreqs* termfreqs)
241 {
242  LOGCALL(MATCH, PostListAndEstimate, "LocalSubMatch::open_post_list", term | wqf | factor | need_positions | qopt | lazy_weight | termfreqs);
243 
244  bool weighted = false;
245 
246  LeafPostList * pl = NULL;
247  if (term.empty()) {
248  Assert(!need_positions);
249  pl = db->open_leaf_post_list(term, false);
250  } else {
251  weighted = (factor != 0.0);
252  const LeafPostList* hint = qopt->get_hint_postlist();
253  if (!hint || !hint->open_nearby_postlist(term, need_positions, pl)) {
254  pl = db->open_leaf_post_list(term, need_positions);
255  }
256  if (pl) qopt->set_hint_postlist(pl);
257  if (pl && !need_positions) {
258  bool need_wdf = (weighted || compound_weight) &&
259  wt_factory.get_sumpart_needs_wdf_();
260  if (!need_wdf && pl->get_termfreq() == qopt->db_size) {
261  // If we're not going to use the wdf or term positions, and the
262  // term indexes all documents, we can replace it with the
263  // MatchAll postlist, which is especially efficient if there
264  // are no gaps in the docids.
265  //
266  // We opened the real PostList already as that's more efficient
267  // than asking the Database for the termfreq in the common case
268  // when the term doesn't index all documents, and is similar
269  // work in the case where it does (for glass, there's the extra
270  // overhead of creating a cursor but we still need to read and
271  // decode the same data).
272  //
273  // The real PostList got set as the QueryOptimiser's hint above
274  // so we can just hand ownership of it to the QueryOptimiser.
275  qopt->own_hint_postlist();
276  pl = db->open_leaf_post_list(string(), false);
277  // We shortcut an empty shard and avoid creating a postlist
278  // tree for it, so an alldocs postlist can't be NULL here.
279  Assert(pl);
280 
281  // Set the term name so the postlist looks up the correct term
282  // frequencies - this is necessary if the weighting scheme
283  // needs collection frequency or reltermfreq (termfreq would be
284  // correct anyway since it's just the collection size in this
285  // case).
286  pl->set_term(term);
287  }
288  }
289  }
290 
291  if (pl && weighted) {
292  Xapian::Weight * wt = wt_factory.clone();
293  if (!lazy_weight) {
294  wt->init_(*total_stats, qlen, term, wqf, factor, db, pl);
295  if (pl->get_termfreq() > 0)
296  total_stats->set_max_part(term, wt->get_maxpart());
297  } else {
298  // Delay initialising the actual weight object, so that we can
299  // gather stats for the terms lazily expanded from a wildcard
300  // (needed for the remote database case).
301  wt = new LazyWeight(pl, wt, total_stats, qlen, wqf, factor, db);
302  }
303  pl->set_termweight(wt);
304  }
305 
306  if (termfreqs) {
307  if (term.empty()) {
308  auto cf = clamp_cast<Xapian::termcount>(total_stats->total_length);
309  *termfreqs = TermFreqs(total_stats->collection_size,
310  total_stats->rset_size,
311  cf);
312  } else if (!lazy_weight) {
313  auto i = total_stats->termfreqs.find(term);
314  Assert(i != total_stats->termfreqs.end());
315  *termfreqs = i->second;
316  }
317  }
318 
319  if (!pl) {
320  RETURN({nullptr, nullptr});
321  }
322 
323  Xapian::docid first = 1, last = Xapian::docid(-1);
324  pl->get_docid_range(first, last);
325 
326  EstimateOp* est = nullptr;
327  if (!qopt->get_no_estimates())
328  est = new EstimateOp(pl->get_termfreq(), first, last);
329  RETURN({pl, est});
330 }
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:62
char name[9]
Definition: dbcheck.cc:57
Cast a value to a type, clamping out of range values.
Class for estimating the total number of matching documents.
Definition: estimateop.h:64
PostList which adds on a term-independent weight contribution.
Xapian::Weight subclass which adds laziness.
double get_sumextra(Xapian::termcount doclen, Xapian::termcount uniqterms, Xapian::termcount wdfdocmax) const override
Calculate the term-independent weight component for a document.
double factor
double get_sumpart(Xapian::termcount wdf, Xapian::termcount doclen, Xapian::termcount uniqterms, Xapian::termcount wdfdocmax) const override
Calculate the weight contribution for this object's term to a document.
std::string name() const override
Return the name of this weighting scheme, e.g.
double get_maxextra() const override
Return an upper bound on what get_sumextra() can return for any document.
std::string serialise() const override
Return this object's parameters serialised as a single string.
const Xapian::Database::Internal * shard
LazyWeight * unserialise(const std::string &serialised) const override
Unserialise parameters.
LazyWeight(LeafPostList *pl_, Xapian::Weight *real_wt_, Xapian::Weight::Internal *stats_, Xapian::termcount qlen_, Xapian::termcount wqf__, double factor_, const Xapian::Database::Internal *shard_)
LazyWeight * clone() const override
Clone this object.
Xapian::Weight::Internal * stats
Xapian::termcount qlen
LeafPostList * pl
double get_maxpart() const override
Return an upper bound on what get_sumpart() can return for any document.
Xapian::termcount wqf
Xapian::Weight * real_wt
void init(double factor_) override
Allow the subclass to perform any initialisation it needs to.
Abstract base class for leaf postlists.
Definition: leafpostlist.h:40
void set_term(std::string_view term_)
Set the term name.
Definition: leafpostlist.h:155
void set_termweight(const Xapian::Weight *weight_)
Set the weighting scheme to use during matching.
Definition: leafpostlist.h:81
virtual bool open_nearby_postlist(std::string_view term_, bool need_read_pos, LeafPostList *&pl) const
Open another postlist from the same database.
Definition: leafpostlist.cc:69
PostListAndEstimate open_post_list(const std::string &term, Xapian::termcount wqf, double factor, bool need_positions, bool compound_weight, Xapian::Internal::QueryOptimiser *qopt, bool lazy_weight, TermFreqs *termfreqs)
PostListAndEstimate get_postlist(PostListTree *matcher, Xapian::termcount *total_subqs_ptr)
Get PostList.
PostListAndEstimate make_synonym_postlist(PostListTree *pltree, PostListAndEstimate or_pl, double factor, const TermFreqs &termfreqs)
Convert a postlist into a synonym postlist.
A postlist comprising several postlists SYNONYMed together.
Virtual base class for Database internals.
Abstract base class for postlists.
Definition: postlist.h:40
Xapian::doccount get_termfreq() const
Get an estimate of the number of documents this PostList will return.
Definition: postlist.h:67
virtual void get_docid_range(docid &first, docid &last) const
Get the bounds on the range of docids this PostList can return.
Definition: postlist.cc:72
Xapian::termcount get_total_subqs() const
void set_hint_postlist(LeafPostList *new_hint)
const LeafPostList * get_hint_postlist() const
InvalidOperationError indicates the API was used in an invalid way.
Definition: error.h:271
bool empty() const noexcept
Check if this query is Xapian::Query::MatchNothing.
Definition: query.h:661
Xapian::Internal::intrusive_ptr< Internal > internal
Definition: query.h:48
Class to hold statistics for a given collection.
Abstract base class for weighting schemes.
Definition: weight.h:38
virtual Weight * clone() const =0
Clone this object.
virtual double get_maxpart() const =0
Return an upper bound on what get_sumpart() can return for any document.
void init_(const Internal &stats, Xapian::termcount query_len_, const std::string &term, Xapian::termcount wqf_, double factor, const Xapian::Database::Internal *shard, void *postlist)
Definition: weight.cc:76
string term
Virtual base class for Database internals.
Debug logging macros.
#define RETURN(...)
Definition: debuglog.h:484
#define LOGCALL(CATEGORY, TYPE, FUNC, PARAMS)
Definition: debuglog.h:478
Hierarchy of classes which Xapian can throw as exceptions.
PostList which adds on a term-independent weight contribution.
Abstract base class for leaf postlists.
SubMatch class for a local database.
unsigned XAPIAN_TERMCOUNT_BASE_TYPE termcount
A counts of terms.
Definition: types.h:64
unsigned XAPIAN_DOCID_BASE_TYPE docid
A unique identifier for a document.
Definition: types.h:51
Various assertion macros.
#define Assert(COND)
Definition: omassert.h:122
Details passed around while building PostList tree from Query tree.
std::unique_ptr< EstimateOp > est
Definition: estimateop.h:217
The frequencies for a term.
Xapian::doccount reltermfreq
Xapian::termcount collfreq
Combine subqueries, weighting as if they are synonyms.
Abstract base class for termlists.
Xapian::Weight::Internal class, holding database and term statistics.