[python3] Simplify generated wrapper post-processing
[xapian.git] / xapian-core / api / query.cc
blob767435f091188ec4645fbf48efbf8c7b2d473831
1 /** @file query.cc
2 * @brief Xapian::Query API class
3 */
4 /* Copyright (C) 2011,2012,2013,2015,2016,2017 Olly Betts
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.
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.
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
21 #include <config.h>
23 #include "xapian/query.h"
24 #include "queryinternal.h"
26 #include <algorithm>
28 #include "debuglog.h"
29 #include "omassert.h"
30 #include "vectortermlist.h"
32 #include "xapian/error.h"
34 using namespace std;
36 namespace Xapian {
38 // Extra () are needed to resolve ambiguity with method declaration.
39 const Query Query::MatchAll((string()));
41 const Query Query::MatchNothing;
43 Query::Query(const string & term, Xapian::termcount wqf, Xapian::termpos pos)
44 : internal(new Xapian::Internal::QueryTerm(term, wqf, pos))
46 LOGCALL_CTOR(API, "Query", term | wqf | pos);
49 Query::Query(Xapian::PostingSource * source)
50 : internal(new Xapian::Internal::QueryPostingSource(source))
52 LOGCALL_CTOR(API, "Query", source);
55 Query::Query(double factor, const Xapian::Query & subquery)
57 LOGCALL_CTOR(API, "Query", factor | subquery);
59 if (!subquery.empty())
60 internal = new Xapian::Internal::QueryScaleWeight(factor, subquery);
63 Query::Query(op op_, const Xapian::Query & subquery, double factor)
65 LOGCALL_CTOR(API, "Query", op_ | subquery | factor);
67 if (rare(op_ != OP_SCALE_WEIGHT))
68 throw Xapian::InvalidArgumentError("op must be OP_SCALE_WEIGHT");
69 // If the subquery is MatchNothing then generate Query() which matches
70 // nothing.
71 if (!subquery.internal.get()) return;
72 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 internal = subquery.internal;
79 return;
80 default:
81 break;
83 internal = new Xapian::Internal::QueryScaleWeight(factor, subquery);
86 Query::Query(op op_, Xapian::valueno slot, const std::string & limit)
88 LOGCALL_CTOR(API, "Query", op_ | slot | limit);
90 if (op_ == OP_VALUE_GE) {
91 if (limit.empty())
92 internal = MatchAll.internal;
93 else
94 internal = new Xapian::Internal::QueryValueGE(slot, limit);
95 } else if (usual(op_ == OP_VALUE_LE)) {
96 internal = new Xapian::Internal::QueryValueLE(slot, limit);
97 } else {
98 throw Xapian::InvalidArgumentError("op must be OP_VALUE_LE or OP_VALUE_GE");
102 Query::Query(op op_, Xapian::valueno slot,
103 const std::string & begin, const std::string & end)
105 LOGCALL_CTOR(API, "Query", op_ | slot | begin | end);
107 if (rare(op_ != OP_VALUE_RANGE))
108 throw Xapian::InvalidArgumentError("op must be OP_VALUE_RANGE");
109 // If begin > end then generate Query() which matches nothing.
110 if (begin.empty()) {
111 internal = new Xapian::Internal::QueryValueLE(slot, end);
112 } else if (usual(begin <= end)) {
113 internal = new Xapian::Internal::QueryValueRange(slot, begin, end);
117 Query::Query(op op_,
118 const std::string & pattern,
119 Xapian::termcount max_expansion,
120 int max_type,
121 op combiner)
123 LOGCALL_CTOR(API, "Query", op_ | pattern | max_expansion | max_type | combiner);
124 if (rare(op_ != OP_WILDCARD))
125 throw Xapian::InvalidArgumentError("op must be OP_WILDCARD");
126 if (rare(combiner != OP_SYNONYM && combiner != OP_MAX && combiner != OP_OR))
127 throw Xapian::InvalidArgumentError("combiner must be OP_SYNONYM or OP_MAX or OP_OR");
128 internal = new Xapian::Internal::QueryWildcard(pattern,
129 max_expansion,
130 max_type,
131 combiner);
134 const TermIterator
135 Query::get_terms_begin() const
137 if (!internal.get())
138 return TermIterator();
140 vector<pair<Xapian::termpos, string> > terms;
141 internal->gather_terms(static_cast<void*>(&terms));
142 sort(terms.begin(), terms.end());
144 vector<string> v;
145 const string * old_term = NULL;
146 Xapian::termpos old_pos = 0;
147 for (auto && i : terms) {
148 // Remove duplicates (same term at the same position).
149 if (old_term && old_pos == i.first && *old_term == i.second)
150 continue;
152 v.push_back(i.second);
153 old_pos = i.first;
154 old_term = &(i.second);
156 return TermIterator(new VectorTermList(v.begin(), v.end()));
159 const TermIterator
160 Query::get_unique_terms_begin() const
162 if (!internal.get())
163 return TermIterator();
165 vector<pair<Xapian::termpos, string> > terms;
166 internal->gather_terms(static_cast<void*>(&terms));
167 sort(terms.begin(), terms.end(), [](
168 const pair<Xapian::termpos, string>& a,
169 const pair<Xapian::termpos, string>& b) {
170 return a.second < b.second;
173 vector<string> v;
174 const string * old_term = NULL;
175 for (auto && i : terms) {
176 // Remove duplicate term names.
177 if (old_term && *old_term == i.second)
178 continue;
180 v.push_back(i.second);
181 old_term = &(i.second);
183 return TermIterator(new VectorTermList(v.begin(), v.end()));
186 Xapian::termcount
187 Query::get_length() const XAPIAN_NOEXCEPT
189 return (internal.get() ? internal->get_length() : 0);
192 string
193 Query::serialise() const
195 string result;
196 if (internal.get())
197 internal->serialise(result);
198 return result;
201 const Query
202 Query::unserialise(const string & s, const Registry & reg)
204 const char * p = s.data();
205 const char * end = p + s.size();
206 Query::Internal * q = Query::Internal::unserialise(&p, end, reg);
207 AssertEq(p, end);
208 return Query(q);
211 Xapian::Query::op
212 Query::get_type() const XAPIAN_NOEXCEPT
214 if (!internal.get())
215 return Xapian::Query::LEAF_MATCH_NOTHING;
216 return internal->get_type();
219 size_t
220 Query::get_num_subqueries() const XAPIAN_NOEXCEPT
222 return internal.get() ? internal->get_num_subqueries() : 0;
225 const Query
226 Query::get_subquery(size_t n) const
228 return internal->get_subquery(n);
231 Xapian::termcount
232 Query::get_leaf_wqf() const
234 return internal->get_wqf();
237 Xapian::termpos
238 Query::get_leaf_pos() const
240 return internal->get_pos();
243 string
244 Query::get_description() const
246 string desc = "Query(";
247 if (internal.get())
248 desc += internal->get_description();
249 desc += ")";
250 return desc;
253 void
254 Query::init(op op_, size_t n_subqueries, Xapian::termcount parameter)
256 if (parameter > 0 &&
257 op_ != OP_NEAR && op_ != OP_PHRASE && op_ != OP_ELITE_SET)
258 throw InvalidArgumentError("parameter only valid with OP_NEAR, "
259 "OP_PHRASE or OP_ELITE_SET");
261 switch (op_) {
262 case OP_AND:
263 internal = new Xapian::Internal::QueryAnd(n_subqueries);
264 break;
265 case OP_OR:
266 internal = new Xapian::Internal::QueryOr(n_subqueries);
267 break;
268 case OP_AND_NOT:
269 internal = new Xapian::Internal::QueryAndNot(n_subqueries);
270 break;
271 case OP_XOR:
272 internal = new Xapian::Internal::QueryXor(n_subqueries);
273 break;
274 case OP_AND_MAYBE:
275 internal = new Xapian::Internal::QueryAndMaybe(n_subqueries);
276 break;
277 case OP_FILTER:
278 internal = new Xapian::Internal::QueryFilter(n_subqueries);
279 break;
280 case OP_NEAR:
281 internal = new Xapian::Internal::QueryNear(n_subqueries,
282 parameter);
283 break;
284 case OP_PHRASE:
285 internal = new Xapian::Internal::QueryPhrase(n_subqueries,
286 parameter);
287 break;
288 case OP_ELITE_SET:
289 internal = new Xapian::Internal::QueryEliteSet(n_subqueries,
290 parameter);
291 break;
292 case OP_SYNONYM:
293 internal = new Xapian::Internal::QuerySynonym(n_subqueries);
294 break;
295 case OP_MAX:
296 internal = new Xapian::Internal::QueryMax(n_subqueries);
297 break;
298 default:
299 if (op_ == OP_INVALID && n_subqueries == 0) {
300 internal = new Xapian::Internal::QueryInvalid();
301 break;
303 throw InvalidArgumentError("op not valid with a list of subqueries");
307 void
308 Query::add_subquery(bool positional, const Xapian::Query & subquery)
310 // We could handle this in a type-safe way, but we'd need to at least
311 // declare Xapian::Internal::QueryBranch in the API header, which seems
312 // less desirable than a static_cast<> here.
313 Xapian::Internal::QueryBranch * branch_query =
314 static_cast<Xapian::Internal::QueryBranch*>(internal.get());
315 Assert(branch_query);
316 if (positional) {
317 switch (subquery.get_type()) {
318 case LEAF_TERM:
319 break;
320 case LEAF_POSTING_SOURCE:
321 case LEAF_MATCH_ALL:
322 case LEAF_MATCH_NOTHING:
323 // None of these have positions, so positional operators won't
324 // match. Add MatchNothing as that is has special handling in
325 // AND-like queries to reduce the parent query to MatchNothing,
326 // which is appropriate in this case.
327 branch_query->add_subquery(MatchNothing);
328 return;
329 case OP_OR:
330 // OP_OR is now handled below OP_NEAR and OP_PHRASE.
331 break;
332 default:
333 throw Xapian::UnimplementedError("OP_NEAR and OP_PHRASE only currently support leaf subqueries");
336 branch_query->add_subquery(subquery);
339 void
340 Query::done()
342 Xapian::Internal::QueryBranch * branch_query =
343 static_cast<Xapian::Internal::QueryBranch*>(internal.get());
344 if (branch_query)
345 internal = branch_query->done();