10#include <QCoreApplication>
17#include "plasma-activities-stats-logsettings.h"
18#include <common/database/Database.h>
19#include <common/specialvalues.h>
20#include <utils/debug_and_return.h>
21#include <utils/qsqlquery_iterator.h>
29#include "activitiessync_p.h"
31#define DEBUG_QUERIES 0
39class ResultSet_ResultPrivate
48 ResultSet::Result::LinkStatus linkStatus;
49 QStringList linkedActivities;
53ResultSet::Result::Result()
54 : d(new ResultSet_ResultPrivate())
58ResultSet::Result::Result(
Result &&result)
64ResultSet::Result::Result(
const Result &result)
65 : d(new ResultSet_ResultPrivate(*result.d))
69ResultSet::Result &ResultSet::Result::operator=(
Result result)
71 std::swap(d, result.d);
76ResultSet::Result::~Result()
81#define CREATE_GETTER_AND_SETTER(Type, Name, Set) \
82 Type ResultSet::Result::Name() const \
87 void ResultSet::Result::Set(Type Name) \
92CREATE_GETTER_AND_SETTER(
QString, resource, setResource)
93CREATE_GETTER_AND_SETTER(
QString, title, setTitle)
94CREATE_GETTER_AND_SETTER(
QString, mimetype, setMimetype)
95CREATE_GETTER_AND_SETTER(
double, score, setScore)
96CREATE_GETTER_AND_SETTER(uint, lastUpdate, setLastUpdate)
97CREATE_GETTER_AND_SETTER(uint, firstUpdate, setFirstUpdate)
98CREATE_GETTER_AND_SETTER(ResultSet::Result::LinkStatus, linkStatus, setLinkStatus)
99CREATE_GETTER_AND_SETTER(
QStringList, linkedActivities, setLinkedActivities)
100CREATE_GETTER_AND_SETTER(
QString, agent, setAgent)
102#undef CREATE_GETTER_AND_SETTER
109 return QUrl(d->resource);
113class ResultSetPrivate
116 Common::Database::Ptr database;
118 Query queryDefinition;
120 mutable ActivitiesSync::ConsumerPtr activities;
124 if (!database || query.isActive()) {
128 auto selection = queryDefinition.selection();
130 query = database->execQuery(replaceQueryParameters(
131 selection == LinkedResources ? linkedResourcesQuery()
132 : selection == UsedResources ? usedResourcesQuery()
133 : selection == AllResources ? allResourcesQuery()
136 if (query.lastError().isValid()) {
137 qCWarning(PLASMA_ACTIVITIES_STATS_LOG) <<
"[Error at ResultSetPrivate::initQuery]: " << query.lastError();
144 return QStringLiteral(
"1");
147 return QLatin1String(
"agent = '")
148 + Common::escapeSqliteLikePattern(agent == QLatin1String(
":current") ?
QCoreApplication::instance()->applicationName() : agent)
149 + QLatin1String(
"'");
152 QString activityClause(
const QString &activity)
const
154 if (activity == QLatin1String(
":any")) {
155 return QStringLiteral(
"1");
158 return QLatin1String(
"activity = '") +
159 Common::escapeSqliteLikePattern(activity == QLatin1String(
":current") ? ActivitiesSync::currentActivity(activities) : activity)
160 + QLatin1String(
"'");
163 inline QString starPattern(
const QString &pattern)
const
165 return Common::parseStarPattern(pattern, QStringLiteral(
"%"), [](QString str) {
166 return str.
replace(QLatin1String(
"%"), QLatin1String(
"\\%")).
replace(QLatin1String(
"_"), QLatin1String(
"\\_"));
170 QString urlFilterClause(
const QString &urlFilter)
const
172 if (urlFilter == QLatin1String(
"*")) {
173 return QStringLiteral(
"1");
176 return QLatin1String(
"resource LIKE '") + Common::starPatternToLike(urlFilter) + QLatin1String(
"' ESCAPE '\\'");
179 QString mimetypeClause(
const QString &mimetype)
const
181 if (mimetype == ANY_TYPE_TAG || mimetype == QLatin1String(
"*")) {
182 return QStringLiteral(
"1");
184 }
else if (mimetype == FILES_TYPE_TAG) {
185 return QStringLiteral(
"mimetype != 'inode/directory' AND mimetype != ''");
186 }
else if (mimetype == DIRECTORIES_TYPE_TAG) {
187 return QStringLiteral(
"mimetype = 'inode/directory'");
190 return QLatin1String(
"mimetype LIKE '") + Common::starPatternToLike(mimetype) + QLatin1String(
"' ESCAPE '\\'");
193 QString dateClause(QDate
start, QDate end)
const
197 return QLatin1String(
"DATE(re.start, 'unixepoch') = '") +
start.toString(
Qt::ISODate) + QLatin1String(
"' ");
200 return QLatin1String(
"DATE(re.start, 'unixepoch') >= '") +
start.toString(
Qt::ISODate) + QLatin1String(
"' AND DATE(re.start, 'unixepoch') <= '")
204 QString titleClause(
const QString titleFilter)
const
206 if (titleFilter == QLatin1String(
"*")) {
207 return QStringLiteral(
"1");
210 return QLatin1String(
"title LIKE '") + Common::starPatternToLike(titleFilter) + QLatin1String(
"' ESCAPE '\\'");
213 QString resourceEventJoinClause()
const
215 return QStringLiteral(R
"(
218 ON from_table.targettedResource = re.targettedResource
219 AND from_table.usedActivity = re.usedActivity
220 AND from_table.initiatingAgent = re.initiatingAgent
229 inline QStringList transformedList(
const QStringList &input, F f)
const
231 using namespace std::placeholders;
234 std::transform(input.
cbegin(), input.
cend(), std::back_inserter(result), std::bind(f,
this, _1));
239 QString limitOffsetSuffix()
const
243 const int limit = queryDefinition.limit();
247 const int offset = queryDefinition.offset();
256 inline QString replaceQueryParameters(
const QString &_query)
const
259 auto ordering = queryDefinition.ordering();
260 QString orderingColumn = QLatin1String(
"linkStatus DESC, ")
261 + (ordering == HighScoredFirst ? QLatin1String(
"score DESC,")
262 : ordering == RecentlyCreatedFirst ? QLatin1String(
"firstUpdate DESC,")
263 : ordering == RecentlyUsedFirst ? QLatin1String(
"lastUpdate DESC,")
264 : ordering == OrderByTitle ? QLatin1String(
"title ASC,")
268 QStringList agentsFilter = transformedList(queryDefinition.agents(), &ResultSetPrivate::agentClause);
271 QStringList activitiesFilter = transformedList(queryDefinition.activities(), &ResultSetPrivate::activityClause);
274 QStringList urlFilter = transformedList(queryDefinition.urlFilters(), &ResultSetPrivate::urlFilterClause);
277 QStringList mimetypeFilter = transformedList(queryDefinition.types(), &ResultSetPrivate::mimetypeClause);
278 QStringList titleFilter = transformedList(queryDefinition.titleFilters(), &ResultSetPrivate::titleClause);
280 QString dateColumn = QStringLiteral(
"1");
281 QString resourceEventJoin;
283 if (!queryDefinition.dateStart().
isNull()) {
284 dateColumn = dateClause(queryDefinition.dateStart(), queryDefinition.dateEnd());
286 resourceEventJoin = resourceEventJoinClause();
289 auto queryString = _query;
291 queryString.
replace(QLatin1String(
"ORDER_BY_CLAUSE"), QLatin1String(
"ORDER BY $orderingColumn resource ASC"))
292 .
replace(QLatin1String(
"LIMIT_CLAUSE"), limitOffsetSuffix());
294 const QString replacedQuery =
295 queryString.
replace(QLatin1String(
"$orderingColumn"), orderingColumn)
296 .
replace(QLatin1String(
"$agentsFilter"), agentsFilter.
join(QStringLiteral(
" OR ")))
297 .
replace(QLatin1String(
"$activitiesFilter"), activitiesFilter.
join(QStringLiteral(
" OR ")))
298 .
replace(QLatin1String(
"$urlFilter"), urlFilter.
join(QStringLiteral(
" OR ")))
299 .
replace(QLatin1String(
"$mimetypeFilter"), mimetypeFilter.
join(QStringLiteral(
" OR ")))
300 .
replace(QLatin1String(
"$resourceEventJoin"), resourceEventJoin)
301 .
replace(QLatin1String(
"$dateFilter"), dateColumn)
302 .
replace(QLatin1String(
"$titleFilter"), titleFilter.
isEmpty() ? QStringLiteral(
"1") : titleFilter.
join(QStringLiteral(
" OR ")));
303 return kamd::utils::debug_and_return(DEBUG_QUERIES,
"Query: ", replacedQuery);
306 static const QString &linkedResourcesQuery()
311 static const QString queryString = QStringLiteral(R
"(
313 from_table.targettedResource as resource
314 , SUM(rsc.cachedScore) as score
315 , MIN(rsc.firstUpdate) as firstUpdate
316 , MAX(rsc.lastUpdate) as lastUpdate
317 , from_table.usedActivity as activity
318 , from_table.initiatingAgent as agent
319 , COALESCE(ri.title, from_table.targettedResource) as title
320 , ri.mimetype as mimetype
324 ResourceLink from_table
326 ResourceScoreCache rsc
327 ON from_table.targettedResource = rsc.targettedResource
328 AND from_table.usedActivity = rsc.usedActivity
329 AND from_table.initiatingAgent = rsc.initiatingAgent
332 ON from_table.targettedResource = ri.targettedResource
338 AND ($activitiesFilter)
340 AND ($mimetypeFilter)
344 GROUP BY resource, title
353 static const QString &usedResourcesQuery()
357 static const QString queryString = QStringLiteral(R
"(
359 from_table.targettedResource as resource
360 , SUM(from_table.cachedScore) as score
361 , MIN(from_table.firstUpdate) as firstUpdate
362 , MAX(from_table.lastUpdate) as lastUpdate
363 , from_table.usedActivity as activity
364 , from_table.initiatingAgent as agent
365 , COALESCE(ri.title, from_table.targettedResource) as title
366 , ri.mimetype as mimetype
370 ResourceScoreCache from_table
373 ON from_table.targettedResource = ri.targettedResource
379 AND ($activitiesFilter)
381 AND ($mimetypeFilter)
385 GROUP BY resource, title
394 static const QString &allResourcesQuery()
399 static const QString queryString = QStringLiteral(R
"(
401 LinkedResourcesResults AS (
402 SELECT from_table.targettedResource as resource
403 , rsc.cachedScore as score
404 , rsc.firstUpdate as firstUpdate
405 , rsc.lastUpdate as lastUpdate
406 , from_table.usedActivity as activity
407 , from_table.initiatingAgent as agent
411 ResourceLink from_table
414 ResourceScoreCache rsc
415 ON from_table.targettedResource = rsc.targettedResource
416 AND from_table.usedActivity = rsc.usedActivity
417 AND from_table.initiatingAgent = rsc.initiatingAgent
423 AND ($activitiesFilter)
425 AND ($mimetypeFilter)
430 UsedResourcesResults AS (
431 SELECT from_table.targettedResource as resource
432 , from_table.cachedScore as score
433 , from_table.firstUpdate as firstUpdate
434 , from_table.lastUpdate as lastUpdate
435 , from_table.usedActivity as activity
436 , from_table.initiatingAgent as agent
440 ResourceScoreCache from_table
446 AND ($activitiesFilter)
448 AND ($mimetypeFilter)
453 CollectedResults AS (
455 FROM LinkedResourcesResults
460 FROM UsedResourcesResults
461 WHERE resource NOT IN (SELECT resource FROM LinkedResourcesResults)
466 , SUM(score) as score
467 , MIN(firstUpdate) as firstUpdate
468 , MAX(lastUpdate) as lastUpdate
471 , COALESCE(ri.title, resource) as title
472 , ri.mimetype as mimetype
475 FROM CollectedResults cr
479 ON cr.resource = ri.targettedResource
481 GROUP BY resource, title
490 ResultSet::Result currentResult()
const
492 ResultSet::Result result;
494 if (!database || !
query.isActive()) {
501 result.setScore(
query.
value(QStringLiteral(
"score")).toDouble());
502 result.setLastUpdate(
query.
value(QStringLiteral(
"lastUpdate")).toUInt());
503 result.setFirstUpdate(
query.
value(QStringLiteral(
"firstUpdate")).toUInt());
506 result.setLinkStatus(
static_cast<ResultSet::Result::LinkStatus
>(
query.
value(QStringLiteral(
"linkStatus")).toUInt()));
508 auto linkedActivitiesQuery = database->createQuery();
510 linkedActivitiesQuery.prepare(QStringLiteral(R
"(
513 WHERE targettedResource = :resource
516 linkedActivitiesQuery.bindValue(QStringLiteral(":resource"), result.resource());
517 linkedActivitiesQuery.exec();
519 QStringList linkedActivities;
520 for (
const auto &item : linkedActivitiesQuery) {
521 linkedActivities << item[0].toString();
524 result.setLinkedActivities(linkedActivities);
532 : d(new ResultSetPrivate())
534 using namespace Common;
536 d->database = Database::instance(Database::ResourcesDatabase, Database::ReadOnly);
538 if (!(d->database)) {
539 qCWarning(PLASMA_ACTIVITIES_STATS_LOG) <<
"Plasma Activities ERROR: There is no database. This probably means "
540 "that you do not have the Activity Manager running, or that "
541 "something else is broken on your system. Recent documents and "
542 "alike will not work!";
545 d->queryDefinition = queryDefinition;
553 std::swap(d, source.d);
556ResultSet::~ResultSet()
563 if (!d->query.isActive()) {
567 d->query.seek(index);
569 return d->currentResult();
575#include "resultset_iterator.cpp"
The activities system tracks resources (documents, contacts, etc.) that the user has used.
Structure containing data of one of the results.
QUrl url() const
Url representation of a resource based on internal resource, readonly,.
Class that can query the KActivities usage tracking mechanism for resources.
Result at(int index) const
ResultSet(Query query)
Creates the ResultSet from the specified query.
Q_SCRIPTABLE Q_NOREPLY void start()
char * toString(const EngineQuery &query)
Provides enums and strucss to use.for building queries with Query.
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
QCoreApplication * instance()
bool isNull() const const
bool isAbsolutePath(const QString &path)
const_iterator cbegin() const const
const_iterator cend() const const
bool isEmpty() const const
T value(qsizetype i) const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString join(QChar separator) const const
QUrl fromLocalFile(const QString &localFile)