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