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