Akonadi Search

core/query.cpp
1 /*
2  * This file is part of the KDE Akonadi Search Project
3  * SPDX-FileCopyrightText: 2013 Vishesh Handa <[email protected]>
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 
22 using namespace Akonadi::Search;
23 
24 const int defaultLimit = 100000;
25 
26 class Akonadi::Search::QueryPrivate
27 {
28 public:
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 
45 Query::Query()
46  : d(new QueryPrivate)
47 {
48 }
49 
50 Query::Query(const Term &t)
51  : d(new QueryPrivate)
52 {
53  d->m_term = t;
54 }
55 
56 Query::Query(const Query &rhs)
57  : d(new QueryPrivate(*rhs.d))
58 {
59 }
60 
61 Query::~Query() = default;
62 
63 void Query::setTerm(const Term &t)
64 {
65  d->m_term = t;
66 }
67 
68 Term Query::term() const
69 {
70  return d->m_term;
71 }
72 
73 void Query::addType(const QString &type)
74 {
75  d->m_types << type.split(QLatin1Char('/'), Qt::SkipEmptyParts);
76 }
77 
78 void Query::addTypes(const QStringList &typeList)
79 {
80  for (const QString &type : typeList) {
81  addType(type);
82  }
83 }
84 
85 void Query::setType(const QString &type)
86 {
87  d->m_types.clear();
88  addType(type);
89 }
90 
91 void Query::setTypes(const QStringList &types)
92 {
93  d->m_types = types;
94 }
95 
96 QStringList Query::types() const
97 {
98  return d->m_types;
99 }
100 
101 QString Query::searchString() const
102 {
103  return d->m_searchString;
104 }
105 
107 {
108  d->m_searchString = str;
109 }
110 
111 uint Query::limit() const
112 {
113  return d->m_limit;
114 }
115 
116 void Query::setLimit(uint limit)
117 {
118  d->m_limit = limit;
119 }
120 
121 uint Query::offset() const
122 {
123  return d->m_offset;
124 }
125 
126 void Query::setOffset(uint offset)
127 {
128  d->m_offset = offset;
129 }
130 
131 void 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 
138 int Query::yearFilter() const
139 {
140  return d->m_yearFilter;
141 }
142 
143 int Query::monthFilter() const
144 {
145  return d->m_monthFilter;
146 }
147 
148 int Query::dayFilter() const
149 {
150  return d->m_dayFilter;
151 }
152 
153 void Query::setSortingOption(Query::SortingOption option)
154 {
155  d->m_sortingOption = option;
156 }
157 
158 Query::SortingOption Query::sortingOption() const
159 {
160  return d->m_sortingOption;
161 }
162 
163 void Query::setSortingProperty(const QString &property)
164 {
165  d->m_sortingOption = SortProperty;
166  d->m_sortingProperty = property;
167 }
168 
169 QString Query::sortingProperty() const
170 {
171  return d->m_sortingProperty;
172 }
173 
174 void Query::addCustomOption(const QString &option, const QVariant &value)
175 {
176  d->m_customOptions.insert(option, value);
177 }
178 
179 QVariant Query::customOption(const QString &option) const
180 {
181  return d->m_customOptions.value(option);
182 }
183 
184 QVariantMap Query::customOptions() const
185 {
186  return d->m_customOptions;
187 }
188 
189 void Query::removeCustomOption(const QString &option)
190 {
191  d->m_customOptions.remove(option);
192 }
193 
194 Q_GLOBAL_STATIC_WITH_ARGS(SearchStore::List, s_searchStores, (SearchStore::searchStores()))
195 
196 ResultIterator 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 
229 QByteArray 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
281 Query 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 
336 QUrl 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 
352 Query Query::fromSearchUrl(const QUrl &url)
353 {
354  if (url.scheme() != QLatin1String("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 
363 QString Query::titleFromQueryUrl(const QUrl &url)
364 {
365  QUrlQuery urlQuery(url);
366  return urlQuery.queryItemValue(QStringLiteral("title"), QUrl::FullyDecoded);
367 }
368 
369 bool 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 
390 Query &Query::operator=(const Query &rhs)
391 {
392  *d = *rhs.d;
393  return *this;
394 }
QJsonObject object() const const
@ SortAuto
The results are returned in the order the SearchStore decides should be ideal.
Definition: core/query.h:100
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QString fromUtf8(const char *str, int size)
QString scheme() const const
void addQueryItem(const QString &key, const QString &value)
T value() const const
QVariantMap toVariantMap() const const
QJsonObject fromVariantMap(const QVariantMap &map)
virtual int exec(const Query &query)=0
Executes the particular query synchronously.
void setScheme(const QString &scheme)
QStringList types(Mode mode=Writing)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
int userType() const const
Akonadi search infrastructure.
Definition: core/query.h:20
void setSortingProperty(const QString &property)
Sets the property that should be used for sorting.
Definition: core/query.cpp:163
FullyDecoded
SkipEmptyParts
void addType(const QString &type)
Add a type to the results of the query.
Definition: core/query.cpp:73
bool isEmpty() const const
QByteArray toUtf8() const const
static List searchStores()
Gives a list of available search stores.
Definition: searchstore.cpp:65
void setDateFilter(int year, int month=-1, int day=-1)
Filter the results in the specified date range.
Definition: core/query.cpp:131
void insert(int i, const T &value)
void setQuery(const QString &query, QUrl::ParsingMode mode)
Search term.
Definition: term.h:26
QByteArray toJson() const const
void setObject(const QJsonObject &object)
const QVariantMap toMap(const MODEL &model)
Search query.
Definition: core/query.h:26
void setSearchString(const QString &str)
Set some text which should be used to search for Items.
Definition: core/query.cpp:106
QString queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding) const const
void setLimit(uint limit)
Only a maximum of limit results will be returned.
Definition: core/query.cpp:116
QFuture< void > map(Sequence &sequence, MapFunctor function)
QHash< QString, QVariant > toHash() const const
const QList< QKeySequence > & end()
char * toString(const EngineQuery &query)
void addCustomOption(const QString &option, const QVariant &value)
Adds a custom option which any search backend could use to configure the query result.
Definition: core/query.cpp:174
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Dec 7 2023 04:11:59 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.