1 /** @file
2  * @brief Xapian::Query API class
3  */
4 /* Copyright (C) 2011,2012,2013,2014,2015,2016,2017,2018,2019 Olly Betts
5  * Copyright (C) 2008 Richard Boulton
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (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, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  */
21 
22 #ifndef XAPIAN_INCLUDED_QUERY_H
23 #define XAPIAN_INCLUDED_QUERY_H
24 
25 #if !defined XAPIAN_IN_XAPIAN_H && !defined XAPIAN_LIB_BUILD
26 # error Never use <xapian/query.h> directly; include <xapian.h> instead.
27 #endif
28 
29 #include <string>
30 
31 #include <xapian/attributes.h>
32 #include <xapian/intrusive_ptr.h>
33 #include <xapian/postingiterator.h>
34 #include <xapian/registry.h>
35 #include <xapian/termiterator.h>
36 #include <xapian/types.h>
37 #include <xapian/visibility.h>
38 
39 class QueryOptimiser; // FIXME
40 
41 namespace Xapian {
42 
43 class PostingSource;
44 
45 /// Class representing a query.
46 class XAPIAN_VISIBILITY_DEFAULT Query {
47   public:
48     /// Class representing the query internals.
49     class Internal;
50     /// @private @internal Reference counted internals.
51     Xapian::Internal::intrusive_ptr<Internal> internal;
52 
53     /** A query matching no documents.
54      *
55      *  This is a static instance of a default-constructed Xapian::Query
56      *  object.  It is safe to use concurrently from different threads,
57      *  unlike @a MatchAll (this is because MatchNothing has a NULL
58      *  internal object so there's no reference counting happening).
59      *
60      *  When combined with other Query objects using the various supported
61      *  operators, MatchNothing works like @c false in boolean logic, so
62      *  <code>MatchNothing & q</code> is @c MatchNothing, while
63      *  <code>MatchNothing | q</code> is @c q.
64      */
65     static const Xapian::Query MatchNothing;
66 
67     /** A query matching all documents.
68      *
69      *  This is a static instance of <code>Xapian::Query(std::string())</code>.
70      *  If you are constructing Query objects which use @a MatchAll in
71      *  different threads then the reference counting of the static object can
72      *  get messed up by concurrent access so you should instead use
73      *  <code>Xapian::Query(std::string())</code> directly.
74      */
75     static const Xapian::Query MatchAll;
76 
77     /** Query operators. */
78     enum op {
79 	/** Match only documents which all subqueries match.
80 	 *
81 	 *  When used in a weighted context, the weight is the sum of the
82 	 *  weights for all the subqueries.
83 	 */
84 	OP_AND = 0,
85 
86 	/** Match documents which at least one subquery matches.
87 	 *
88 	 *  When used in a weighted context, the weight is the sum of the
89 	 *  weights for matching subqueries (so additional matching subqueries
90 	 *  will mean a higher weight).
91 	 */
92 	OP_OR = 1,
93 
94 	/** Match documents which the first subquery matches but no others do.
95 	 *
96 	 *  When used in a weighted context, the weight is just the weight of
97 	 *  the first subquery.
98 	 */
99 	OP_AND_NOT = 2,
100 
101 	/** Match documents which an odd number of subqueries match.
102 	 *
103 	 *  When used in a weighted context, the weight is the sum of the
104 	 *  weights for matching subqueries (so additional matching subqueries
105 	 *  will mean a higher weight).
106 	 */
107 	OP_XOR = 3,
108 
109 	/** Match the first subquery taking extra weight from other subqueries.
110 	 *
111 	 *  When used in a weighted context, the weight is the sum of the
112 	 *  weights for matching subqueries (so additional matching subqueries
113 	 *  will mean a higher weight).
114 	 *
115 	 *  Because only the first subquery determines which documents are
116 	 *  matched, in a non-weighted context only the first subquery matters.
117 	 */
118 	OP_AND_MAYBE = 4,
119 
120 	/** Match like OP_AND but only taking weight from the first subquery.
121 	 *
122 	 *  When used in a non-weighted context, OP_FILTER and OP_AND are
123 	 *  equivalent.
124 	 *
125 	 *  In older 1.4.x, the third and subsequent subqueries were ignored
126 	 *  in some situations.  This was fixed in 1.4.15.
127 	 */
128 	OP_FILTER = 5,
129 
130 	/** Match only documents where all subqueries match near each other.
131 	 *
132 	 *  The subqueries must match at term positions within the specified
133 	 *  window size, in any order.
134 	 *
135 	 *  Currently subqueries must be terms or terms composed with OP_OR.
136 	 *
137 	 *  When used in a weighted context, the weight is the sum of the
138 	 *  weights for all the subqueries.
139 	 */
140 	OP_NEAR = 6,
141 
142 	/** Match only documents where all subqueries match near and in order.
143 	 *
144 	 *  The subqueries must match at term positions within the specified
145 	 *  window size, in the same term position order as subquery order.
146 	 *
147 	 *  Currently subqueries must be terms or terms composed with OP_OR.
148 	 *
149 	 *  When used in a weighted context, the weight is the sum of the
150 	 *  weights for all the subqueries.
151 	 */
152 	OP_PHRASE = 7,
153 
154 	/** Match only documents where a value slot is within a given range.
155 	 *
156 	 *  This operator never contributes weight.
157 	 */
158 	OP_VALUE_RANGE = 8,
159 
160 	/** Scale the weight contributed by a subquery.
161 	 *
162 	 *  The weight is the weight of the subquery multiplied by the
163 	 *  specified non-negative scale factor (so if the scale factor is
164 	 *  zero then the subquery contributes no weight).
165 	 */
166 	OP_SCALE_WEIGHT = 9,
167 
168 	/** Pick the best N subqueries and combine with OP_OR.
169 	 *
170 	 *  If you want to implement a feature which finds documents similar to
171 	 *  a piece of text, an obvious approach is to build an "OR" query from
172 	 *  all the terms in the text, and run this query against a database
173 	 *  containing the documents.  However such a query can contain a lots
174 	 *  of terms and be quite slow to perform, yet many of these terms
175 	 *  don't contribute usefully to the results.
176 	 *
177 	 *  The OP_ELITE_SET operator can be used instead of OP_OR in this
178 	 *  situation.  OP_ELITE_SET selects the most important ''N'' terms and
179 	 *  then acts as an OP_OR query with just these, ignoring any other
180 	 *  terms.  This will usually return results just as good as the full
181 	 *  OP_OR query, but much faster.
182 	 *
183 	 *  In general, the OP_ELITE_SET operator can be used when you have a
184 	 *  large OR query, but it doesn't matter if the search completely
185 	 *  ignores some of the less important terms in the query.
186 	 *
187 	 *  The subqueries don't have to be terms, but if they aren't then
188 	 *  OP_ELITE_SET will look at the estimated frequencies of the
189 	 *  subqueries and so could pick a subset which don't actually
190 	 *  match any documents even if the full OR would match some.
191 	 *
192 	 *  You can specify a parameter to the query constructor which control
193 	 *  the number of terms which OP_ELITE_SET will pick.  If not
194 	 *  specified, this defaults to 10 (Xapian used to default to
195 	 *  <code>ceil(sqrt(number_of_subqueries))</code> if there are more
196 	 *  than 100 subqueries, but this rather arbitrary special case was
197 	 *  dropped in 1.3.0).  For example, this will pick the best 7 terms:
198 	 *
199 	 *  <pre>
200 	 *  Xapian::Query query(Xapian::Query::OP_ELITE_SET, subqs.begin(), subqs.end(), 7);
201 	 *  </pre>
202 	 *
203 	 * If the number of subqueries is less than this threshold,
204 	 * OP_ELITE_SET behaves identically to OP_OR.
205 	 */
206 	OP_ELITE_SET = 10,
207 
208 	/** Match only documents where a value slot is >= a given value.
209 	 *
210 	 *  Similar to @a OP_VALUE_RANGE, but open-ended.
211 	 *
212 	 *  This operator never contributes weight.
213 	 */
214 	OP_VALUE_GE = 11,
215 
216 	/** Match only documents where a value slot is <= a given value.
217 	 *
218 	 *  Similar to @a OP_VALUE_RANGE, but open-ended.
219 	 *
220 	 *  This operator never contributes weight.
221 	 */
222 	OP_VALUE_LE = 12,
223 
224 	/** Match like OP_OR but weighting as if a single term.
225 	 *
226 	 *  The weight is calculated combining the statistics for the
227 	 *  subqueries to approximate the weight of a single term occurring
228 	 *  with those statistics.
229 	 */
230 	OP_SYNONYM = 13,
231 
232 	/** Pick the maximum weight of any subquery.
233 	 *
234 	 *  Matches the same documents as @a OP_OR, but the weight contributed
235 	 *  is the maximum weight from any matching subquery (for OP_OR, it's
236 	 *  the sum of the weights from the matching subqueries).
237 	 *
238 	 *  Added in Xapian 1.3.2.
239 	 */
240 	OP_MAX = 14,
241 
242 	/** Wildcard expansion.
243 	 *
244 	 *  Added in Xapian 1.3.3.
245 	 */
246 	OP_WILDCARD = 15,
247 
248 	/** Construct an invalid query.
249 	 *
250 	 *  This can be useful as a placeholder - for example @a RangeProcessor
251 	 *  uses it as a return value to indicate that a range hasn't been
252 	 *  recognised.
253 	 */
254 	OP_INVALID = 99,
255 
256 	/** Value returned by get_type() for a term. */
257 	LEAF_TERM = 100,
258 
259 	/** Value returned by get_type() for a PostingSource. */
260 	LEAF_POSTING_SOURCE,
261 
262 	/** Value returned by get_type() for MatchAll or equivalent.
263 	 *
264 	 *  This is returned for any <code>Xapian::Query(std::string())</code>
265 	 *  object.
266 	 */
267 	LEAF_MATCH_ALL,
268 
269 	/** Value returned by get_type() for MatchNothing or equivalent.
270 	 *
271 	 *  This is returned for any <code>Xapian::Query()</code> object.
272 	 */
273 	LEAF_MATCH_NOTHING
274     };
275 
276     enum {
277 	/** Throw an error if OP_WILDCARD exceeds its expansion limit.
278 	 *
279 	 *  Xapian::WildcardError will be thrown when the query is actually
280 	 *  run.
281 	 */
282 	WILDCARD_LIMIT_ERROR,
283 	/** Stop expanding when OP_WILDCARD reaches its expansion limit.
284 	 *
285 	 *  This makes the wildcard expand to only the first N terms (sorted
286 	 *  by byte order).
287 	 */
288 	WILDCARD_LIMIT_FIRST,
289 	/** Limit OP_WILDCARD expansion to the most frequent terms.
290 	 *
291 	 *  If OP_WILDCARD would expand to more than its expansion limit, the
292 	 *  most frequent terms are taken.  This approach works well for cases
293 	 *  such as expanding a partial term at the end of a query string which
294 	 *  the user hasn't finished typing yet - as well as being less expense
295 	 *  to evaluate than the full expansion, using only the most frequent
296 	 *  terms tends to give better results too.
297 	 */
298 	WILDCARD_LIMIT_MOST_FREQUENT
299     };
300 
301     /** Construct a query matching no documents.
302      *
303      *  @a MatchNothing is a static instance of this.
304      *
305      *  When combined with other Query objects using the various supported
306      *  operators, <code>Query()</code> works like @c false in boolean logic,
307      *  so <code>Query() & q</code> is @c Query(), while
308      *  <code>Query() | q</code> is @c q.
309      */
XAPIAN_NOTHROW(Query ())310     XAPIAN_NOTHROW(Query()) { }
311 
312     /// Destructor.
~Query()313     ~Query() { }
314 
315     /** Copying is allowed.
316      *
317      *  The internals are reference counted, so copying is cheap.
318      */
Query(const Query & o)319     Query(const Query & o) : internal(o.internal) { }
320 
321     /** Copying is allowed.
322      *
323      *  The internals are reference counted, so assignment is cheap.
324      */
325     Query & operator=(const Query & o) { internal = o.internal; return *this; }
326 
327 #ifdef XAPIAN_MOVE_SEMANTICS
328     /// Move constructor.
329     Query(Query &&) = default;
330 
331     /// Move assignment operator.
332     Query & operator=(Query &&) = default;
333 #endif
334 
335     /** Construct a Query object for a term.
336      *
337      *  @param term The term.  An empty string constructs a query matching
338      *		    all documents (@a MatchAll is a static instance of this).
339      *  @param wqf  The within-query frequency. (default: 1)
340      *  @param pos  The query position.  Currently this is mainly used to
341      *		    determine the order of terms obtained via
342      *		    get_terms_begin(). (default: 0)
343      */
344     Query(const std::string & term,
345 	  Xapian::termcount wqf = 1,
346 	  Xapian::termpos pos = 0);
347 
348     /** Construct a Query object for a PostingSource. */
349     explicit Query(Xapian::PostingSource * source);
350 
351     /** Scale using OP_SCALE_WEIGHT.
352      *
353      *  @param factor Non-negative real number to multiply weights by.
354      *  @param subquery Query object to scale weights from.
355      */
356     Query(double factor, const Xapian::Query & subquery);
357 
358     /** Scale using OP_SCALE_WEIGHT.
359      *
360      *  In this form, the op_ parameter is totally redundant - use
361      *  Query(factor, subquery) in preference.
362      *
363      *  @param op_	Must be OP_SCALE_WEIGHT.
364      *  @param factor	Non-negative real number to multiply weights by.
365      *  @param subquery	Query object to scale weights from.
366      */
367     Query(op op_, const Xapian::Query & subquery, double factor);
368 
369     /** Construct a Query object by combining two others.
370      *
371      *  @param op_	The operator to combine the queries with.
372      *  @param a	First subquery.
373      *  @param b	Second subquery.
374      */
Query(op op_,const Xapian::Query & a,const Xapian::Query & b)375     Query(op op_, const Xapian::Query & a, const Xapian::Query & b)
376     {
377 	init(op_, 2);
378 	bool positional = (op_ == OP_NEAR || op_ == OP_PHRASE);
379 	add_subquery(positional, a);
380 	add_subquery(positional, b);
381 	done();
382     }
383 
384     /** Construct a Query object by combining two terms.
385      *
386      *  @param op_	The operator to combine the terms with.
387      *  @param a	First term.
388      *  @param b	Second term.
389      */
Query(op op_,const std::string & a,const std::string & b)390     Query(op op_, const std::string & a, const std::string & b)
391     {
392 	init(op_, 2);
393 	add_subquery(false, a);
394 	add_subquery(false, b);
395 	done();
396     }
397 
398     /** Construct a Query object for a single-ended value range.
399      *
400      *  @param op_		Must be OP_VALUE_LE or OP_VALUE_GE currently.
401      *  @param slot		The value slot to work over.
402      *  @param range_limit	The limit of the range.
403      */
404     Query(op op_, Xapian::valueno slot, const std::string & range_limit);
405 
406     /** Construct a Query object for a value range.
407      *
408      *  @param op_		Must be OP_VALUE_RANGE currently.
409      *  @param slot		The value slot to work over.
410      *  @param range_lower	Lower end of the range.
411      *  @param range_upper	Upper end of the range.
412      */
413     Query(op op_, Xapian::valueno slot,
414 	  const std::string & range_lower, const std::string & range_upper);
415 
416     /** Query constructor for OP_WILDCARD queries.
417      *
418      *  @param op_	Must be OP_WILDCARD
419      *  @param pattern	The wildcard pattern - currently this is just a string
420      *			and the wildcard expands to terms which start with
421      *			exactly this string.
422      *	@param max_expansion	The maximum number of terms to expand to
423      *				(default: 0, which means no limit)
424      *	@param max_type	How to enforce max_expansion - one of
425      *			@a WILDCARD_LIMIT_ERROR (the default),
426      *			@a WILDCARD_LIMIT_FIRST or
427      *			@a WILDCARD_LIMIT_MOST_FREQUENT.
428      *			When searching multiple databases, the expansion limit
429      *			is currently applied independently for each database,
430      *			so the total number of terms may be higher than the
431      *			limit.  This is arguably a bug, and may change in
432      *			future versions.
433      *	@param combiner The @a Query::op to combine the terms with - one of
434      *			@a OP_SYNONYM (the default), @a OP_OR or @a OP_MAX.
435      */
436     Query(op op_,
437 	  const std::string & pattern,
438 	  Xapian::termcount max_expansion = 0,
439 	  int max_type = WILDCARD_LIMIT_ERROR,
440 	  op combiner = OP_SYNONYM);
441 
442     /** Construct a Query object from a begin/end iterator pair.
443      *
444      *  Dereferencing the iterator should return a Xapian::Query, a non-NULL
445      *  Xapian::Query*, a std::string or a type which converts to one of
446      *  these (e.g. const char*).
447      *
448      *  If begin == end then there are no subqueries and the resulting Query
449      *  won't match anything.
450      *
451      *  @param op_	The operator to combine the queries with.
452      *  @param begin	Begin iterator.
453      *  @param end	End iterator.
454      *  @param window	Window size for OP_NEAR and OP_PHRASE, or 0 to use the
455      *			number of subqueries as the window size (default: 0).
456      */
457     template<typename I>
458     Query(op op_, I begin, I end, Xapian::termcount window = 0)
459     {
460 	if (begin != end) {
461 	    typedef typename std::iterator_traits<I>::iterator_category iterator_category;
462 	    init(op_, window, begin, end, iterator_category());
463 	    bool positional = (op_ == OP_NEAR || op_ == OP_PHRASE);
464 	    for (I i = begin; i != end; ++i) {
465 		add_subquery(positional, *i);
466 	    }
467 	    done();
468 	}
469     }
470 
471 #ifdef SWIG
472     // SWIG's %template doesn't seem to handle a templated ctor so we
473     // provide this fake specialised form of the above prototype.
474     Query(op op_, XapianSWIGQueryItor qbegin, XapianSWIGQueryItor qend,
475 	  Xapian::termcount parameter = 0);
476 
477 # ifdef SWIGJAVA
478     Query(op op_, XapianSWIGStrItor qbegin, XapianSWIGStrItor qend,
479 	  Xapian::termcount parameter = 0);
480 # endif
481 #endif
482 
483     /** Begin iterator for terms in the query object.
484      *
485      *  The iterator returns terms in ascending query position order, and
486      *  will return the same term in each unique position it occurs in.
487      *  If you want the terms in sorted order and without duplicates, see
488      *  get_unique_terms_begin().
489      */
490     const TermIterator get_terms_begin() const;
491 
492     /// End iterator for terms in the query object.
XAPIAN_NOTHROW(get_terms_end ()const)493     const TermIterator XAPIAN_NOTHROW(get_terms_end() const) {
494 	return TermIterator();
495     }
496 
497     /** Begin iterator for unique terms in the query object.
498      *
499      *  Terms are sorted and terms with the same name removed from the list.
500      *
501      *  If you want the terms in ascending query position order, see
502      *  get_terms_begin().
503      */
504     const TermIterator get_unique_terms_begin() const;
505 
506     /// End iterator for unique terms in the query object.
XAPIAN_NOTHROW(get_unique_terms_end ()const)507     const TermIterator XAPIAN_NOTHROW(get_unique_terms_end() const) {
508 	return TermIterator();
509     }
510 
511     /** Return the length of this query object. */
512     Xapian::termcount XAPIAN_NOTHROW(get_length() const) XAPIAN_PURE_FUNCTION;
513 
514     /** Check if this query is Xapian::Query::MatchNothing. */
XAPIAN_NOTHROW(empty ()const)515     bool XAPIAN_NOTHROW(empty() const) {
516 	return internal.get() == 0;
517     }
518 
519     /** Serialise this object into a string. */
520     std::string serialise() const;
521 
522     /** Unserialise a string and return a Query object.
523      *
524      *  @param serialised	the string to unserialise.
525      *  @param reg		Xapian::Registry object to use to unserialise
526      *				user-subclasses of Xapian::PostingSource
527      *				(default: standard registry).
528      */
529     static const Query unserialise(const std::string & serialised,
530 				   const Registry & reg = Registry());
531 
532     /** Get the type of the top level of the query. */
533     op XAPIAN_NOTHROW(get_type() const) XAPIAN_PURE_FUNCTION;
534 
535     /** Get the number of subqueries of the top level query. */
536     size_t XAPIAN_NOTHROW(get_num_subqueries() const) XAPIAN_PURE_FUNCTION;
537 
538     /** Read a top level subquery.
539       *
540       * @param n  Return the n-th subquery (starting from 0) - only valid when
541       *		  0 <= n < get_num_subqueries().
542       */
543     const Query get_subquery(size_t n) const;
544 
545     /// Return a string describing this object.
546     std::string get_description() const;
547 
548     /** Combine with another Xapian::Query object using OP_AND.
549      *
550      *  @since Since Xapian 1.4.10, when called on a Query object which is
551      *  OP_AND and has a reference count of 1, then @a o is appended as a new
552      *  subquery (provided @a o is a different Query object and
553      *  <code>!o.empty()</code>).
554      */
555     const Query operator&=(const Query & o);
556 
557     /** Combine with another Xapian::Query object using OP_OR.
558      *
559      *  @since Since Xapian 1.4.10, when called on a Query object which is
560      *  OP_OR and has a reference count of 1, then @a o is appended as a new
561      *  subquery (provided @a o is a different Query object and
562      *  <code>!o.empty()</code>).
563      */
564     const Query operator|=(const Query & o);
565 
566     /** Combine with another Xapian::Query object using OP_XOR.
567      *
568      *  @since Since Xapian 1.4.10, when called on a Query object which is
569      *  OP_XOR and has a reference count of 1, then @a o is appended as a new
570      *  subquery (provided @a o is a different Query object and
571      *  <code>!o.empty()</code>).
572      */
573     const Query operator^=(const Query & o);
574 
575     /** Scale using OP_SCALE_WEIGHT.
576      *
577      *  @param factor Non-negative real number to multiply weights by.
578      */
579     const Query operator*=(double factor) {
580 	return (*this = Query(factor, *this));
581     }
582 
583     /** Inverse scale using OP_SCALE_WEIGHT.
584      *
585      *  @param factor Positive real number to divide weights by.
586      */
587     const Query operator/=(double factor) {
588 	return (*this = Query(1.0 / factor, *this));
589     }
590 
591     /** @private @internal */
Query(Internal * internal_)592     explicit Query(Internal * internal_) : internal(internal_) { }
593 
594     /** Construct with just an operator.
595      *
596      *  @param op_ The operator to use - currently only OP_INVALID is useful.
597      */
Query(Query::op op_)598     explicit Query(Query::op op_) {
599 	init(op_, 0);
600 	if (op_ != Query::OP_INVALID) done();
601     }
602 
603   private:
604     void init(Query::op op_, size_t n_subqueries, Xapian::termcount window = 0);
605 
606     template<typename I>
init(Query::op op_,Xapian::termcount window,const I & begin,const I & end,std::random_access_iterator_tag)607     void init(Query::op op_, Xapian::termcount window,
608 	      const I & begin, const I & end, std::random_access_iterator_tag)
609     {
610 	init(op_, end - begin, window);
611     }
612 
613     template<typename I>
init(Query::op op_,Xapian::termcount window,const I &,const I &,std::input_iterator_tag)614     void init(Query::op op_, Xapian::termcount window,
615 	      const I &, const I &, std::input_iterator_tag)
616     {
617 	init(op_, 0, window);
618     }
619 
620     void add_subquery(bool positional, const Xapian::Query & subquery);
621 
add_subquery(bool,const std::string & subquery)622     void add_subquery(bool, const std::string & subquery) {
623 	add_subquery(false, Xapian::Query(subquery));
624     }
625 
add_subquery(bool positional,const Xapian::Query * subquery)626     void add_subquery(bool positional, const Xapian::Query * subquery) {
627 	// FIXME: subquery NULL?
628 	add_subquery(positional, *subquery);
629     }
630 
631     void done();
632 };
633 
634 /** Combine two Xapian::Query objects using OP_AND. */
635 inline const Query
636 operator&(const Query & a, const Query & b)
637 {
638     return Query(Query::OP_AND, a, b);
639 }
640 
641 /** Combine two Xapian::Query objects using OP_OR. */
642 inline const Query
643 operator|(const Query & a, const Query & b)
644 {
645     return Query(Query::OP_OR, a, b);
646 }
647 
648 /** Combine two Xapian::Query objects using OP_XOR. */
649 inline const Query
650 operator^(const Query & a, const Query & b)
651 {
652     return Query(Query::OP_XOR, a, b);
653 }
654 
655 /** Scale a Xapian::Query object using OP_SCALE_WEIGHT.
656  *
657  *  @param factor Non-negative real number to multiply weights by.
658  *  @param q Xapian::Query object.
659  */
660 inline const Query
661 operator*(double factor, const Query & q)
662 {
663     return Query(factor, q);
664 }
665 
666 /** Scale a Xapian::Query object using OP_SCALE_WEIGHT.
667  *
668  *  @param q Xapian::Query object.
669  *  @param factor Non-negative real number to multiply weights by.
670  */
671 inline const Query
672 operator*(const Query & q, double factor)
673 {
674     return Query(factor, q);
675 }
676 
677 /** Inverse-scale a Xapian::Query object using OP_SCALE_WEIGHT.
678  *
679  *  @param factor Positive real number to divide weights by.
680  *  @param q Xapian::Query object.
681  */
682 inline const Query
683 operator/(const Query & q, double factor)
684 {
685     return Query(1.0 / factor, q);
686 }
687 
688 /** @private @internal */
689 class InvertedQuery_ {
690     const Query & query;
691 
692     void operator=(const InvertedQuery_ &);
693 
InvertedQuery_(const Query & query_)694     explicit InvertedQuery_(const Query & query_) : query(query_) { }
695 
696   public:
697     // GCC 4.2 seems to needs a copy ctor.
InvertedQuery_(const InvertedQuery_ & o)698     InvertedQuery_(const InvertedQuery_ & o) : query(o.query) { }
699 
Query()700     operator Query() const {
701 	return Query(Query::OP_AND_NOT, Query(std::string()), query);
702     }
703 
704     friend const InvertedQuery_ operator~(const Query &q);
705 
706     friend const Query operator&(const Query & a, const InvertedQuery_ & b);
707 
708     friend const Query operator&=(Query & a, const InvertedQuery_ & b);
709 };
710 
711 /** Combine two Xapian::Query objects using OP_AND_NOT.
712  *
713  *  E.g. Xapian::Query q = q1 &~ q2;
714  */
715 inline const Query
716 operator&(const Query & a, const InvertedQuery_ & b)
717 {
718     return Query(Query::OP_AND_NOT, a, b.query);
719 }
720 
721 /** Combine two Xapian::Query objects using OP_AND_NOT with result in the first.
722  *
723  *  E.g. q1 &=~ q2;
724  */
725 inline const Query
726 operator&=(Query & a, const InvertedQuery_ & b)
727 {
728     return (a = Query(Query::OP_AND_NOT, a, b.query));
729 }
730 
731 #ifndef DOXYGEN /* @internal doesn't seem to avoid a warning here. */
732 /** @internal Helper to allow q1 &~ q2 to work. */
733 inline const InvertedQuery_
734 operator~(const Query &q)
735 {
736     return InvertedQuery_(q);
737 }
738 #endif
739 
740 namespace Internal {
741 class AndContext;
742 class OrContext;
743 class XorContext;
744 }
745 
746 /** @private @internal */
747 class Query::Internal : public Xapian::Internal::intrusive_base {
748   public:
XAPIAN_NOTHROW(Internal ())749     XAPIAN_NOTHROW(Internal()) { }
750 
751     virtual ~Internal();
752 
753     virtual PostingIterator::Internal * postlist(QueryOptimiser * qopt, double factor) const = 0;
754 
755     virtual void postlist_sub_and_like(Xapian::Internal::AndContext& ctx,
756 				       QueryOptimiser * qopt,
757 				       double factor) const;
758 
759     virtual void postlist_sub_or_like(Xapian::Internal::OrContext& ctx,
760 				      QueryOptimiser * qopt,
761 				      double factor) const;
762 
763     virtual void postlist_sub_xor(Xapian::Internal::XorContext& ctx,
764 				  QueryOptimiser * qopt,
765 				  double factor) const;
766 
767     virtual termcount XAPIAN_NOTHROW(get_length() const) XAPIAN_PURE_FUNCTION;
768 
769     virtual void serialise(std::string & result) const = 0;
770 
771     static Query::Internal * unserialise(const char ** p, const char * end, const Registry & reg);
772 
773     virtual Query::op XAPIAN_NOTHROW(get_type() const) XAPIAN_PURE_FUNCTION = 0;
774     virtual size_t XAPIAN_NOTHROW(get_num_subqueries() const) XAPIAN_PURE_FUNCTION;
775     virtual const Query get_subquery(size_t n) const;
776 
777     virtual std::string get_description() const = 0;
778 
779     // Pass argument as void* to avoid need to include <vector>.
780     virtual void gather_terms(void * void_terms) const;
781 };
782 
783 inline const Query
784 Query::operator&=(const Query & o)
785 {
786     if (o.empty()) {
787 	// q &= empty_query sets q to empty_query.
788 	*this = o;
789     } else if (this != &o &&
790 	       internal.get() &&
791 	       internal->_refs == 1 &&
792 	       get_type() == OP_AND) {
793 	// Appending a subquery to an existing AND.
794 	add_subquery(false, o);
795     } else {
796 	*this = Query(OP_AND, *this, o);
797     }
798     return *this;
799 }
800 
801 inline const Query
802 Query::operator|=(const Query & o)
803 {
804     if (o.empty()) {
805 	// q |= empty_query is a no-op.
806     } else if (this != &o &&
807 	       internal.get() &&
808 	       internal->_refs == 1 &&
809 	       get_type() == OP_OR) {
810 	// Appending a subquery to an existing OR.
811 	add_subquery(false, o);
812     } else {
813 	*this = Query(OP_OR, *this, o);
814     }
815     return *this;
816 }
817 
818 inline const Query
819 Query::operator^=(const Query & o)
820 {
821     if (o.empty()) {
822 	// q ^= empty_query is a no-op.
823     } else if (internal.get() == o.internal.get()) {
824 	// q ^= q gives MatchNothing.
825 	internal = NULL;
826     } else if (internal.get() &&
827 	       internal->_refs == 1 &&
828 	       get_type() == OP_XOR) {
829 	// Appending a subquery to an existing XOR.
830 	add_subquery(false, o);
831     } else {
832 	*this = Query(OP_XOR, *this, o);
833     }
834     return *this;
835 }
836 
837 }
838 
839 #endif // XAPIAN_INCLUDED_QUERY_H
840