Akonadi Search

term.cpp
1/*
2 * This file is part of the KDE Akonadi Search Project
3 * SPDX-FileCopyrightText: 2013 Vishesh Handa <me@vhanda.in>
4 *
5 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 *
7 */
8
9#include "term.h"
10
11#include <QDateTime>
12
13using namespace Akonadi::Search;
14
15class Akonadi::Search::TermPrivate
16{
17public:
18 Term::Operation m_op = Term::None;
19 Term::Comparator m_comp = Term::Auto;
20
21 QString m_property;
22 QVariant m_value;
23
24 bool m_isNegated = false;
25
26 QList<Term> m_subTerms;
27 QVariantHash m_userData;
28};
29
30Term::Term()
31 : d(new TermPrivate)
32{
33}
34
35Term::Term(const Term &t)
36 : d(new TermPrivate(*t.d))
37{
38}
39
40Term::Term(const QString &property)
41 : d(new TermPrivate)
42{
43 d->m_property = property;
44}
45
46Term::Term(const QString &property, const QVariant &value, Term::Comparator c)
47 : d(new TermPrivate)
48{
49 d->m_property = property;
50 d->m_value = value;
51
52 if (c == Auto) {
53 auto valueType = value.metaType().id();
54 if (valueType == QMetaType::QString) {
55 d->m_comp = Contains;
56 } else if (valueType == QMetaType::QDateTime) {
57 d->m_comp = Contains;
58 } else {
59 d->m_comp = Equal;
60 }
61 } else {
62 d->m_comp = c;
63 }
64}
65
66/*
67Term::Term(const QString& property, const QVariant& start, const QVariant& end)
68 : d(new TermPrivate)
69{
70 d->m_property = property;
71 d->m_op = Range;
72
73 // FIXME: How to save range queries?
74}
75*/
76
77Term::Term(Term::Operation op)
78 : d(new TermPrivate)
79{
80 d->m_op = op;
81}
82
83Term::Term(Term::Operation op, const Term &t)
84 : d(new TermPrivate)
85{
86 d->m_op = op;
87 d->m_subTerms << t;
88}
89
90Term::Term(Term::Operation op, const QList<Term> &t)
91 : d(new TermPrivate)
92{
93 d->m_op = op;
94 d->m_subTerms = t;
95}
96
97Term::Term(const Term &lhs, Term::Operation op, const Term &rhs)
98 : d(new TermPrivate)
99{
100 d->m_op = op;
101 d->m_subTerms << lhs;
102 d->m_subTerms << rhs;
103}
104
105Term::~Term() = default;
106
107bool Term::isValid() const
108{
109 if (d->m_property.isEmpty()) {
110 if (d->m_op == Term::None) {
111 return false;
112 }
113
114 return d->m_property.isEmpty() && d->m_value.isNull();
115 }
116
117 return true;
118}
119
120void Term::setNegation(bool isNegated)
121{
122 d->m_isNegated = isNegated;
123}
124
125bool Term::isNegated() const
126{
127 return d->m_isNegated;
128}
129
130bool Term::negated() const
131{
132 return d->m_isNegated;
133}
134
135void Term::addSubTerm(const Term &term)
136{
137 d->m_subTerms << term;
138}
139
140void Term::setSubTerms(const QList<Term> &terms)
141{
142 d->m_subTerms = terms;
143}
144
146{
147 if (!d->m_subTerms.isEmpty()) {
148 return d->m_subTerms.first();
149 }
150
151 return {};
152}
153
154QList<Term> Term::subTerms() const
155{
156 return d->m_subTerms;
157}
158
159void Term::setOperation(Term::Operation op)
160{
161 d->m_op = op;
162}
163
164Term::Operation Term::operation() const
165{
166 return d->m_op;
167}
168
169bool Term::empty() const
170{
171 return isEmpty();
172}
173
174bool Term::isEmpty() const
175{
176 return d->m_property.isEmpty() && d->m_value.isNull() && d->m_subTerms.isEmpty();
177}
178
180{
181 return d->m_property;
182}
183
184void Term::setProperty(const QString &property)
185{
186 d->m_property = property;
187}
188
189void Term::setValue(const QVariant &value)
190{
191 d->m_value = value;
192}
193
194QVariant Term::value() const
195{
196 return d->m_value;
197}
198
199Term::Comparator Term::comparator() const
200{
201 return d->m_comp;
202}
203
204void Term::setComparator(Term::Comparator c)
205{
206 d->m_comp = c;
207}
208
209void Term::setUserData(const QString &name, const QVariant &value)
210{
211 d->m_userData.insert(name, value);
212}
213
214QVariant Term::userData(const QString &name) const
215{
216 return d->m_userData.value(name);
217}
218
219QVariantMap Term::toVariantMap() const
220{
221 QVariantMap map;
222 if (d->m_op != None) {
223 QVariantList variantList;
224 variantList.reserve(d->m_subTerms.count());
225 for (const Term &term : std::as_const(d->m_subTerms)) {
226 variantList << QVariant(term.toVariantMap());
227 }
228
229 if (d->m_op == And) {
230 map[QStringLiteral("$and")] = variantList;
231 } else {
232 map[QStringLiteral("$or")] = variantList;
233 }
234
235 return map;
236 }
237
238 QString op;
239 switch (d->m_comp) {
240 case Equal:
241 map[d->m_property] = d->m_value;
242 return map;
243
244 case Contains:
245 op = QStringLiteral("$ct");
246 break;
247
248 case Greater:
249 op = QStringLiteral("$gt");
250 break;
251
252 case GreaterEqual:
253 op = QStringLiteral("$gte");
254 break;
255
256 case Less:
257 op = QStringLiteral("$lt");
258 break;
259
260 case LessEqual:
261 op = QStringLiteral("$lte");
262 break;
263
264 default:
265 return {};
266 }
267
268 QVariantMap m;
269 m[op] = d->m_value;
270 map[d->m_property] = QVariant(m);
271
272 return map;
273}
274
275namespace
276{
277// QJson does not recognize QDate/QDateTime parameters. We try to guess
278// and see if they can be converted into date/datetime.
279QVariant tryConvert(const QVariant &var)
280{
281 if (var.canConvert<QDateTime>()) {
282 QDateTime dt = var.toDateTime();
283 if (!dt.isValid()) {
284 return var;
285 }
286
287 if (!var.toString().contains(QLatin1Char('T'))) {
288 return QVariant(var.toDate());
289 }
290 return dt;
291 }
292 return var;
293}
294}
295
296Term Term::fromVariantMap(const QVariantMap &map)
297{
298 if (map.size() != 1) {
299 return {};
300 }
301
302 Term term;
303
304 QString andOrString;
305 if (map.contains(QStringLiteral("$and"))) {
306 andOrString = QStringLiteral("$and");
307 term.setOperation(And);
308 } else if (map.contains(QStringLiteral("$or"))) {
309 andOrString = QStringLiteral("$or");
310 term.setOperation(Or);
311 }
312
313 if (andOrString.size()) {
314 QList<Term> subTerms;
315
316 const QVariantList list = map[andOrString].toList();
317 subTerms.reserve(list.count());
318 for (const QVariant &var : list) {
319 subTerms << Term::fromVariantMap(var.toMap());
320 }
321
322 term.setSubTerms(subTerms);
323 return term;
324 }
325
326 QString prop = map.cbegin().key();
327 term.setProperty(prop);
328
329 QVariant value = map.value(prop);
330 if (value.userType() == QMetaType::QVariantMap) {
331 QVariantMap map = value.toMap();
332 if (map.size() != 1) {
333 return term;
334 }
335
336 QString op = map.cbegin().key();
337 Term::Comparator com;
338 if (op == QLatin1StringView("$ct")) {
339 com = Contains;
340 } else if (op == QLatin1StringView("$gt")) {
341 com = Greater;
342 } else if (op == QLatin1StringView("$gte")) {
343 com = GreaterEqual;
344 } else if (op == QLatin1StringView("$lt")) {
345 com = Less;
346 } else if (op == QLatin1StringView("$lte")) {
347 com = LessEqual;
348 } else {
349 return term;
350 }
351
352 term.setComparator(com);
353 term.setValue(tryConvert(map.value(op)));
354
355 return term;
356 }
357
358 term.setComparator(Equal);
359 term.setValue(tryConvert(value));
360
361 return term;
362}
363
364bool Term::operator==(const Term &rhs) const
365{
366 if (d->m_op != rhs.d->m_op || d->m_comp != rhs.d->m_comp || d->m_isNegated != rhs.d->m_isNegated || d->m_property != rhs.d->m_property
367 || d->m_value != rhs.d->m_value) {
368 return false;
369 }
370
371 if (d->m_subTerms.size() != rhs.d->m_subTerms.size()) {
372 return false;
373 }
374
375 if (d->m_subTerms.isEmpty()) {
376 return true;
377 }
378
379 for (const Term &t : std::as_const(d->m_subTerms)) {
380 if (!rhs.d->m_subTerms.contains(t)) {
381 return false;
382 }
383 }
384
385 return true;
386}
387
388Term &Term::operator=(const Term &rhs)
389{
390 *d = *rhs.d;
391 return *this;
392}
393
394namespace
395{
396QString comparatorToString(Term::Comparator c)
397{
398 switch (c) {
399 case Term::Auto:
400 return QStringLiteral("Auto");
401 case Term::Equal:
402 return QStringLiteral("=");
403 case Term::Contains:
404 return QStringLiteral(":");
405 case Term::Less:
406 return QStringLiteral("<");
407 case Term::LessEqual:
408 return QStringLiteral("<=");
409 case Term::Greater:
410 return QStringLiteral(">");
411 case Term::GreaterEqual:
412 return QStringLiteral(">=");
413 }
414
415 return {};
416}
417
418QString operationToString(Term::Operation op)
419{
420 switch (op) {
421 case Term::None:
422 return QStringLiteral("NONE");
423 case Term::And:
424 return QStringLiteral("AND");
425 case Term::Or:
426 return QStringLiteral("OR");
427 }
428
429 return {};
430}
431} // namespace
432
433QDebug operator<<(QDebug d, const Term &t)
434{
435 if (t.subTerms().isEmpty()) {
436 d << QStringLiteral("(%1 %2 %3 (%4))")
437 .arg(t.property(), comparatorToString(t.comparator()), t.value().toString(), QString::fromLatin1(t.value().typeName()))
438 .toUtf8()
439 .constData();
440 } else {
441 d << "(" << operationToString(t.operation()).toUtf8().constData();
442 const QList<Term> subterms = t.subTerms();
443 for (const Term &term : std::as_const(subterms)) {
444 d << term;
445 }
446 d << ")";
447 }
448 return d;
449}
Search term.
Definition term.h:27
void setNegation(bool isNegated)
Negate this term.
Definition term.cpp:120
QString property() const
Return the property this term is targeting.
Definition term.cpp:179
Term subTerm() const
Returns the first subTerm in the list of subTerms.
Definition term.cpp:145
Akonadi search infrastructure.
Definition core/query.h:21
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QDebug operator<<(QDebug dbg, const PerceptualColor::LchaDouble &value)
bool isValid() const const
qsizetype count() const const
void reserve(qsizetype size)
int id() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromLatin1(QByteArrayView str)
qsizetype size() const const
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
bool canConvert() const const
QMetaType metaType() const const
QDate toDate() const const
QDateTime toDateTime() const const
QMap< QString, QVariant > toMap() const const
QString toString() const const
const char * typeName() const const
int userType() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:15:27 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.