LCOV - code coverage report
Current view: top level - api - query.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 7028d852e609 Lines: 168 178 94.4 %
Date: 2019-02-17 14:59:59 Functions: 24 24 100.0 %
Branches: 165 294 56.1 %

           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         [ +  - ]:        750 : const Query Query::MatchAll((string()));
      40                 :            : 
      41                 :        750 : const Query Query::MatchNothing;
      42                 :            : 
      43                 :     684994 : Query::Query(const string & term, Xapian::termcount wqf, Xapian::termpos pos)
      44         [ +  - ]:     684994 :     : internal(new Xapian::Internal::QueryTerm(term, wqf, pos))
      45                 :            : {
      46                 :            :     LOGCALL_CTOR(API, "Query", term | wqf | pos);
      47                 :     684994 : }
      48                 :            : 
      49                 :        626 : Query::Query(Xapian::PostingSource * source)
      50         [ +  + ]:        626 :     : internal(new Xapian::Internal::QueryPostingSource(source))
      51                 :            : {
      52                 :            :     LOGCALL_CTOR(API, "Query", source);
      53                 :        622 : }
      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                 :      19756 : Query::Query(op op_, Xapian::valueno slot,
     103                 :      19756 :              const std::string & begin, const std::string & end)
     104                 :            : {
     105                 :            :     LOGCALL_CTOR(API, "Query", op_ | slot | begin | end);
     106                 :            : 
     107         [ -  + ]:      19756 :     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         [ +  + ]:      19756 :     if (begin.empty()) {
     111 [ +  - ][ +  - ]:         89 :         internal = new Xapian::Internal::QueryValueLE(slot, end);
                 [ +  - ]
     112 [ +  - ][ +  + ]:      19667 :     } else if (usual(begin <= end)) {
     113 [ +  - ][ +  - ]:      10050 :         internal = new Xapian::Internal::QueryValueRange(slot, begin, end);
                 [ +  - ]
     114                 :            :     }
     115                 :      19756 : }
     116                 :            : 
     117                 :        714 : Query::Query(op op_,
     118                 :            :              const std::string & pattern,
     119                 :            :              Xapian::termcount max_expansion,
     120                 :            :              int flags,
     121                 :        714 :              op combiner)
     122                 :            : {
     123                 :            :     LOGCALL_CTOR(API, "Query", op_ | pattern | max_expansion | flags | combiner);
     124         [ -  + ]:        714 :     if (rare(op_ != OP_WILDCARD))
     125 [ #  # ][ #  # ]:          0 :         throw Xapian::InvalidArgumentError("op must be OP_WILDCARD");
                 [ #  # ]
     126 [ +  + ][ +  - ]:        714 :     if (rare(combiner != OP_SYNONYM && combiner != OP_MAX && combiner != OP_OR))
         [ -  + ][ -  + ]
     127 [ #  # ][ #  # ]:          0 :         throw Xapian::InvalidArgumentError("combiner must be OP_SYNONYM or OP_MAX or OP_OR");
                 [ #  # ]
     128                 :            : 
     129                 :        714 :     auto just_flags = flags & ~Query::WILDCARD_LIMIT_MASK_;
     130         [ +  + ]:        714 :     if (pattern.empty()) {
     131         [ -  + ]:          1 :         if (just_flags == 0) {
     132                 :            :             // Empty pattern with implicit trailing '*' -> MatchAll.
     133 [ #  # ][ #  # ]:          0 :             internal = new Xapian::Internal::QueryTerm();
                 [ #  # ]
     134                 :            :         } else {
     135                 :            :             // Empty pattern with extended wildcards -> MatchNothing.
     136                 :            :         }
     137                 :          1 :         return;
     138                 :            :     }
     139                 :            : 
     140                 :            :     // Check if pattern consists of one or more '*' and at most one '?' (in any
     141                 :            :     // order) - if so treat it as just MatchAll.
     142                 :        713 :     bool match_all = false;
     143                 :        713 :     bool question_marks = false;
     144         [ +  + ]:        750 :     for (auto&& ch : pattern) {
     145 [ +  + ][ +  - ]:        740 :         if (ch == '*' && (flags & Query::WILDCARD_PATTERN_MULTI)) {
     146                 :         26 :             match_all = true;
     147 [ +  + ][ +  + ]:        714 :         } else if (ch == '?' && !question_marks &&
                 [ +  - ]
     148                 :         11 :                    (flags & Query::WILDCARD_PATTERN_SINGLE)) {
     149                 :         11 :             question_marks = true;
     150                 :            :         } else {
     151                 :        703 :             match_all = false;
     152                 :        703 :             break;
     153                 :            :         }
     154                 :            :     }
     155         [ +  + ]:        713 :     if (match_all) {
     156 [ +  - ][ +  - ]:          9 :         internal = new Xapian::Internal::QueryTerm();
                 [ +  - ]
     157                 :          9 :         return;
     158                 :            :     }
     159                 :            : 
     160         [ +  - ]:        704 :     internal = new Xapian::Internal::QueryWildcard(pattern,
     161                 :            :                                                    max_expansion,
     162                 :            :                                                    flags,
     163 [ +  - ][ +  - ]:        714 :                                                    combiner);
     164                 :            : }
     165                 :            : 
     166                 :            : const TermIterator
     167                 :        196 : Query::get_terms_begin() const
     168                 :            : {
     169         [ +  + ]:        196 :     if (!internal.get())
     170                 :          1 :         return TermIterator();
     171                 :            : 
     172                 :        195 :     vector<pair<Xapian::termpos, string>> terms;
     173         [ +  - ]:        195 :     internal->gather_terms(static_cast<void*>(&terms));
     174         [ +  - ]:        195 :     sort(terms.begin(), terms.end());
     175                 :            : 
     176                 :        390 :     vector<string> v;
     177                 :        195 :     const string * old_term = NULL;
     178                 :        195 :     Xapian::termpos old_pos = 0;
     179         [ +  + ]:        653 :     for (auto && i : terms) {
     180                 :            :         // Remove duplicates (same term at the same position).
     181 [ +  + ][ +  + ]:        458 :         if (old_term && old_pos == i.first && *old_term == i.second)
         [ -  + ][ -  + ]
     182                 :          0 :             continue;
     183                 :            : 
     184         [ +  - ]:        458 :         v.push_back(i.second);
     185                 :        458 :         old_pos = i.first;
     186                 :        458 :         old_term = &(i.second);
     187                 :            :     }
     188 [ +  - ][ +  - ]:        391 :     return TermIterator(new VectorTermList(v.begin(), v.end()));
                 [ +  - ]
     189                 :            : }
     190                 :            : 
     191                 :            : const TermIterator
     192                 :     300780 : Query::get_unique_terms_begin() const
     193                 :            : {
     194         [ -  + ]:     300780 :     if (!internal.get())
     195                 :          0 :         return TermIterator();
     196                 :            : 
     197                 :     300780 :     vector<pair<Xapian::termpos, string>> terms;
     198         [ +  - ]:     300780 :     internal->gather_terms(static_cast<void*>(&terms));
     199                 :            :     sort(terms.begin(), terms.end(), [](
     200                 :            :                 const pair<Xapian::termpos, string>& a,
     201                 :     282020 :                 const pair<Xapian::termpos, string>& b) {
     202                 :     282020 :         return a.second < b.second;
     203         [ +  - ]:     582800 :     });
     204                 :            : 
     205                 :     601560 :     vector<string> v;
     206                 :     300780 :     const string * old_term = NULL;
     207         [ +  + ]:     824366 :     for (auto && i : terms) {
     208                 :            :         // Remove duplicate term names.
     209 [ +  + ][ +  + ]:     523586 :         if (old_term && *old_term == i.second)
                 [ +  + ]
     210                 :       7398 :             continue;
     211                 :            : 
     212         [ +  - ]:     516188 :         v.push_back(i.second);
     213                 :     516188 :         old_term = &(i.second);
     214                 :            :     }
     215 [ +  - ][ +  - ]:     601560 :     return TermIterator(new VectorTermList(v.begin(), v.end()));
                 [ +  - ]
     216                 :            : }
     217                 :            : 
     218                 :            : Xapian::termcount
     219                 :      15315 : Query::get_length() const XAPIAN_NOEXCEPT
     220                 :            : {
     221         [ +  + ]:      15315 :     return (internal.get() ? internal->get_length() : 0);
     222                 :            : }
     223                 :            : 
     224                 :            : string
     225                 :      10381 : Query::serialise() const
     226                 :            : {
     227                 :      10381 :     string result;
     228         [ +  + ]:      10381 :     if (internal.get())
     229         [ +  + ]:      10380 :         internal->serialise(result);
     230                 :      10381 :     return result;
     231                 :            : }
     232                 :            : 
     233                 :            : const Query
     234                 :      10377 : Query::unserialise(const string & s, const Registry & reg)
     235                 :            : {
     236                 :      10377 :     const char * p = s.data();
     237                 :      10377 :     const char * end = p + s.size();
     238         [ +  + ]:      10377 :     Query::Internal * q = Query::Internal::unserialise(&p, end, reg);
     239                 :            :     AssertEq(p, end);
     240         [ +  - ]:      10375 :     return Query(q);
     241                 :            : }
     242                 :            : 
     243                 :            : Xapian::Query::op
     244                 :      45970 : Query::get_type() const XAPIAN_NOEXCEPT
     245                 :            : {
     246         [ +  + ]:      45970 :     if (!internal.get())
     247                 :       9169 :         return Xapian::Query::LEAF_MATCH_NOTHING;
     248                 :      36801 :     return internal->get_type();
     249                 :            : }
     250                 :            : 
     251                 :            : size_t
     252                 :       1080 : Query::get_num_subqueries() const XAPIAN_NOEXCEPT
     253                 :            : {
     254         [ +  + ]:       1080 :     return internal.get() ? internal->get_num_subqueries() : 0;
     255                 :            : }
     256                 :            : 
     257                 :            : const Query
     258                 :        811 : Query::get_subquery(size_t n) const
     259                 :            : {
     260                 :        811 :     return internal->get_subquery(n);
     261                 :            : }
     262                 :            : 
     263                 :            : Xapian::termcount
     264                 :          2 : Query::get_leaf_wqf() const
     265                 :            : {
     266                 :          2 :     return internal->get_wqf();
     267                 :            : }
     268                 :            : 
     269                 :            : Xapian::termpos
     270                 :          2 : Query::get_leaf_pos() const
     271                 :            : {
     272                 :          2 :     return internal->get_pos();
     273                 :            : }
     274                 :            : 
     275                 :            : string
     276                 :       2412 : Query::get_description() const
     277                 :            : {
     278         [ +  - ]:       2412 :     string desc = "Query(";
     279         [ +  + ]:       2412 :     if (internal.get())
     280 [ +  - ][ +  - ]:       2360 :         desc += internal->get_description();
     281         [ +  - ]:       2412 :     desc += ")";
     282                 :       2412 :     return desc;
     283                 :            : }
     284                 :            : 
     285                 :            : void
     286                 :     621922 : Query::init(op op_, size_t n_subqueries, Xapian::termcount parameter)
     287                 :            : {
     288 [ +  + ][ +  + ]:     621922 :     if (parameter > 0 &&
     289 [ +  + ][ -  + ]:        819 :         op_ != OP_NEAR && op_ != OP_PHRASE && op_ != OP_ELITE_SET)
     290                 :            :         throw InvalidArgumentError("parameter only valid with OP_NEAR, "
     291 [ #  # ][ #  # ]:          0 :                                    "OP_PHRASE or OP_ELITE_SET");
                 [ #  # ]
     292                 :            : 
     293   [ +  +  +  +  :     621922 :     switch (op_) {
          +  +  +  +  +  
                +  +  + ]
     294                 :            :         case OP_AND:
     295         [ +  - ]:        367 :             internal = new Xapian::Internal::QueryAnd(n_subqueries);
     296                 :        367 :             break;
     297                 :            :         case OP_OR:
     298         [ +  - ]:     567908 :             internal = new Xapian::Internal::QueryOr(n_subqueries);
     299                 :     567908 :             break;
     300                 :            :         case OP_AND_NOT:
     301         [ +  - ]:        177 :             internal = new Xapian::Internal::QueryAndNot(n_subqueries);
     302                 :        177 :             break;
     303                 :            :         case OP_XOR:
     304         [ +  - ]:        100 :             internal = new Xapian::Internal::QueryXor(n_subqueries);
     305                 :        100 :             break;
     306                 :            :         case OP_AND_MAYBE:
     307         [ +  - ]:        106 :             internal = new Xapian::Internal::QueryAndMaybe(n_subqueries);
     308                 :        106 :             break;
     309                 :            :         case OP_FILTER:
     310         [ +  - ]:         79 :             internal = new Xapian::Internal::QueryFilter(n_subqueries);
     311                 :         79 :             break;
     312                 :            :         case OP_NEAR:
     313                 :        276 :             internal = new Xapian::Internal::QueryNear(n_subqueries,
     314         [ +  - ]:        276 :                                                        parameter);
     315                 :        276 :             break;
     316                 :            :         case OP_PHRASE:
     317                 :        767 :             internal = new Xapian::Internal::QueryPhrase(n_subqueries,
     318         [ +  - ]:        767 :                                                          parameter);
     319                 :        767 :             break;
     320                 :            :         case OP_ELITE_SET:
     321                 :        200 :             internal = new Xapian::Internal::QueryEliteSet(n_subqueries,
     322         [ +  - ]:        200 :                                                            parameter);
     323                 :        200 :             break;
     324                 :            :         case OP_SYNONYM:
     325         [ +  - ]:      51836 :             internal = new Xapian::Internal::QuerySynonym(n_subqueries);
     326                 :      51836 :             break;
     327                 :            :         case OP_MAX:
     328         [ +  - ]:         10 :             internal = new Xapian::Internal::QueryMax(n_subqueries);
     329                 :         10 :             break;
     330                 :            :         default:
     331 [ +  - ][ +  - ]:         96 :             if (op_ == OP_INVALID && n_subqueries == 0) {
     332                 :         96 :                 internal = new Xapian::Internal::QueryInvalid();
     333                 :         96 :                 break;
     334                 :            :             }
     335 [ #  # ][ #  # ]:          0 :             throw InvalidArgumentError("op not valid with a list of subqueries");
                 [ #  # ]
     336                 :            :     }
     337                 :     621922 : }
     338                 :            : 
     339                 :            : void
     340                 :    1297786 : Query::add_subquery(bool positional, const Xapian::Query & subquery)
     341                 :            : {
     342                 :            :     // We could handle this in a type-safe way, but we'd need to at least
     343                 :            :     // declare Xapian::Internal::QueryBranch in the API header, which seems
     344                 :            :     // less desirable than a static_cast<> here.
     345                 :            :     Xapian::Internal::QueryBranch * branch_query =
     346                 :    1297786 :         static_cast<Xapian::Internal::QueryBranch*>(internal.get());
     347                 :            :     Assert(branch_query);
     348         [ +  + ]:    1297786 :     if (positional) {
     349   [ +  +  +  + ]:       2146 :         switch (subquery.get_type()) {
     350                 :            :             case LEAF_TERM:
     351                 :       1926 :                 break;
     352                 :            :             case LEAF_POSTING_SOURCE:
     353                 :            :             case LEAF_MATCH_ALL:
     354                 :            :             case LEAF_MATCH_NOTHING:
     355                 :            :                 // None of these have positions, so positional operators won't
     356                 :            :                 // match.  Add MatchNothing as that is has special handling in
     357                 :            :                 // AND-like queries to reduce the parent query to MatchNothing,
     358                 :            :                 // which is appropriate in this case.
     359                 :         44 :                 branch_query->add_subquery(MatchNothing);
     360                 :    1297742 :                 return;
     361                 :            :             case OP_OR:
     362                 :            :                 // OP_OR is now handled below OP_NEAR and OP_PHRASE.
     363                 :        132 :                 break;
     364                 :            :             default:
     365 [ +  - ][ +  - ]:       2102 :                 throw Xapian::UnimplementedError("OP_NEAR and OP_PHRASE only currently support leaf subqueries");
                 [ +  - ]
     366                 :            :         }
     367                 :            :     }
     368                 :    1297698 :     branch_query->add_subquery(subquery);
     369                 :            : }
     370                 :            : 
     371                 :            : void
     372                 :     621782 : Query::done()
     373                 :            : {
     374                 :            :     Xapian::Internal::QueryBranch * branch_query =
     375                 :     621782 :         static_cast<Xapian::Internal::QueryBranch*>(internal.get());
     376         [ +  - ]:     621782 :     if (branch_query)
     377                 :     621782 :         internal = branch_query->done();
     378                 :     621782 : }
     379                 :            : 
     380 [ +  - ][ +  - ]:       3000 : }

Generated by: LCOV version 1.11