LCOV - code coverage report
Current view: top level - api - query.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 954b5873a738 Lines: 179 191 93.7 %
Date: 2019-06-30 05:20:33 Functions: 25 25 100.0 %
Branches: 181 334 54.2 %

           Branch data     Line data    Source code
       1                 :            : /** @file query.cc
       2                 :            :  * @brief Xapian::Query API class
       3                 :            :  */
       4                 :            : /* Copyright (C) 2011,2012,2013,2015,2016,2017,2018 Olly Betts
       5                 :            :  *
       6                 :            :  * This program is free software; you can redistribute it and/or
       7                 :            :  * modify it under the terms of the GNU General Public License as
       8                 :            :  * published by the Free Software Foundation; either version 2 of the
       9                 :            :  * License, or (at your option) any later version.
      10                 :            :  *
      11                 :            :  * This program is distributed in the hope that it will be useful,
      12                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14                 :            :  * GNU General Public License for more details.
      15                 :            :  *
      16                 :            :  * You should have received a copy of the GNU General Public License
      17                 :            :  * along with this program; if not, write to the Free Software
      18                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
      19                 :            :  */
      20                 :            : 
      21                 :            : #include <config.h>
      22                 :            : 
      23                 :            : #include "xapian/query.h"
      24                 :            : #include "queryinternal.h"
      25                 :            : 
      26                 :            : #include <algorithm>
      27                 :            : 
      28                 :            : #include "debuglog.h"
      29                 :            : #include "omassert.h"
      30                 :            : #include "vectortermlist.h"
      31                 :            : 
      32                 :            : #include "xapian/error.h"
      33                 :            : 
      34                 :            : using namespace std;
      35                 :            : 
      36                 :            : namespace Xapian {
      37                 :            : 
      38                 :            : // Extra () are needed to resolve ambiguity with method declaration.
      39         [ +  - ]:        817 : const Query Query::MatchAll((string()));
      40                 :            : 
      41                 :        817 : const Query Query::MatchNothing;
      42                 :            : 
      43                 :     185244 : Query::Query(const string & term, Xapian::termcount wqf, Xapian::termpos pos)
      44         [ +  - ]:     185244 :     : internal(new Xapian::Internal::QueryTerm(term, wqf, pos))
      45                 :            : {
      46                 :            :     LOGCALL_CTOR(API, "Query", term | wqf | pos);
      47                 :     185244 : }
      48                 :            : 
      49                 :        628 : Query::Query(Xapian::PostingSource * source)
      50         [ +  + ]:        628 :     : internal(new Xapian::Internal::QueryPostingSource(source))
      51                 :            : {
      52                 :            :     LOGCALL_CTOR(API, "Query", source);
      53                 :        624 : }
      54                 :            : 
      55                 :         10 : Query::Query(double factor, const Xapian::Query & subquery)
      56                 :            : {
      57                 :            :     LOGCALL_CTOR(API, "Query", factor | subquery);
      58                 :            : 
      59         [ +  - ]:          5 :     if (!subquery.empty())
      60 [ +  - ][ +  - ]:          5 :         internal = new Xapian::Internal::QueryScaleWeight(factor, subquery);
                 [ +  - ]
      61                 :          5 : }
      62                 :            : 
      63                 :      39248 : Query::Query(op op_, const Xapian::Query & subquery, double factor)
      64                 :            : {
      65                 :            :     LOGCALL_CTOR(API, "Query", op_ | subquery | factor);
      66                 :            : 
      67         [ -  + ]:      19512 :     if (rare(op_ != OP_SCALE_WEIGHT))
      68 [ #  # ][ #  # ]:          0 :         throw Xapian::InvalidArgumentError("op must be OP_SCALE_WEIGHT");
                 [ #  # ]
      69                 :            :     // If the subquery is MatchNothing then generate Query() which matches
      70                 :            :     // nothing.
      71         [ +  + ]:      19512 :     if (!subquery.internal.get()) return;
      72         [ +  + ]:      10359 :     switch (subquery.internal->get_type()) {
      73                 :            :         case OP_VALUE_RANGE:
      74                 :            :         case OP_VALUE_GE:
      75                 :            :         case OP_VALUE_LE:
      76                 :            :             // These operators always return weight 0, so OP_SCALE_WEIGHT has
      77                 :            :             // no effect on them.
      78         [ +  - ]:       9502 :             internal = subquery.internal;
      79                 :       9502 :             return;
      80                 :            :         default:
      81                 :        857 :             break;
      82                 :            :     }
      83 [ +  - ][ +  + ]:      19512 :     internal = new Xapian::Internal::QueryScaleWeight(factor, subquery);
                 [ +  - ]
      84                 :            : }
      85                 :            : 
      86                 :        386 : Query::Query(op op_, Xapian::valueno slot, const std::string & limit)
      87                 :            : {
      88                 :            :     LOGCALL_CTOR(API, "Query", op_ | slot | limit);
      89                 :            : 
      90         [ +  + ]:        193 :     if (op_ == OP_VALUE_GE) {
      91         [ +  + ]:        104 :         if (limit.empty())
      92 [ +  - ][ +  - ]:          8 :             internal = new Xapian::Internal::QueryTerm();
                 [ +  - ]
      93                 :            :         else
      94 [ +  - ][ +  - ]:        104 :             internal = new Xapian::Internal::QueryValueGE(slot, limit);
                 [ +  - ]
      95         [ +  - ]:         89 :     } else if (usual(op_ == OP_VALUE_LE)) {
      96 [ +  - ][ +  - ]:         89 :         internal = new Xapian::Internal::QueryValueLE(slot, limit);
                 [ +  - ]
      97                 :            :     } else {
      98 [ #  # ][ #  # ]:          0 :         throw Xapian::InvalidArgumentError("op must be OP_VALUE_LE or OP_VALUE_GE");
                 [ #  # ]
      99                 :            :     }
     100                 :        193 : }
     101                 :            : 
     102                 :      19816 : Query::Query(op op_, Xapian::valueno slot,
     103                 :      19816 :              const std::string & begin, const std::string & end)
     104                 :            : {
     105                 :            :     LOGCALL_CTOR(API, "Query", op_ | slot | begin | end);
     106                 :            : 
     107         [ -  + ]:      19816 :     if (rare(op_ != OP_VALUE_RANGE))
     108 [ #  # ][ #  # ]:          0 :         throw Xapian::InvalidArgumentError("op must be OP_VALUE_RANGE");
                 [ #  # ]
     109                 :            :     // If begin > end then generate Query() which matches nothing.
     110         [ +  + ]:      19816 :     if (begin.empty()) {
     111 [ +  - ][ +  - ]:         89 :         internal = new Xapian::Internal::QueryValueLE(slot, end);
                 [ +  - ]
     112 [ +  - ][ +  + ]:      19727 :     } else if (usual(begin <= end)) {
     113 [ +  - ][ +  - ]:      10107 :         internal = new Xapian::Internal::QueryValueRange(slot, begin, end);
                 [ +  - ]
     114                 :            :     }
     115                 :      19816 : }
     116                 :            : 
     117                 :        736 : Query::Query(op op_,
     118                 :            :              const std::string & pattern,
     119                 :            :              Xapian::termcount max_expansion,
     120                 :            :              int flags,
     121                 :        736 :              op combiner)
     122                 :            : {
     123                 :            :     LOGCALL_CTOR(API, "Query", op_ | pattern | max_expansion | flags | combiner);
     124 [ +  + ][ +  - ]:        736 :     if (rare(combiner != OP_SYNONYM && combiner != OP_MAX && combiner != OP_OR))
         [ -  + ][ -  + ]
     125                 :            :         throw Xapian::InvalidArgumentError("combiner must be OP_SYNONYM or "
     126 [ #  # ][ #  # ]:          0 :                                            "OP_MAX or OP_OR");
                 [ #  # ]
     127         [ +  + ]:        736 :     if (op_ == OP_EDIT_DISTANCE) {
     128         [ +  - ]:          6 :         internal = new Xapian::Internal::QueryEditDistance(pattern,
     129                 :            :                                                            max_expansion,
     130                 :            :                                                            flags,
     131 [ +  - ][ +  - ]:          6 :                                                            combiner);
     132                 :          6 :         return;
     133                 :            :     }
     134         [ -  + ]:        730 :     if (rare(op_ != OP_WILDCARD))
     135                 :            :         throw Xapian::InvalidArgumentError("op must be OP_EDIT_DISTANCE or "
     136 [ #  # ][ #  # ]:          0 :                                            "OP_WILDCARD");
                 [ #  # ]
     137                 :            : 
     138                 :        730 :     auto just_flags = flags & ~Query::WILDCARD_LIMIT_MASK_;
     139         [ +  + ]:        730 :     if (pattern.empty()) {
     140         [ -  + ]:          1 :         if (just_flags == 0) {
     141                 :            :             // Empty pattern with implicit trailing '*' -> MatchAll.
     142 [ #  # ][ #  # ]:          0 :             internal = new Xapian::Internal::QueryTerm();
                 [ #  # ]
     143                 :            :         } else {
     144                 :            :             // Empty pattern with extended wildcards -> MatchNothing.
     145                 :            :         }
     146                 :          1 :         return;
     147                 :            :     }
     148                 :            : 
     149                 :            :     // Check if pattern consists of one or more '*' and at most one '?' (in any
     150                 :            :     // order) - if so treat it as just MatchAll.
     151                 :        729 :     bool match_all = false;
     152                 :        729 :     bool question_marks = false;
     153         [ +  + ]:        772 :     for (auto&& ch : pattern) {
     154 [ +  + ][ +  - ]:        756 :         if (ch == '*' && (flags & Query::WILDCARD_PATTERN_MULTI)) {
     155                 :         26 :             match_all = true;
     156 [ +  + ][ +  + ]:        730 :         } else if (ch == '?' && !question_marks &&
                 [ +  - ]
     157                 :         17 :                    (flags & Query::WILDCARD_PATTERN_SINGLE)) {
     158                 :         17 :             question_marks = true;
     159                 :            :         } else {
     160                 :        713 :             match_all = false;
     161                 :        713 :             break;
     162                 :            :         }
     163                 :            :     }
     164         [ +  + ]:        729 :     if (match_all) {
     165 [ +  - ][ +  - ]:          9 :         internal = new Xapian::Internal::QueryTerm();
                 [ +  - ]
     166                 :          9 :         return;
     167                 :            :     }
     168                 :            : 
     169         [ +  - ]:        720 :     internal = new Xapian::Internal::QueryWildcard(pattern,
     170                 :            :                                                    max_expansion,
     171                 :            :                                                    flags,
     172 [ +  - ][ +  - ]:        736 :                                                    combiner);
     173                 :            : }
     174                 :            : 
     175                 :        145 : Query::Query(op op_,
     176                 :            :              const std::string& pattern,
     177                 :            :              Xapian::termcount max_expansion,
     178                 :            :              int flags,
     179                 :            :              op combiner,
     180                 :            :              unsigned edit_distance,
     181                 :        145 :              size_t min_prefix_len)
     182                 :            : {
     183                 :            :     LOGCALL_CTOR(API, "Query", op_ | pattern | max_expansion | flags | combiner | edit_distance | min_prefix_len);
     184 [ +  + ][ +  - ]:        145 :     if (rare(combiner != OP_SYNONYM && combiner != OP_MAX && combiner != OP_OR))
         [ -  + ][ -  + ]
     185                 :            :         throw Xapian::InvalidArgumentError("combiner must be OP_SYNONYM or "
     186 [ #  # ][ #  # ]:          0 :                                            "OP_MAX or OP_OR");
                 [ #  # ]
     187         [ -  + ]:        145 :     if (rare(op_ != OP_EDIT_DISTANCE))
     188 [ #  # ][ #  # ]:          0 :         throw Xapian::InvalidArgumentError("op must be OP_EDIT_DISTANCE");
                 [ #  # ]
     189         [ +  - ]:        145 :     internal = new Xapian::Internal::QueryEditDistance(pattern,
     190                 :            :                                                        max_expansion,
     191                 :            :                                                        flags,
     192                 :            :                                                        combiner,
     193                 :            :                                                        edit_distance,
     194 [ +  - ][ +  - ]:        145 :                                                        min_prefix_len);
     195                 :        145 : }
     196                 :            : 
     197                 :            : const TermIterator
     198                 :        196 : Query::get_terms_begin() const
     199                 :            : {
     200         [ +  + ]:        196 :     if (!internal.get())
     201                 :          1 :         return TermIterator();
     202                 :            : 
     203                 :        195 :     vector<pair<Xapian::termpos, string>> terms;
     204         [ +  - ]:        195 :     internal->gather_terms(static_cast<void*>(&terms));
     205         [ +  - ]:        195 :     sort(terms.begin(), terms.end());
     206                 :            : 
     207                 :        390 :     vector<string> v;
     208                 :        195 :     const string * old_term = NULL;
     209                 :        195 :     Xapian::termpos old_pos = 0;
     210         [ +  + ]:        653 :     for (auto && i : terms) {
     211                 :            :         // Remove duplicates (same term at the same position).
     212 [ +  + ][ +  + ]:        458 :         if (old_term && old_pos == i.first && *old_term == i.second)
         [ +  - ][ -  + ]
                 [ -  + ]
     213                 :          0 :             continue;
     214                 :            : 
     215         [ +  - ]:        458 :         v.push_back(i.second);
     216                 :        458 :         old_pos = i.first;
     217                 :        458 :         old_term = &(i.second);
     218                 :            :     }
     219 [ +  - ][ +  - ]:        391 :     return TermIterator(new VectorTermList(v.begin(), v.end()));
                 [ +  - ]
     220                 :            : }
     221                 :            : 
     222                 :            : const TermIterator
     223                 :     301712 : Query::get_unique_terms_begin() const
     224                 :            : {
     225         [ -  + ]:     301712 :     if (!internal.get())
     226                 :          0 :         return TermIterator();
     227                 :            : 
     228                 :     301712 :     vector<pair<Xapian::termpos, string>> terms;
     229         [ +  - ]:     301712 :     internal->gather_terms(static_cast<void*>(&terms));
     230                 :            :     sort(terms.begin(), terms.end(), [](
     231                 :            :                 const pair<Xapian::termpos, string>& a,
     232                 :     283854 :                 const pair<Xapian::termpos, string>& b) {
     233                 :     283854 :         return a.second < b.second;
     234         [ +  - ]:     585566 :     });
     235                 :            : 
     236                 :     603424 :     vector<string> v;
     237                 :     301712 :     const string * old_term = NULL;
     238         [ +  + ]:     826720 :     for (auto && i : terms) {
     239                 :            :         // Remove duplicate term names.
     240 [ +  + ][ +  - ]:     525008 :         if (old_term && *old_term == i.second)
         [ +  + ][ +  + ]
     241                 :       7408 :             continue;
     242                 :            : 
     243         [ +  - ]:     517600 :         v.push_back(i.second);
     244                 :     517600 :         old_term = &(i.second);
     245                 :            :     }
     246 [ +  - ][ +  - ]:     603424 :     return TermIterator(new VectorTermList(v.begin(), v.end()));
                 [ +  - ]
     247                 :            : }
     248                 :            : 
     249                 :            : Xapian::termcount
     250                 :      15647 : Query::get_length() const XAPIAN_NOEXCEPT
     251                 :            : {
     252         [ +  + ]:      15647 :     return (internal.get() ? internal->get_length() : 0);
     253                 :            : }
     254                 :            : 
     255                 :            : string
     256                 :      10677 : Query::serialise() const
     257                 :            : {
     258                 :      10677 :     string result;
     259         [ +  + ]:      10677 :     if (internal.get())
     260         [ +  + ]:      10676 :         internal->serialise(result);
     261                 :      10677 :     return result;
     262                 :            : }
     263                 :            : 
     264                 :            : const Query
     265                 :      10673 : Query::unserialise(const string & s, const Registry & reg)
     266                 :            : {
     267                 :      10673 :     const char * p = s.data();
     268                 :      10673 :     const char * end = p + s.size();
     269         [ +  + ]:      10673 :     Query::Internal * q = Query::Internal::unserialise(&p, end, reg);
     270                 :            :     AssertEq(p, end);
     271         [ +  - ]:      10671 :     return Query(q);
     272                 :            : }
     273                 :            : 
     274                 :            : Xapian::Query::op
     275                 :      46332 : Query::get_type() const XAPIAN_NOEXCEPT
     276                 :            : {
     277         [ +  + ]:      46332 :     if (!internal.get())
     278                 :       9169 :         return Xapian::Query::LEAF_MATCH_NOTHING;
     279                 :      37163 :     return internal->get_type();
     280                 :            : }
     281                 :            : 
     282                 :            : size_t
     283                 :       1227 : Query::get_num_subqueries() const XAPIAN_NOEXCEPT
     284                 :            : {
     285         [ +  + ]:       1227 :     return internal.get() ? internal->get_num_subqueries() : 0;
     286                 :            : }
     287                 :            : 
     288                 :            : const Query
     289                 :        967 : Query::get_subquery(size_t n) const
     290                 :            : {
     291                 :        967 :     return internal->get_subquery(n);
     292                 :            : }
     293                 :            : 
     294                 :            : Xapian::termcount
     295                 :          2 : Query::get_leaf_wqf() const
     296                 :            : {
     297                 :          2 :     return internal->get_wqf();
     298                 :            : }
     299                 :            : 
     300                 :            : Xapian::termpos
     301                 :          2 : Query::get_leaf_pos() const
     302                 :            : {
     303                 :          2 :     return internal->get_pos();
     304                 :            : }
     305                 :            : 
     306                 :            : string
     307                 :       2487 : Query::get_description() const
     308                 :            : {
     309         [ +  - ]:       2487 :     string desc = "Query(";
     310         [ +  + ]:       2487 :     if (internal.get())
     311 [ +  - ][ +  - ]:       2435 :         desc += internal->get_description();
     312         [ +  - ]:       2487 :     desc += ")";
     313                 :       2487 :     return desc;
     314                 :            : }
     315                 :            : 
     316                 :            : void
     317                 :     122195 : Query::init(op op_, size_t n_subqueries, Xapian::termcount parameter)
     318                 :            : {
     319 [ +  + ][ +  + ]:     122195 :     if (parameter > 0 &&
     320 [ +  + ][ -  + ]:        837 :         op_ != OP_NEAR && op_ != OP_PHRASE && op_ != OP_ELITE_SET)
     321                 :            :         throw InvalidArgumentError("parameter only valid with OP_NEAR, "
     322 [ #  # ][ #  # ]:          0 :                                    "OP_PHRASE or OP_ELITE_SET");
                 [ #  # ]
     323                 :            : 
     324   [ +  +  +  +  :     122195 :     switch (op_) {
          +  +  +  +  +  
                +  +  + ]
     325                 :            :         case OP_AND:
     326         [ +  - ]:        406 :             internal = new Xapian::Internal::QueryAnd(n_subqueries);
     327                 :        406 :             break;
     328                 :            :         case OP_OR:
     329         [ +  - ]:      67954 :             internal = new Xapian::Internal::QueryOr(n_subqueries);
     330                 :      67954 :             break;
     331                 :            :         case OP_AND_NOT:
     332         [ +  - ]:        191 :             internal = new Xapian::Internal::QueryAndNot(n_subqueries);
     333                 :        191 :             break;
     334                 :            :         case OP_XOR:
     335         [ +  - ]:        103 :             internal = new Xapian::Internal::QueryXor(n_subqueries);
     336                 :        103 :             break;
     337                 :            :         case OP_AND_MAYBE:
     338         [ +  - ]:        114 :             internal = new Xapian::Internal::QueryAndMaybe(n_subqueries);
     339                 :        114 :             break;
     340                 :            :         case OP_FILTER:
     341         [ +  - ]:         79 :             internal = new Xapian::Internal::QueryFilter(n_subqueries);
     342                 :         79 :             break;
     343                 :            :         case OP_NEAR:
     344                 :        282 :             internal = new Xapian::Internal::QueryNear(n_subqueries,
     345         [ +  - ]:        282 :                                                        parameter);
     346                 :        282 :             break;
     347                 :            :         case OP_PHRASE:
     348                 :        791 :             internal = new Xapian::Internal::QueryPhrase(n_subqueries,
     349         [ +  - ]:        791 :                                                          parameter);
     350                 :        791 :             break;
     351                 :            :         case OP_ELITE_SET:
     352                 :        200 :             internal = new Xapian::Internal::QueryEliteSet(n_subqueries,
     353         [ +  - ]:        200 :                                                            parameter);
     354                 :        200 :             break;
     355                 :            :         case OP_SYNONYM:
     356         [ +  - ]:      51969 :             internal = new Xapian::Internal::QuerySynonym(n_subqueries);
     357                 :      51969 :             break;
     358                 :            :         case OP_MAX:
     359         [ +  - ]:         10 :             internal = new Xapian::Internal::QueryMax(n_subqueries);
     360                 :         10 :             break;
     361                 :            :         default:
     362 [ +  - ][ +  - ]:         96 :             if (op_ == OP_INVALID && n_subqueries == 0) {
     363                 :         96 :                 internal = new Xapian::Internal::QueryInvalid();
     364                 :         96 :                 break;
     365                 :            :             }
     366 [ #  # ][ #  # ]:          0 :             throw InvalidArgumentError("op not valid with a list of subqueries");
                 [ #  # ]
     367                 :            :     }
     368                 :     122195 : }
     369                 :            : 
     370                 :            : void
     371                 :     298248 : Query::add_subquery(bool positional, const Xapian::Query & subquery)
     372                 :            : {
     373                 :            :     // We could handle this in a type-safe way, but we'd need to at least
     374                 :            :     // declare Xapian::Internal::QueryBranch in the API header, which seems
     375                 :            :     // less desirable than a static_cast<> here.
     376                 :            :     Xapian::Internal::QueryBranch * branch_query =
     377                 :     298248 :         static_cast<Xapian::Internal::QueryBranch*>(internal.get());
     378                 :            :     Assert(branch_query);
     379         [ +  + ]:     298248 :     if (positional) {
     380   [ +  +  +  + ]:       2152 :         switch (subquery.get_type()) {
     381                 :            :             case LEAF_TERM:
     382                 :       1932 :                 break;
     383                 :            :             case LEAF_POSTING_SOURCE:
     384                 :            :             case LEAF_MATCH_ALL:
     385                 :            :             case LEAF_MATCH_NOTHING:
     386                 :            :                 // None of these have positions, so positional operators won't
     387                 :            :                 // match.  Add MatchNothing as that is has special handling in
     388                 :            :                 // AND-like queries to reduce the parent query to MatchNothing,
     389                 :            :                 // which is appropriate in this case.
     390                 :         44 :                 branch_query->add_subquery(MatchNothing);
     391                 :     298204 :                 return;
     392                 :            :             case OP_OR:
     393                 :            :                 // OP_OR is now handled below OP_NEAR and OP_PHRASE.
     394                 :        132 :                 break;
     395                 :            :             default:
     396 [ +  - ][ +  - ]:       2108 :                 throw Xapian::UnimplementedError("OP_NEAR and OP_PHRASE only currently support leaf subqueries");
                 [ +  - ]
     397                 :            :         }
     398                 :            :     }
     399                 :     298160 :     branch_query->add_subquery(subquery);
     400                 :            : }
     401                 :            : 
     402                 :            : void
     403                 :     122055 : Query::done()
     404                 :            : {
     405                 :            :     Xapian::Internal::QueryBranch * branch_query =
     406                 :     122055 :         static_cast<Xapian::Internal::QueryBranch*>(internal.get());
     407         [ +  - ]:     122055 :     if (branch_query)
     408                 :     122055 :         internal = branch_query->done();
     409                 :     122055 : }
     410                 :            : 
     411 [ +  - ][ +  - ]:       3268 : }

Generated by: LCOV version 1.11