File indexing completed on 2024-04-28 15:17:40
0001 /* 0002 This file is part of the KDE Baloo Project 0003 SPDX-FileCopyrightText: 2013 Vishesh Handa <me@vhanda.in> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "query.h" 0009 #include "term.h" 0010 #include "advancedqueryparser.h" 0011 #include "searchstore.h" 0012 #include "baloodebug.h" 0013 0014 #include <QString> 0015 #include <QStringList> 0016 #include <QUrlQuery> 0017 0018 #include <QJsonDocument> 0019 #include <QJsonObject> 0020 0021 using namespace Baloo; 0022 0023 const int defaultLimit = -1; 0024 0025 class BALOO_CORE_NO_EXPORT Baloo::Query::Private { 0026 public: 0027 Term m_term; 0028 0029 QStringList m_types; 0030 QString m_searchString; 0031 int m_limit = defaultLimit; 0032 uint m_offset = 0; 0033 0034 int m_yearFilter = 0; 0035 int m_monthFilter = 0; 0036 int m_dayFilter = 0; 0037 0038 SortingOption m_sortingOption = SortAuto; 0039 QString m_includeFolder; 0040 }; 0041 0042 Query::Query() 0043 : d(new Private) 0044 { 0045 } 0046 0047 Query::Query(const Query& rhs) 0048 : d(new Private(*rhs.d)) 0049 { 0050 } 0051 0052 Query::~Query() 0053 { 0054 delete d; 0055 } 0056 0057 void Query::addType(const QString& type) 0058 { 0059 d->m_types << type.split(QLatin1Char('/'), Qt::SkipEmptyParts); 0060 } 0061 0062 void Query::addTypes(const QStringList& typeList) 0063 { 0064 for (const QString& type : typeList) { 0065 addType(type); 0066 } 0067 } 0068 0069 void Query::setType(const QString& type) 0070 { 0071 d->m_types.clear(); 0072 addType(type); 0073 } 0074 0075 void Query::setTypes(const QStringList& types) 0076 { 0077 d->m_types = types; 0078 } 0079 0080 QStringList Query::types() const 0081 { 0082 return d->m_types; 0083 } 0084 0085 QString Query::searchString() const 0086 { 0087 return d->m_searchString; 0088 } 0089 0090 void Query::setSearchString(const QString& str) 0091 { 0092 d->m_searchString = str; 0093 0094 d->m_term = Term(); 0095 } 0096 0097 uint Query::limit() const 0098 { 0099 return d->m_limit; 0100 } 0101 0102 void Query::setLimit(uint limit) 0103 { 0104 d->m_limit = limit; 0105 } 0106 0107 uint Query::offset() const 0108 { 0109 return d->m_offset; 0110 } 0111 0112 void Query::setOffset(uint offset) 0113 { 0114 d->m_offset = offset; 0115 } 0116 0117 void Query::setDateFilter(int year, int month, int day) 0118 { 0119 d->m_yearFilter = year; 0120 d->m_monthFilter = month; 0121 d->m_dayFilter = day; 0122 } 0123 0124 int Query::yearFilter() const 0125 { 0126 return d->m_yearFilter; 0127 } 0128 0129 int Query::monthFilter() const 0130 { 0131 return d->m_monthFilter; 0132 } 0133 0134 int Query::dayFilter() const 0135 { 0136 return d->m_dayFilter; 0137 } 0138 0139 void Query::setSortingOption(Query::SortingOption option) 0140 { 0141 d->m_sortingOption = option; 0142 } 0143 0144 Query::SortingOption Query::sortingOption() const 0145 { 0146 return d->m_sortingOption; 0147 } 0148 0149 QString Query::includeFolder() const 0150 { 0151 return d->m_includeFolder; 0152 } 0153 0154 void Query::setIncludeFolder(const QString& folder) 0155 { 0156 d->m_includeFolder = folder; 0157 } 0158 0159 ResultIterator Query::exec() 0160 { 0161 if (!d->m_searchString.isEmpty()) { 0162 if (d->m_term.isValid()) { 0163 qCDebug(BALOO) << "Term already set"; 0164 } 0165 AdvancedQueryParser parser; 0166 d->m_term = parser.parse(d->m_searchString); 0167 } 0168 0169 Term term(d->m_term); 0170 if (!d->m_types.isEmpty()) { 0171 for (const QString& type : std::as_const(d->m_types)) { 0172 term = term && Term(QStringLiteral("type"), type); 0173 } 0174 } 0175 0176 if (!d->m_includeFolder.isEmpty()) { 0177 term = term && Term(QStringLiteral("includefolder"), d->m_includeFolder); 0178 } 0179 0180 if (d->m_yearFilter || d->m_monthFilter || d->m_dayFilter) { 0181 QByteArray ba = QByteArray::number(d->m_yearFilter); 0182 if (d->m_monthFilter < 10) { 0183 ba += '0'; 0184 } 0185 ba += QByteArray::number(d->m_monthFilter); 0186 if (d->m_dayFilter < 10) { 0187 ba += '0'; 0188 } 0189 ba += QByteArray::number(d->m_dayFilter); 0190 0191 term = term && Term(QStringLiteral("modified"), ba, Term::Equal); 0192 } 0193 0194 SearchStore searchStore; 0195 auto results = searchStore.exec(term, d->m_offset, d->m_limit, d->m_sortingOption == SortAuto); 0196 return ResultIterator(std::move(results)); 0197 } 0198 0199 QByteArray Query::toJSON() 0200 { 0201 QVariantMap map; 0202 0203 if (!d->m_types.isEmpty()) { 0204 map[QStringLiteral("type")] = d->m_types; 0205 } 0206 0207 if (d->m_limit != defaultLimit) { 0208 map[QStringLiteral("limit")] = d->m_limit; 0209 } 0210 0211 if (d->m_offset) { 0212 map[QStringLiteral("offset")] = d->m_offset; 0213 } 0214 0215 if (!d->m_searchString.isEmpty()) { 0216 map[QStringLiteral("searchString")] = d->m_searchString; 0217 } 0218 0219 if (d->m_term.isValid()) { 0220 map[QStringLiteral("term")] = QVariant(d->m_term.toVariantMap()); 0221 } 0222 0223 if (d->m_yearFilter > 0) { 0224 map[QStringLiteral("yearFilter")] = d->m_yearFilter; 0225 } 0226 if (d->m_monthFilter > 0) { 0227 map[QStringLiteral("monthFilter")] = d->m_monthFilter; 0228 } 0229 if (d->m_dayFilter > 0) { 0230 map[QStringLiteral("dayFilter")] = d->m_dayFilter; 0231 } 0232 0233 if (d->m_sortingOption != SortAuto) { 0234 map[QStringLiteral("sortingOption")] = static_cast<int>(d->m_sortingOption); 0235 } 0236 0237 if (!d->m_includeFolder.isEmpty()) { 0238 map[QStringLiteral("includeFolder")] = d->m_includeFolder; 0239 } 0240 0241 QJsonObject jo = QJsonObject::fromVariantMap(map); 0242 QJsonDocument jdoc; 0243 jdoc.setObject(jo); 0244 return jdoc.toJson(QJsonDocument::JsonFormat::Compact); 0245 } 0246 0247 // static 0248 Query Query::fromJSON(const QByteArray& arr) 0249 { 0250 QJsonDocument jdoc = QJsonDocument::fromJson(arr); 0251 const QVariantMap map = jdoc.object().toVariantMap(); 0252 0253 Query query; 0254 query.d->m_types = map[QStringLiteral("type")].toStringList(); 0255 0256 if (map.contains(QStringLiteral("limit"))) { 0257 query.d->m_limit = map[QStringLiteral("limit")].toUInt(); 0258 } else { 0259 query.d->m_limit = defaultLimit; 0260 } 0261 0262 query.d->m_offset = map[QStringLiteral("offset")].toUInt(); 0263 query.d->m_searchString = map[QStringLiteral("searchString")].toString(); 0264 query.d->m_term = Term::fromVariantMap(map[QStringLiteral("term")].toMap()); 0265 0266 if (map.contains(QStringLiteral("yearFilter"))) { 0267 query.d->m_yearFilter = map[QStringLiteral("yearFilter")].toInt(); 0268 } 0269 if (map.contains(QStringLiteral("monthFilter"))) { 0270 query.d->m_monthFilter = map[QStringLiteral("monthFilter")].toInt(); 0271 } 0272 if (map.contains(QStringLiteral("dayFilter"))) { 0273 query.d->m_dayFilter = map[QStringLiteral("dayFilter")].toInt(); 0274 } 0275 0276 if (map.contains(QStringLiteral("sortingOption"))) { 0277 int option = map.value(QStringLiteral("sortingOption")).toInt(); 0278 query.d->m_sortingOption = static_cast<SortingOption>(option); 0279 } 0280 0281 if (map.contains(QStringLiteral("includeFolder"))) { 0282 query.d->m_includeFolder = map.value(QStringLiteral("includeFolder")).toString(); 0283 } 0284 0285 if (!query.d->m_searchString.isEmpty() && query.d->m_term.isValid()) { 0286 qCWarning(BALOO) << "Only one of 'searchString' and 'term' should be set:" << arr; 0287 } 0288 return query; 0289 } 0290 0291 QUrl Query::toSearchUrl(const QString& title) 0292 { 0293 QUrl url; 0294 url.setScheme(QStringLiteral("baloosearch")); 0295 0296 QUrlQuery urlQuery; 0297 urlQuery.addQueryItem(QStringLiteral("json"), QString::fromUtf8(toJSON())); 0298 0299 if (!title.isEmpty()) { 0300 urlQuery.addQueryItem(QStringLiteral("title"), title); 0301 } 0302 0303 url.setQuery(urlQuery); 0304 return url; 0305 } 0306 0307 static QString jsonQueryFromUrl(const QUrl &url) 0308 { 0309 const QString path = url.path(); 0310 0311 if (path == QLatin1String("/documents")) { 0312 return QStringLiteral("{\"type\":[\"Document\"]}"); 0313 } else if (path.endsWith(QLatin1String("/images"))) { 0314 return QStringLiteral("{\"type\":[\"Image\"]}"); 0315 } else if (path.endsWith(QLatin1String("/audio"))) { 0316 return QStringLiteral("{\"type\":[\"Audio\"]}"); 0317 } else if (path.endsWith(QLatin1String("/videos"))) { 0318 return QStringLiteral("{\"type\":[\"Video\"]}"); 0319 } 0320 0321 return QString(); 0322 } 0323 0324 Query Query::fromSearchUrl(const QUrl& url) 0325 { 0326 if (url.scheme() != QLatin1String("baloosearch")) { 0327 return Query(); 0328 } 0329 0330 QUrlQuery urlQuery(url); 0331 0332 if (urlQuery.hasQueryItem(QStringLiteral("json"))) { 0333 QString jsonString = urlQuery.queryItemValue(QStringLiteral("json"), QUrl::FullyDecoded); 0334 return Query::fromJSON(jsonString.toUtf8()); 0335 } 0336 0337 if (urlQuery.hasQueryItem(QStringLiteral("query"))) { 0338 QString queryString = urlQuery.queryItemValue(QStringLiteral("query"), QUrl::FullyDecoded); 0339 Query q; 0340 q.setSearchString(queryString); 0341 return q; 0342 } 0343 0344 const QString jsonString = jsonQueryFromUrl(url); 0345 if (!jsonString.isEmpty()) { 0346 return Query::fromJSON(jsonString.toUtf8()); 0347 } 0348 0349 return Query(); 0350 } 0351 0352 QString Query::titleFromQueryUrl(const QUrl& url) 0353 { 0354 QUrlQuery urlQuery(url); 0355 return urlQuery.queryItemValue(QStringLiteral("title"), QUrl::FullyDecoded); 0356 } 0357 0358 bool Query::operator==(const Query& rhs) const 0359 { 0360 if (rhs.d->m_limit != d->m_limit || rhs.d->m_offset != d->m_offset || 0361 rhs.d->m_dayFilter != d->m_dayFilter || rhs.d->m_monthFilter != d->m_monthFilter || 0362 rhs.d->m_yearFilter != d->m_yearFilter || rhs.d->m_includeFolder != d->m_includeFolder || 0363 rhs.d->m_searchString != d->m_searchString || 0364 rhs.d->m_sortingOption != d->m_sortingOption) 0365 { 0366 return false; 0367 } 0368 0369 if (rhs.d->m_types.size() != d->m_types.size()) { 0370 return false; 0371 } 0372 0373 for (const QString& type : std::as_const(rhs.d->m_types)) { 0374 if (!d->m_types.contains(type)) { 0375 return false; 0376 } 0377 } 0378 0379 return d->m_term == rhs.d->m_term; 0380 } 0381 0382 bool Query::operator!=(const Query& rhs) const 0383 { 0384 return !(*this == rhs); 0385 } 0386 0387 Query& Query::operator=(const Query& rhs) 0388 { 0389 *d = *rhs.d; 0390 return *this; 0391 }