Baloo

query.cpp
1 /*
2  This file is part of the KDE Baloo 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 #include "query.h"
9 #include "term.h"
10 #include "advancedqueryparser.h"
11 #include "searchstore.h"
12 #include "baloodebug.h"
13 
14 #include <QString>
15 #include <QStringList>
16 #include <QUrlQuery>
17 
18 #include <QJsonDocument>
19 #include <QJsonObject>
20 
21 using namespace Baloo;
22 
23 const int defaultLimit = -1;
24 
25 class BALOO_CORE_NO_EXPORT Baloo::Query::Private {
26 public:
27  Term m_term;
28 
29  QStringList m_types;
30  QString m_searchString;
31  int m_limit = defaultLimit;
32  uint m_offset = 0;
33 
34  int m_yearFilter = 0;
35  int m_monthFilter = 0;
36  int m_dayFilter = 0;
37 
38  SortingOption m_sortingOption = SortAuto;
39  QString m_includeFolder;
40 };
41 
42 Query::Query()
43  : d(new Private)
44 {
45 }
46 
47 Query::Query(const Query& rhs)
48  : d(new Private(*rhs.d))
49 {
50 }
51 
52 Query::~Query()
53 {
54  delete d;
55 }
56 
57 void Query::addType(const QString& type)
58 {
59  d->m_types << type.split(QLatin1Char('/'), Qt::SkipEmptyParts);
60 }
61 
62 void Query::addTypes(const QStringList& typeList)
63 {
64  for (const QString& type : typeList) {
65  addType(type);
66  }
67 }
68 
69 void Query::setType(const QString& type)
70 {
71  d->m_types.clear();
72  addType(type);
73 }
74 
75 void Query::setTypes(const QStringList& types)
76 {
77  d->m_types = types;
78 }
79 
80 QStringList Query::types() const
81 {
82  return d->m_types;
83 }
84 
85 QString Query::searchString() const
86 {
87  return d->m_searchString;
88 }
89 
90 void Query::setSearchString(const QString& str)
91 {
92  d->m_searchString = str;
93 
94  d->m_term = Term();
95 }
96 
97 uint Query::limit() const
98 {
99  return d->m_limit;
100 }
101 
102 void Query::setLimit(uint limit)
103 {
104  d->m_limit = limit;
105 }
106 
107 uint Query::offset() const
108 {
109  return d->m_offset;
110 }
111 
112 void Query::setOffset(uint offset)
113 {
114  d->m_offset = offset;
115 }
116 
117 void Query::setDateFilter(int year, int month, int day)
118 {
119  d->m_yearFilter = year;
120  d->m_monthFilter = month;
121  d->m_dayFilter = day;
122 }
123 
124 int Query::yearFilter() const
125 {
126  return d->m_yearFilter;
127 }
128 
129 int Query::monthFilter() const
130 {
131  return d->m_monthFilter;
132 }
133 
134 int Query::dayFilter() const
135 {
136  return d->m_dayFilter;
137 }
138 
139 void Query::setSortingOption(Query::SortingOption option)
140 {
141  d->m_sortingOption = option;
142 }
143 
144 Query::SortingOption Query::sortingOption() const
145 {
146  return d->m_sortingOption;
147 }
148 
149 QString Query::includeFolder() const
150 {
151  return d->m_includeFolder;
152 }
153 
154 void Query::setIncludeFolder(const QString& folder)
155 {
156  d->m_includeFolder = folder;
157 }
158 
159 ResultIterator Query::exec()
160 {
161  if (!d->m_searchString.isEmpty()) {
162  if (d->m_term.isValid()) {
163  qCDebug(BALOO) << "Term already set";
164  }
165  AdvancedQueryParser parser;
166  d->m_term = parser.parse(d->m_searchString);
167  }
168 
169  Term term(d->m_term);
170  if (!d->m_types.isEmpty()) {
171  for (const QString& type : std::as_const(d->m_types)) {
172  term = term && Term(QStringLiteral("type"), type);
173  }
174  }
175 
176  if (!d->m_includeFolder.isEmpty()) {
177  term = term && Term(QStringLiteral("includefolder"), d->m_includeFolder);
178  }
179 
180  if (d->m_yearFilter || d->m_monthFilter || d->m_dayFilter) {
181  QByteArray ba = QByteArray::number(d->m_yearFilter);
182  if (d->m_monthFilter < 10) {
183  ba += '0';
184  }
185  ba += QByteArray::number(d->m_monthFilter);
186  if (d->m_dayFilter < 10) {
187  ba += '0';
188  }
189  ba += QByteArray::number(d->m_dayFilter);
190 
191  term = term && Term(QStringLiteral("modified"), ba, Term::Equal);
192  }
193 
194  SearchStore searchStore;
195  auto results = searchStore.exec(term, d->m_offset, d->m_limit, d->m_sortingOption == SortAuto);
196  return ResultIterator(std::move(results));
197 }
198 
199 QByteArray Query::toJSON()
200 {
201  QVariantMap map;
202 
203  if (!d->m_types.isEmpty()) {
204  map[QStringLiteral("type")] = d->m_types;
205  }
206 
207  if (d->m_limit != defaultLimit) {
208  map[QStringLiteral("limit")] = d->m_limit;
209  }
210 
211  if (d->m_offset) {
212  map[QStringLiteral("offset")] = d->m_offset;
213  }
214 
215  if (!d->m_searchString.isEmpty()) {
216  map[QStringLiteral("searchString")] = d->m_searchString;
217  }
218 
219  if (d->m_term.isValid()) {
220  map[QStringLiteral("term")] = QVariant(d->m_term.toVariantMap());
221  }
222 
223  if (d->m_yearFilter > 0) {
224  map[QStringLiteral("yearFilter")] = d->m_yearFilter;
225  }
226  if (d->m_monthFilter > 0) {
227  map[QStringLiteral("monthFilter")] = d->m_monthFilter;
228  }
229  if (d->m_dayFilter > 0) {
230  map[QStringLiteral("dayFilter")] = d->m_dayFilter;
231  }
232 
233  if (d->m_sortingOption != SortAuto) {
234  map[QStringLiteral("sortingOption")] = static_cast<int>(d->m_sortingOption);
235  }
236 
237  if (!d->m_includeFolder.isEmpty()) {
238  map[QStringLiteral("includeFolder")] = d->m_includeFolder;
239  }
240 
242  QJsonDocument jdoc;
243  jdoc.setObject(jo);
244  return jdoc.toJson(QJsonDocument::JsonFormat::Compact);
245 }
246 
247 // static
248 Query Query::fromJSON(const QByteArray& arr)
249 {
251  const QVariantMap map = jdoc.object().toVariantMap();
252 
253  Query query;
254  query.d->m_types = map[QStringLiteral("type")].toStringList();
255 
256  if (map.contains(QStringLiteral("limit"))) {
257  query.d->m_limit = map[QStringLiteral("limit")].toUInt();
258  } else {
259  query.d->m_limit = defaultLimit;
260  }
261 
262  query.d->m_offset = map[QStringLiteral("offset")].toUInt();
263  query.d->m_searchString = map[QStringLiteral("searchString")].toString();
264  query.d->m_term = Term::fromVariantMap(map[QStringLiteral("term")].toMap());
265 
266  if (map.contains(QStringLiteral("yearFilter"))) {
267  query.d->m_yearFilter = map[QStringLiteral("yearFilter")].toInt();
268  }
269  if (map.contains(QStringLiteral("monthFilter"))) {
270  query.d->m_monthFilter = map[QStringLiteral("monthFilter")].toInt();
271  }
272  if (map.contains(QStringLiteral("dayFilter"))) {
273  query.d->m_dayFilter = map[QStringLiteral("dayFilter")].toInt();
274  }
275 
276  if (map.contains(QStringLiteral("sortingOption"))) {
277  int option = map.value(QStringLiteral("sortingOption")).toInt();
278  query.d->m_sortingOption = static_cast<SortingOption>(option);
279  }
280 
281  if (map.contains(QStringLiteral("includeFolder"))) {
282  query.d->m_includeFolder = map.value(QStringLiteral("includeFolder")).toString();
283  }
284 
285  if (!query.d->m_searchString.isEmpty() && query.d->m_term.isValid()) {
286  qCWarning(BALOO) << "Only one of 'searchString' and 'term' should be set:" << arr;
287  }
288  return query;
289 }
290 
291 QUrl Query::toSearchUrl(const QString& title)
292 {
293  QUrl url;
294  url.setScheme(QStringLiteral("baloosearch"));
295 
296  QUrlQuery urlQuery;
297  urlQuery.addQueryItem(QStringLiteral("json"), QString::fromUtf8(toJSON()));
298 
299  if (!title.isEmpty()) {
300  urlQuery.addQueryItem(QStringLiteral("title"), title);
301  }
302 
303  url.setQuery(urlQuery);
304  return url;
305 }
306 
307 static QString jsonQueryFromUrl(const QUrl &url)
308 {
309  const QString path = url.path();
310 
311  if (path == QLatin1String("/documents")) {
312  return QStringLiteral("{\"type\":[\"Document\"]}");
313  } else if (path.endsWith(QLatin1String("/images"))) {
314  return QStringLiteral("{\"type\":[\"Image\"]}");
315  } else if (path.endsWith(QLatin1String("/audio"))) {
316  return QStringLiteral("{\"type\":[\"Audio\"]}");
317  } else if (path.endsWith(QLatin1String("/videos"))) {
318  return QStringLiteral("{\"type\":[\"Video\"]}");
319  }
320 
321  return QString();
322 }
323 
324 Query Query::fromSearchUrl(const QUrl& url)
325 {
326  if (url.scheme() != QLatin1String("baloosearch")) {
327  return Query();
328  }
329 
330  QUrlQuery urlQuery(url);
331 
332  if (urlQuery.hasQueryItem(QStringLiteral("json"))) {
333  QString jsonString = urlQuery.queryItemValue(QStringLiteral("json"), QUrl::FullyDecoded);
334  return Query::fromJSON(jsonString.toUtf8());
335  }
336 
337  if (urlQuery.hasQueryItem(QStringLiteral("query"))) {
338  QString queryString = urlQuery.queryItemValue(QStringLiteral("query"), QUrl::FullyDecoded);
339  Query q;
340  q.setSearchString(queryString);
341  return q;
342  }
343 
344  const QString jsonString = jsonQueryFromUrl(url);
345  if (!jsonString.isEmpty()) {
346  return Query::fromJSON(jsonString.toUtf8());
347  }
348 
349  return Query();
350 }
351 
352 QString Query::titleFromQueryUrl(const QUrl& url)
353 {
354  QUrlQuery urlQuery(url);
355  return urlQuery.queryItemValue(QStringLiteral("title"), QUrl::FullyDecoded);
356 }
357 
358 bool Query::operator==(const Query& rhs) const
359 {
360  if (rhs.d->m_limit != d->m_limit || rhs.d->m_offset != d->m_offset ||
361  rhs.d->m_dayFilter != d->m_dayFilter || rhs.d->m_monthFilter != d->m_monthFilter ||
362  rhs.d->m_yearFilter != d->m_yearFilter || rhs.d->m_includeFolder != d->m_includeFolder ||
363  rhs.d->m_searchString != d->m_searchString ||
364  rhs.d->m_sortingOption != d->m_sortingOption)
365  {
366  return false;
367  }
368 
369  if (rhs.d->m_types.size() != d->m_types.size()) {
370  return false;
371  }
372 
373  for (const QString& type : std::as_const(rhs.d->m_types)) {
374  if (!d->m_types.contains(type)) {
375  return false;
376  }
377  }
378 
379  return d->m_term == rhs.d->m_term;
380 }
381 
382 bool Query::operator!=(const Query& rhs) const
383 {
384  return !(*this == rhs);
385 }
386 
387 Query& Query::operator=(const Query& rhs)
388 {
389  *d = *rhs.d;
390  return *this;
391 }
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QJsonObject object() const const
std::optional< QSqlQuery > query(const QString &queryStatement)
@ SortAuto
The results are returned in the order Baloo decides should be ideal.
Definition: query.h:116
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QString fromUtf8(const char *str, int size)
QString scheme() const const
void setSearchString(const QString &str)
Set some text which should be used to search for Items.
void addQueryItem(const QString &key, const QString &value)
bool hasQueryItem(const QString &key) const const
QVariantMap toVariantMap() const const
QJsonObject fromVariantMap(const QVariantMap &map)
QByteArray number(int n, int base)
void setScheme(const QString &scheme)
QStringList types(Mode mode=Writing)
void setIncludeFolder(const QString &folder)
Only files in this folder will be returned.
Definition: query.cpp:154
FullyDecoded
SkipEmptyParts
Implements storage for docIds without any associated data Instantiated for:
Definition: coding.cpp:11
bool isEmpty() const const
QByteArray toUtf8() const const
void setQuery(const QString &query, QUrl::ParsingMode mode)
QString path(QUrl::ComponentFormattingOptions options) const const
QString path(const QString &relativePath)
QByteArray toJson() const const
void setObject(const QJsonObject &object)
const QVariantMap toMap(const MODEL &model)
QString queryItemValue(const QString &key, QUrl::ComponentFormattingOptions encoding) const const
void setLimit(uint limit)
Only a maximum of limit results will be returned.
Definition: query.cpp:102
void addType(const QString &type)
Add a type to the results of the query.
Definition: query.cpp:57
QFuture< void > map(Sequence &sequence, MapFunctor function)
void setDateFilter(int year, int month=0, int day=0)
Filter the results in the specified date range.
char * toString(const EngineQuery &query)
Helper for QTest.
Definition: enginequery.h:91
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Nov 29 2023 03:56:26 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.