Akonadi Search

core/query.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 "query.h"
10using namespace Qt::Literals::StringLiterals;
11
12#include "searchstore.h"
13#include "term.h"
14
15#include <QSharedPointer>
16#include <QString>
17#include <QStringList>
18#include <QUrlQuery>
19#include <QVariant>
20
21#include <QJsonDocument>
22#include <QJsonObject>
23
24using namespace Akonadi::Search;
25
26const int defaultLimit = 100000;
27
28class Akonadi::Search::QueryPrivate
29{
30public:
31 Term m_term;
32
33 QStringList m_types;
34 QString m_searchString;
35 uint m_limit = defaultLimit;
36 uint m_offset = 0;
37
38 int m_yearFilter = -1;
39 int m_monthFilter = -1;
40 int m_dayFilter = -1;
41
42 Query::SortingOption m_sortingOption = Query::SortAuto;
43 QString m_sortingProperty;
44 QVariantMap m_customOptions;
45};
46
47Query::Query()
48 : d(new QueryPrivate)
49{
50}
51
52Query::Query(const Term &t)
53 : d(new QueryPrivate)
54{
55 d->m_term = t;
56}
57
58Query::Query(const Query &rhs)
59 : d(new QueryPrivate(*rhs.d))
60{
61}
62
63Query::~Query() = default;
64
65void Query::setTerm(const Term &t)
66{
67 d->m_term = t;
68}
69
70Term Query::term() const
71{
72 return d->m_term;
73}
74
75void Query::addType(const QString &type)
76{
77 d->m_types << type.split(QLatin1Char('/'), Qt::SkipEmptyParts);
78}
79
80void Query::addTypes(const QStringList &typeList)
81{
82 for (const QString &type : typeList) {
83 addType(type);
84 }
85}
86
87void Query::setType(const QString &type)
88{
89 d->m_types.clear();
90 addType(type);
91}
92
93void Query::setTypes(const QStringList &types)
94{
95 d->m_types = types;
96}
97
98QStringList Query::types() const
99{
100 return d->m_types;
101}
102
103QString Query::searchString() const
104{
105 return d->m_searchString;
106}
107
109{
110 d->m_searchString = str;
111}
112
113uint Query::limit() const
114{
115 return d->m_limit;
116}
117
118void Query::setLimit(uint limit)
119{
120 d->m_limit = limit;
121}
122
123uint Query::offset() const
124{
125 return d->m_offset;
126}
127
128void Query::setOffset(uint offset)
129{
130 d->m_offset = offset;
131}
132
133void Query::setDateFilter(int year, int month, int day)
134{
135 d->m_yearFilter = year;
136 d->m_monthFilter = month;
137 d->m_dayFilter = day;
138}
139
140int Query::yearFilter() const
141{
142 return d->m_yearFilter;
143}
144
145int Query::monthFilter() const
146{
147 return d->m_monthFilter;
148}
149
150int Query::dayFilter() const
151{
152 return d->m_dayFilter;
153}
154
155void Query::setSortingOption(Query::SortingOption option)
156{
157 d->m_sortingOption = option;
158}
159
160Query::SortingOption Query::sortingOption() const
161{
162 return d->m_sortingOption;
163}
164
166{
167 d->m_sortingOption = SortProperty;
168 d->m_sortingProperty = property;
169}
170
171QString Query::sortingProperty() const
172{
173 return d->m_sortingProperty;
174}
175
176void Query::addCustomOption(const QString &option, const QVariant &value)
177{
178 d->m_customOptions.insert(option, value);
179}
180
181QVariant Query::customOption(const QString &option) const
182{
183 return d->m_customOptions.value(option);
184}
185
186QVariantMap Query::customOptions() const
187{
188 return d->m_customOptions;
189}
190
191void Query::removeCustomOption(const QString &option)
192{
193 d->m_customOptions.remove(option);
194}
195
196Q_GLOBAL_STATIC_WITH_ARGS(SearchStore::List, s_searchStores, (SearchStore::searchStores()))
197
198ResultIterator Query::exec()
199{
200 // vHanda: Maybe this should default to allow searches on all search stores?
201 Q_ASSERT_X(!types().isEmpty(), "Akonadi::Search::Query::exec", "A query is being initialized without a type");
202 if (types().isEmpty()) {
203 return {};
204 }
205
206 SearchStore *storeMatch = nullptr;
207 for (const QSharedPointer<SearchStore> &store : std::as_const(*s_searchStores)) {
208 bool matches = true;
209 const auto typeList{types()};
210 for (const QString &type : typeList) {
211 if (!store->types().contains(type)) {
212 matches = false;
213 break;
214 }
215 }
216
217 if (matches) {
218 storeMatch = store.data();
219 break;
220 }
221 }
222
223 if (!storeMatch) {
224 return {};
225 }
226
227 int id = storeMatch->exec(*this);
228 return {id, storeMatch};
229}
230
231QByteArray Query::toJSON() const
232{
233 QVariantMap map;
234
235 if (!d->m_types.isEmpty()) {
236 map[QStringLiteral("type")] = d->m_types;
237 }
238
239 if (d->m_limit != defaultLimit) {
240 map[QStringLiteral("limit")] = d->m_limit;
241 }
242
243 if (d->m_offset) {
244 map[QStringLiteral("offset")] = d->m_offset;
245 }
246
247 if (!d->m_searchString.isEmpty()) {
248 map[QStringLiteral("searchString")] = d->m_searchString;
249 }
250
251 if (d->m_term.isValid()) {
252 map[QStringLiteral("term")] = QVariant(d->m_term.toVariantMap());
253 }
254
255 if (d->m_yearFilter >= 0) {
256 map[QStringLiteral("yearFilter")] = d->m_yearFilter;
257 }
258 if (d->m_monthFilter >= 0) {
259 map[QStringLiteral("monthFilter")] = d->m_monthFilter;
260 }
261 if (d->m_dayFilter >= 0) {
262 map[QStringLiteral("dayFilter")] = d->m_dayFilter;
263 }
264
265 if (d->m_sortingOption != SortAuto) {
266 map[QStringLiteral("sortingOption")] = static_cast<int>(d->m_sortingOption);
267 }
268 if (!d->m_sortingProperty.isEmpty()) {
269 map[QStringLiteral("sortingProperty")] = d->m_sortingProperty;
270 }
271
272 if (!d->m_customOptions.isEmpty()) {
273 map[QStringLiteral("customOptions")] = d->m_customOptions;
274 }
275
277 QJsonDocument jdoc;
278 jdoc.setObject(jo);
279 return jdoc.toJson();
280}
281
282// static
283Query Query::fromJSON(const QByteArray &arr)
284{
286 const QVariantMap map = jdoc.object().toVariantMap();
287
288 Query query;
289 query.d->m_types = map[QStringLiteral("type")].toStringList();
290
291 if (map.contains(QStringLiteral("limit"))) {
292 query.d->m_limit = map[QStringLiteral("limit")].toUInt();
293 } else {
294 query.d->m_limit = defaultLimit;
295 }
296
297 query.d->m_offset = map[QStringLiteral("offset")].toUInt();
298 query.d->m_searchString = map[QStringLiteral("searchString")].toString();
299 query.d->m_term = Term::fromVariantMap(map[QStringLiteral("term")].toMap());
300
301 if (map.contains(QStringLiteral("yearFilter"))) {
302 query.d->m_yearFilter = map[QStringLiteral("yearFilter")].toInt();
303 }
304 if (map.contains(QStringLiteral("monthFilter"))) {
305 query.d->m_monthFilter = map[QStringLiteral("monthFilter")].toInt();
306 }
307 if (map.contains(QStringLiteral("dayFilter"))) {
308 query.d->m_dayFilter = map[QStringLiteral("dayFilter")].toInt();
309 }
310
311 if (map.contains(QStringLiteral("sortingOption"))) {
312 int option = map.value(QStringLiteral("sortingOption")).toInt();
313 query.d->m_sortingOption = static_cast<SortingOption>(option);
314 }
315
316 if (map.contains(QStringLiteral("sortingProperty"))) {
317 query.d->m_sortingProperty = map.value(QStringLiteral("sortingProperty")).toString();
318 }
319
320 if (map.contains(QStringLiteral("customOptions"))) {
321 QVariant var = map[QStringLiteral("customOptions")];
322 if (var.userType() == QMetaType::QVariantMap) {
323 query.d->m_customOptions = map[QStringLiteral("customOptions")].toMap();
324 } else if (var.userType() == QMetaType::QVariantHash) {
325 QVariantHash hash = var.toHash();
326
327 QHash<QString, QVariant>::const_iterator it = hash.constBegin();
328 const QHash<QString, QVariant>::const_iterator end = hash.constEnd();
329 for (; it != end; ++it) {
330 query.d->m_customOptions.insert(it.key(), it.value());
331 }
332 }
333 }
334
335 return query;
336}
337
338QUrl Query::toSearchUrl(const QString &title)
339{
340 QUrl url;
341 url.setScheme(QStringLiteral("akonadisearch"));
342
343 QUrlQuery urlQuery;
344 urlQuery.addQueryItem(QStringLiteral("json"), QString::fromUtf8(toJSON()));
345
346 if (!title.isEmpty()) {
347 urlQuery.addQueryItem(QStringLiteral("title"), title);
348 }
349
350 url.setQuery(urlQuery);
351 return url;
352}
353
354Query Query::fromSearchUrl(const QUrl &url)
355{
356 if (url.scheme() != "akonadisearch"_L1) {
357 return {};
358 }
359
360 QUrlQuery urlQuery(url);
361 const QString jsonString = urlQuery.queryItemValue(QStringLiteral("json"), QUrl::FullyDecoded);
362 return Query::fromJSON(jsonString.toUtf8());
363}
364
365QString Query::titleFromQueryUrl(const QUrl &url)
366{
367 QUrlQuery urlQuery(url);
368 return urlQuery.queryItemValue(QStringLiteral("title"), QUrl::FullyDecoded);
369}
370
371bool Query::operator==(const Query &rhs) const
372{
373 if (rhs.d->m_limit != d->m_limit || rhs.d->m_offset != d->m_offset || rhs.d->m_dayFilter != d->m_dayFilter || rhs.d->m_monthFilter != d->m_monthFilter
374 || rhs.d->m_yearFilter != d->m_yearFilter || rhs.d->m_customOptions != d->m_customOptions || rhs.d->m_searchString != d->m_searchString
375 || rhs.d->m_sortingProperty != d->m_sortingProperty || rhs.d->m_sortingOption != d->m_sortingOption) {
376 return false;
377 }
378
379 if (rhs.d->m_types.size() != d->m_types.size()) {
380 return false;
381 }
382
383 for (const QString &type : std::as_const(rhs.d->m_types)) {
384 if (!d->m_types.contains(type)) {
385 return false;
386 }
387 }
388
389 return d->m_term == rhs.d->m_term;
390}
391
392Query &Query::operator=(const Query &rhs)
393{
394 *d = *rhs.d;
395 return *this;
396}
void setLimit(uint limit)
Only a maximum of limit results will be returned.
void addCustomOption(const QString &option, const QVariant &value)
Adds a custom option which any search backend could use to configure the query result.
void setDateFilter(int year, int month=-1, int day=-1)
Filter the results in the specified date range.
void addType(const QString &type)
Add a type to the results of the query.
void setSearchString(const QString &str)
Set some text which should be used to search for Items.
void setSortingProperty(const QString &property)
Sets the property that should be used for sorting.
@ SortProperty
The results are returned based on the explicit property specified.
Definition core/query.h:106
@ SortAuto
The results are returned in the order the SearchStore decides should be ideal.
Definition core/query.h:100
virtual int exec(const Query &query)=0
Executes the particular query synchronously.
static List searchStores()
Gives a list of available search stores.
Search term.
Definition term.h:27
Akonadi search infrastructure.
Definition core/query.h:21
std::optional< QSqlQuery > query(const QString &queryStatement)
const QVariantMap toMap(const MODEL &model)
const QList< QKeySequence > & end()
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
void setObject(const QJsonObject &object)
QByteArray toJson(JsonFormat format) const const
QJsonObject fromVariantMap(const QVariantMap &map)
QVariantMap toVariantMap() const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QByteArray toUtf8() const const
SkipEmptyParts
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
FullyDecoded
QString scheme() const const
void setQuery(const QString &query, ParsingMode mode)
void setScheme(const QString &scheme)
void addQueryItem(const QString &key, const QString &value)
QString queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding) const const
QHash< QString, QVariant > toHash() const const
int userType() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:56:07 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.