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