Website now in git not CVS
[xapian.git] / xapian-core / api / query.cc
blob316d23c788e36d22c970415843bd4d61ce73cc70
1 /** @file query.cc
2 * @brief Xapian::Query API class
3 */
4 /* Copyright (C) 2011,2012,2013,2015 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 using namespace std;
34 namespace Xapian {
36 // Extra () are needed to resolve ambiguity with method declaration.
37 const Query Query::MatchAll((string()));
39 const Query Query::MatchNothing;
41 Query::Query(const string & term, Xapian::termcount wqf, Xapian::termpos pos)
42 : internal(new Xapian::Internal::QueryTerm(term, wqf, pos))
44 LOGCALL_CTOR(API, "Query", term | wqf | pos);
47 Query::Query(Xapian::PostingSource * source)
48 : internal(new Xapian::Internal::QueryPostingSource(source))
50 LOGCALL_CTOR(API, "Query", source);
53 Query::Query(double factor, const Xapian::Query & subquery)
55 LOGCALL_CTOR(API, "Query", factor | subquery);
57 if (!subquery.empty())
58 internal = new Xapian::Internal::QueryScaleWeight(factor, subquery);
61 Query::Query(op op_, const Xapian::Query & subquery, double factor)
63 LOGCALL_CTOR(API, "Query", op_ | subquery | factor);
65 if (rare(op_ != OP_SCALE_WEIGHT))
66 throw Xapian::InvalidArgumentError("op must be OP_SCALE_WEIGHT");
67 // If the subquery is MatchNothing then generate Query() which matches
68 // nothing.
69 if (subquery.internal.get())
70 internal = new Xapian::Internal::QueryScaleWeight(factor, subquery);
73 Query::Query(op op_, Xapian::valueno slot, const std::string & limit)
75 LOGCALL_CTOR(API, "Query", op_ | slot | limit);
77 if (op_ == OP_VALUE_GE) {
78 if (limit.empty())
79 internal = MatchAll.internal;
80 else
81 internal = new Xapian::Internal::QueryValueGE(slot, limit);
82 } else if (usual(op_ == OP_VALUE_LE)) {
83 internal = new Xapian::Internal::QueryValueLE(slot, limit);
84 } else {
85 throw Xapian::InvalidArgumentError("op must be OP_VALUE_LE or OP_VALUE_GE");
89 Query::Query(op op_, Xapian::valueno slot,
90 const std::string & begin, const std::string & end)
92 LOGCALL_CTOR(API, "Query", op_ | slot | begin | end);
94 if (rare(op_ != OP_VALUE_RANGE))
95 throw Xapian::InvalidArgumentError("op must be OP_VALUE_RANGE");
96 // If begin > end then generate Query() which matches nothing.
97 if (begin.empty()) {
98 internal = new Xapian::Internal::QueryValueLE(slot, end);
99 } else if (usual(begin <= end)) {
100 internal = new Xapian::Internal::QueryValueRange(slot, begin, end);
104 Query::Query(op op_,
105 const std::string & pattern,
106 Xapian::termcount max_expansion,
107 int max_type,
108 op combiner)
110 LOGCALL_CTOR(API, "Query", op_ | pattern | max_expansion | max_type | combiner);
111 if (rare(op_ != OP_WILDCARD))
112 throw Xapian::InvalidArgumentError("op must be OP_WILDCARD");
113 if (rare(combiner != OP_SYNONYM && combiner != OP_MAX && combiner != OP_OR))
114 throw Xapian::InvalidArgumentError("combiner must be OP_SYNONYM or OP_MAX or OP_OR");
115 internal = new Xapian::Internal::QueryWildcard(pattern,
116 max_expansion,
117 max_type,
118 combiner);
121 const TermIterator
122 Query::get_terms_begin() const
124 if (!internal.get())
125 return TermIterator();
127 vector<pair<Xapian::termpos, string> > terms;
128 internal->gather_terms(static_cast<void*>(&terms));
129 sort(terms.begin(), terms.end());
131 vector<string> v;
132 const string * old_term = NULL;
133 Xapian::termpos old_pos = 0;
134 for (auto && i : terms) {
135 // Remove duplicates (same term at the same position).
136 if (old_term && old_pos == i.first && *old_term == i.second)
137 continue;
139 v.push_back(i.second);
140 old_pos = i.first;
141 old_term = &(i.second);
143 return TermIterator(new VectorTermList(v.begin(), v.end()));
146 const TermIterator
147 Query::get_unique_terms_begin() const
149 if (!internal.get())
150 return TermIterator();
152 vector<pair<Xapian::termpos, string> > terms;
153 internal->gather_terms(static_cast<void*>(&terms));
154 sort(terms.begin(), terms.end(), [](
155 const pair<Xapian::termpos, string>& a,
156 const pair<Xapian::termpos, string>& b) {
157 return a.second < b.second;
160 vector<string> v;
161 const string * old_term = NULL;
162 for (auto && i : terms) {
163 // Remove duplicate term names.
164 if (old_term && *old_term == i.second)
165 continue;
167 v.push_back(i.second);
168 old_term = &(i.second);
170 return TermIterator(new VectorTermList(v.begin(), v.end()));
173 Xapian::termcount
174 Query::get_length() const XAPIAN_NOEXCEPT
176 return (internal.get() ? internal->get_length() : 0);
179 string
180 Query::serialise() const
182 string result;
183 if (internal.get())
184 internal->serialise(result);
185 return result;
188 const Query
189 Query::unserialise(const string & s, const Registry & reg)
191 const char * p = s.data();
192 const char * end = p + s.size();
193 Query::Internal * q = Query::Internal::unserialise(&p, end, reg);
194 AssertEq(p, end);
195 return Query(q);
198 Xapian::Query::op
199 Query::get_type() const XAPIAN_NOEXCEPT
201 if (!internal.get())
202 return Xapian::Query::LEAF_MATCH_NOTHING;
203 return internal->get_type();
206 size_t
207 Query::get_num_subqueries() const XAPIAN_NOEXCEPT
209 return internal.get() ? internal->get_num_subqueries() : 0;
212 const Query
213 Query::get_subquery(size_t n) const
215 return internal->get_subquery(n);
218 string
219 Query::get_description() const
221 string desc = "Query(";
222 if (internal.get())
223 desc += internal->get_description();
224 desc += ")";
225 return desc;
228 void
229 Query::init(op op_, size_t n_subqueries, Xapian::termcount parameter)
231 if (parameter > 0 &&
232 op_ != OP_NEAR && op_ != OP_PHRASE && op_ != OP_ELITE_SET)
233 throw InvalidArgumentError("parameter only valid with OP_NEAR, "
234 "OP_PHRASE or OP_ELITE_SET");
236 switch (op_) {
237 case OP_AND:
238 internal = new Xapian::Internal::QueryAnd(n_subqueries);
239 break;
240 case OP_OR:
241 internal = new Xapian::Internal::QueryOr(n_subqueries);
242 break;
243 case OP_AND_NOT:
244 internal = new Xapian::Internal::QueryAndNot(n_subqueries);
245 break;
246 case OP_XOR:
247 internal = new Xapian::Internal::QueryXor(n_subqueries);
248 break;
249 case OP_AND_MAYBE:
250 internal = new Xapian::Internal::QueryAndMaybe(n_subqueries);
251 break;
252 case OP_FILTER:
253 internal = new Xapian::Internal::QueryFilter(n_subqueries);
254 break;
255 case OP_NEAR:
256 internal = new Xapian::Internal::QueryNear(n_subqueries,
257 parameter);
258 break;
259 case OP_PHRASE:
260 internal = new Xapian::Internal::QueryPhrase(n_subqueries,
261 parameter);
262 break;
263 case OP_ELITE_SET:
264 internal = new Xapian::Internal::QueryEliteSet(n_subqueries,
265 parameter);
266 break;
267 case OP_SYNONYM:
268 internal = new Xapian::Internal::QuerySynonym(n_subqueries);
269 break;
270 case OP_MAX:
271 internal = new Xapian::Internal::QueryMax(n_subqueries);
272 break;
273 default:
274 throw InvalidArgumentError("op not valid with a list of subqueries");
278 void
279 Query::add_subquery(const Xapian::Query & subquery)
281 // We could handle this in a type-safe way, but we'd need to at least
282 // declare Xapian::Internal::QueryBranch in the API header, which seems
283 // less desirable than a static_cast<> here.
284 Xapian::Internal::QueryBranch * branch_query =
285 static_cast<Xapian::Internal::QueryBranch*>(internal.get());
286 Assert(branch_query);
287 branch_query->add_subquery(subquery);
290 void
291 Query::done()
293 Xapian::Internal::QueryBranch * branch_query =
294 static_cast<Xapian::Internal::QueryBranch*>(internal.get());
295 if (branch_query)
296 internal = branch_query->done();