2 * @brief Xapian::Query API class
4 /* Copyright (C) 2011,2012,2013,2015,2016,2017,2018 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
23 #include "xapian/query.h"
24 #include "queryinternal.h"
30 #include "vectortermlist.h"
32 #include "xapian/error.h"
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
71 if (!subquery
.internal
.get()) return;
72 switch (subquery
.internal
->get_type()) {
76 // These operators always return weight 0, so OP_SCALE_WEIGHT has
78 internal
= subquery
.internal
;
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
) {
92 internal
= new Xapian::Internal::QueryTerm();
94 internal
= new Xapian::Internal::QueryValueGE(slot
, limit
);
95 } else if (usual(op_
== OP_VALUE_LE
)) {
96 internal
= new Xapian::Internal::QueryValueLE(slot
, limit
);
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.
111 internal
= new Xapian::Internal::QueryValueLE(slot
, end
);
112 } else if (usual(begin
<= end
)) {
113 internal
= new Xapian::Internal::QueryValueRange(slot
, begin
, end
);
118 const std::string
& pattern
,
119 Xapian::termcount max_expansion
,
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
,
135 Query::get_terms_begin() const
138 return TermIterator();
140 vector
<pair
<Xapian::termpos
, string
>> terms
;
141 internal
->gather_terms(static_cast<void*>(&terms
));
142 sort(terms
.begin(), terms
.end());
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
)
152 v
.push_back(i
.second
);
154 old_term
= &(i
.second
);
156 return TermIterator(new VectorTermList(v
.begin(), v
.end()));
160 Query::get_unique_terms_begin() const
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
;
174 const string
* old_term
= NULL
;
175 for (auto && i
: terms
) {
176 // Remove duplicate term names.
177 if (old_term
&& *old_term
== i
.second
)
180 v
.push_back(i
.second
);
181 old_term
= &(i
.second
);
183 return TermIterator(new VectorTermList(v
.begin(), v
.end()));
187 Query::get_length() const XAPIAN_NOEXCEPT
189 return (internal
.get() ? internal
->get_length() : 0);
193 Query::serialise() const
197 internal
->serialise(result
);
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
);
212 Query::get_type() const XAPIAN_NOEXCEPT
215 return Xapian::Query::LEAF_MATCH_NOTHING
;
216 return internal
->get_type();
220 Query::get_num_subqueries() const XAPIAN_NOEXCEPT
222 return internal
.get() ? internal
->get_num_subqueries() : 0;
226 Query::get_subquery(size_t n
) const
228 return internal
->get_subquery(n
);
232 Query::get_description() const
234 string desc
= "Query(";
236 desc
+= internal
->get_description();
242 Query::init(op op_
, size_t n_subqueries
, Xapian::termcount parameter
)
245 op_
!= OP_NEAR
&& op_
!= OP_PHRASE
&& op_
!= OP_ELITE_SET
)
246 throw InvalidArgumentError("parameter only valid with OP_NEAR, "
247 "OP_PHRASE or OP_ELITE_SET");
251 internal
= new Xapian::Internal::QueryAnd(n_subqueries
);
254 internal
= new Xapian::Internal::QueryOr(n_subqueries
);
257 internal
= new Xapian::Internal::QueryAndNot(n_subqueries
);
260 internal
= new Xapian::Internal::QueryXor(n_subqueries
);
263 internal
= new Xapian::Internal::QueryAndMaybe(n_subqueries
);
266 internal
= new Xapian::Internal::QueryFilter(n_subqueries
);
269 internal
= new Xapian::Internal::QueryNear(n_subqueries
,
273 internal
= new Xapian::Internal::QueryPhrase(n_subqueries
,
277 internal
= new Xapian::Internal::QueryEliteSet(n_subqueries
,
281 internal
= new Xapian::Internal::QuerySynonym(n_subqueries
);
284 internal
= new Xapian::Internal::QueryMax(n_subqueries
);
287 if (op_
== OP_INVALID
&& n_subqueries
== 0) {
288 internal
= new Xapian::Internal::QueryInvalid();
291 throw InvalidArgumentError("op not valid with a list of subqueries");
296 Query::add_subquery(bool positional
, const Xapian::Query
& subquery
)
298 // We could handle this in a type-safe way, but we'd need to at least
299 // declare Xapian::Internal::QueryBranch in the API header, which seems
300 // less desirable than a static_cast<> here.
301 Xapian::Internal::QueryBranch
* branch_query
=
302 static_cast<Xapian::Internal::QueryBranch
*>(internal
.get());
303 Assert(branch_query
);
305 switch (subquery
.get_type()) {
308 case LEAF_POSTING_SOURCE
:
310 case LEAF_MATCH_NOTHING
:
311 // None of these have positions, so positional operators won't
312 // match. Add MatchNothing as that is has special handling in
313 // AND-like queries to reduce the parent query to MatchNothing,
314 // which is appropriate in this case.
315 branch_query
->add_subquery(MatchNothing
);
318 // OP_OR is now handled below OP_NEAR and OP_PHRASE.
321 throw Xapian::UnimplementedError("OP_NEAR and OP_PHRASE only currently support leaf subqueries");
324 branch_query
->add_subquery(subquery
);
330 Xapian::Internal::QueryBranch
* branch_query
=
331 static_cast<Xapian::Internal::QueryBranch
*>(internal
.get());
333 internal
= branch_query
->done();