summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2016-05-01 16:22:55 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2016-05-01 16:22:55 +0200
commit096d14f20f4dc39d20d35d605ca755b66bd48cf9 (patch)
tree4b28dd3b310ebcfa9cefa1b41992eeefa5456764 /common
parentc1475df297975b403d991f69ef9436cd576c1e46 (diff)
downloadsink-096d14f20f4dc39d20d35d605ca755b66bd48cf9.tar.gz
sink-096d14f20f4dc39d20d35d605ca755b66bd48cf9.zip
Account filter for resources and contains comparator in query
Diffstat (limited to 'common')
-rw-r--r--common/CMakeLists.txt1
-rw-r--r--common/query.cpp56
-rw-r--r--common/query.h43
-rw-r--r--common/queryrunner.cpp10
-rw-r--r--common/resourcefacade.cpp7
-rw-r--r--common/store.cpp25
-rw-r--r--common/typeindex.cpp4
7 files changed, 129 insertions, 17 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 54d86f3..c269a85 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -65,6 +65,7 @@ set(command_SRCS
65 domain/mail.cpp 65 domain/mail.cpp
66 domain/folder.cpp 66 domain/folder.cpp
67 test.cpp 67 test.cpp
68 query.cpp
68 ${storage_SRCS}) 69 ${storage_SRCS})
69 70
70add_library(${PROJECT_NAME} SHARED ${command_SRCS}) 71add_library(${PROJECT_NAME} SHARED ${command_SRCS})
diff --git a/common/query.cpp b/common/query.cpp
new file mode 100644
index 0000000..a80aecb
--- /dev/null
+++ b/common/query.cpp
@@ -0,0 +1,56 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20#include "query.h"
21
22using namespace Sink;
23
24QDebug operator<<(QDebug dbg, const Sink::Query::Comparator &c)
25{
26 if (c.comparator == Sink::Query::Comparator::Equals) {
27 dbg.nospace() << "== " << c.value;
28 } else if (c.comparator == Sink::Query::Comparator::Contains) {
29 dbg.nospace() << "contains " << c.value;
30 } else {
31 dbg.nospace() << "unknown comparator: " << c.value;
32 }
33
34 return dbg.space();
35}
36
37Query::Comparator::Comparator() : comparator(Invalid)
38{
39}
40
41Query::Comparator::Comparator(const QVariant &v) : value(v), comparator(Equals)
42{
43}
44
45bool Query::Comparator::matches(const QVariant &v) const
46{
47 switch(comparator) {
48 case Equals:
49 return v == value;
50 case Contains:
51 return v.toList().contains(value);
52 default:
53 break;
54 }
55 return false;
56}
diff --git a/common/query.h b/common/query.h
index 3a56c9f..ccac1e7 100644
--- a/common/query.h
+++ b/common/query.h
@@ -19,6 +19,7 @@
19 */ 19 */
20#pragma once 20#pragma once
21 21
22#include "sink_export.h"
22#include <QByteArrayList> 23#include <QByteArrayList>
23#include <QHash> 24#include <QHash>
24#include <QSet> 25#include <QSet>
@@ -29,7 +30,7 @@ namespace Sink {
29/** 30/**
30 * A query that matches a set of entities. 31 * A query that matches a set of entities.
31 */ 32 */
32class Query 33class SINK_EXPORT Query
33{ 34{
34public: 35public:
35 enum Flag 36 enum Flag
@@ -46,6 +47,13 @@ public:
46 return query; 47 return query;
47 } 48 }
48 49
50 static Query PropertyContainsFilter(const QByteArray &key, const QVariant &value)
51 {
52 Query query;
53 query.propertyFilter.insert(key, value);
54 return query;
55 }
56
49 static Query PropertyFilter(const QByteArray &key, const ApplicationDomain::Entity &entity) 57 static Query PropertyFilter(const QByteArray &key, const ApplicationDomain::Entity &entity)
50 { 58 {
51 return PropertyFilter(key, QVariant::fromValue(entity.identifier())); 59 return PropertyFilter(key, QVariant::fromValue(entity.identifier()));
@@ -70,6 +78,18 @@ public:
70 return ResourceFilter(entity.identifier()); 78 return ResourceFilter(entity.identifier());
71 } 79 }
72 80
81 static Query AccountFilter(const QByteArrayList &identifier)
82 {
83 Query query;
84 query.accounts = identifier;
85 return query;
86 }
87
88 static Query AccountFilter(const ApplicationDomain::SinkAccount &entity)
89 {
90 return AccountFilter(entity.identifier());
91 }
92
73 static Query IdentityFilter(const QByteArray &identifier) 93 static Query IdentityFilter(const QByteArray &identifier)
74 { 94 {
75 Query query; 95 Query query;
@@ -110,6 +130,7 @@ public:
110 Query &operator+=(const Query &rhs) 130 Query &operator+=(const Query &rhs)
111 { 131 {
112 resources += rhs.resources; 132 resources += rhs.resources;
133 accounts += rhs.accounts;
113 ids += rhs.ids; 134 ids += rhs.ids;
114 for (auto it = rhs.propertyFilter.constBegin(); it != rhs.propertyFilter.constEnd(); it++) { 135 for (auto it = rhs.propertyFilter.constBegin(); it != rhs.propertyFilter.constEnd(); it++) {
115 propertyFilter.insert(it.key(), it.value()); 136 propertyFilter.insert(it.key(), it.value());
@@ -128,9 +149,25 @@ public:
128 return lhs; 149 return lhs;
129 } 150 }
130 151
152 struct Comparator {
153 enum Comparators {
154 Invalid,
155 Equals,
156 Contains
157 };
158
159 Comparator();
160 Comparator(const QVariant &v);
161 bool matches(const QVariant &v) const;
162
163 QVariant value;
164 Comparators comparator;
165 };
166
131 QByteArrayList resources; 167 QByteArrayList resources;
168 QByteArrayList accounts;
132 QByteArrayList ids; 169 QByteArrayList ids;
133 QHash<QByteArray, QVariant> propertyFilter; 170 QHash<QByteArray, Comparator> propertyFilter;
134 QByteArrayList requestedProperties; 171 QByteArrayList requestedProperties;
135 QByteArray parentProperty; 172 QByteArray parentProperty;
136 QByteArray sortProperty; 173 QByteArray sortProperty;
@@ -139,4 +176,6 @@ public:
139}; 176};
140} 177}
141 178
179QDebug operator<<(QDebug dbg, const Sink::Query::Comparator &c);
180
142Q_DECLARE_OPERATORS_FOR_FLAGS(Sink::Query::Flags) 181Q_DECLARE_OPERATORS_FOR_FLAGS(Sink::Query::Flags)
diff --git a/common/queryrunner.cpp b/common/queryrunner.cpp
index bbaae7b..38fc779 100644
--- a/common/queryrunner.cpp
+++ b/common/queryrunner.cpp
@@ -382,9 +382,9 @@ QueryWorker<DomainType>::getFilter(const QSet<QByteArray> remainingFilters, cons
382 for (const auto &filterProperty : remainingFilters) { 382 for (const auto &filterProperty : remainingFilters) {
383 const auto property = domainObject->getProperty(filterProperty); 383 const auto property = domainObject->getProperty(filterProperty);
384 if (property.isValid()) { 384 if (property.isValid()) {
385 // TODO implement other comparison operators than equality 385 const auto comparator = query.propertyFilter.value(filterProperty);
386 if (property != query.propertyFilter.value(filterProperty)) { 386 if (!comparator.matches(property)) {
387 Trace() << "Filtering entity due to property mismatch on filter: " << filterProperty << property << ":" << query.propertyFilter.value(filterProperty); 387 Trace() << "Filtering entity due to property mismatch on filter: " << filterProperty << property << ":" << comparator.value;
388 return false; 388 return false;
389 } 389 }
390 } else { 390 } else {
@@ -445,10 +445,10 @@ QPair<qint64, qint64> QueryWorker<DomainType>::executeInitialQuery(
445 if (!query.parentProperty.isEmpty()) { 445 if (!query.parentProperty.isEmpty()) {
446 if (parent) { 446 if (parent) {
447 Trace() << "Running initial query for parent:" << parent->identifier(); 447 Trace() << "Running initial query for parent:" << parent->identifier();
448 modifiedQuery.propertyFilter.insert(query.parentProperty, parent->identifier()); 448 modifiedQuery.propertyFilter.insert(query.parentProperty, Query::Comparator(parent->identifier()));
449 } else { 449 } else {
450 Trace() << "Running initial query for toplevel"; 450 Trace() << "Running initial query for toplevel";
451 modifiedQuery.propertyFilter.insert(query.parentProperty, QVariant()); 451 modifiedQuery.propertyFilter.insert(query.parentProperty, Query::Comparator(QVariant()));
452 } 452 }
453 } 453 }
454 auto revisionAndReplayedEntities = load(modifiedQuery, [&](Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters, QByteArray &remainingSorting) -> ResultSet { 454 auto revisionAndReplayedEntities = load(modifiedQuery, [&](Sink::Storage::Transaction &transaction, QSet<QByteArray> &remainingFilters, QByteArray &remainingSorting) -> ResultSet {
diff --git a/common/resourcefacade.cpp b/common/resourcefacade.cpp
index e6d235f..96e2ac3 100644
--- a/common/resourcefacade.cpp
+++ b/common/resourcefacade.cpp
@@ -113,13 +113,13 @@ KAsync::Job<void> LocalStorageFacade<DomainType>::remove(const DomainType &domai
113 }); 113 });
114} 114}
115 115
116static bool matchesFilter(const QHash<QByteArray, QVariant> &filter, const QMap<QByteArray, QVariant> &properties) 116static bool matchesFilter(const QHash<QByteArray, Sink::Query::Comparator> &filter, const QMap<QByteArray, QVariant> &properties)
117{ 117{
118 for (const auto &filterProperty : filter.keys()) { 118 for (const auto &filterProperty : filter.keys()) {
119 if (filterProperty == "type") { 119 if (filterProperty == "type") {
120 continue; 120 continue;
121 } 121 }
122 if (filter.value(filterProperty).toByteArray() != properties.value(filterProperty).toByteArray()) { 122 if (filter.value(filterProperty).matches(properties.value(filterProperty))) {
123 return false; 123 return false;
124 } 124 }
125 } 125 }
@@ -138,7 +138,8 @@ QPair<KAsync::Job<void>, typename Sink::ResultEmitter<typename DomainType::Ptr>:
138 const auto entries = mConfigStore.getEntries(); 138 const auto entries = mConfigStore.getEntries();
139 for (const auto &res : entries.keys()) { 139 for (const auto &res : entries.keys()) {
140 const auto type = entries.value(res); 140 const auto type = entries.value(res);
141 if (query.propertyFilter.contains("type") && query.propertyFilter.value("type").toByteArray() != type) { 141
142 if (query.propertyFilter.contains("type") && query.propertyFilter.value("type").value.toByteArray() != type) {
142 Trace() << "Skipping due to type."; 143 Trace() << "Skipping due to type.";
143 continue; 144 continue;
144 } 145 }
diff --git a/common/store.cpp b/common/store.cpp
index b89e08c..0ac99be 100644
--- a/common/store.cpp
+++ b/common/store.cpp
@@ -54,8 +54,16 @@ QString Store::getTemporaryFilePath()
54/* 54/*
55 * Returns a map of resource instance identifiers and resource type 55 * Returns a map of resource instance identifiers and resource type
56 */ 56 */
57static QMap<QByteArray, QByteArray> getResources(const QList<QByteArray> &resourceFilter, const QByteArray &type = QByteArray()) 57static QMap<QByteArray, QByteArray> getResources(const QList<QByteArray> &resourceFilter, const QList<QByteArray> &accountFilter,const QByteArray &type = QByteArray())
58{ 58{
59 const auto filterResource = [&](const QByteArray &res) {
60 const auto configuration = ResourceConfig::getConfiguration(res);
61 if (!accountFilter.isEmpty() && !accountFilter.contains(configuration.value("account").toByteArray())) {
62 return true;
63 }
64 return false;
65 };
66
59 QMap<QByteArray, QByteArray> resources; 67 QMap<QByteArray, QByteArray> resources;
60 // Return the global resource (signified by an empty name) for types that don't belong to a specific resource 68 // Return the global resource (signified by an empty name) for types that don't belong to a specific resource
61 if (type == "sinkresource" || type == "sinkaccount" || type == "identity") { 69 if (type == "sinkresource" || type == "sinkaccount" || type == "identity") {
@@ -65,15 +73,22 @@ static QMap<QByteArray, QByteArray> getResources(const QList<QByteArray> &resour
65 const auto configuredResources = ResourceConfig::getResources(); 73 const auto configuredResources = ResourceConfig::getResources();
66 if (resourceFilter.isEmpty()) { 74 if (resourceFilter.isEmpty()) {
67 for (const auto &res : configuredResources.keys()) { 75 for (const auto &res : configuredResources.keys()) {
76 const auto type = configuredResources.value(res);
77 if (filterResource(res)) {
78 continue;
79 }
68 // TODO filter by entity type 80 // TODO filter by entity type
69 resources.insert(res, configuredResources.value(res)); 81 resources.insert(res, type);
70 } 82 }
71 } else { 83 } else {
72 for (const auto &res : resourceFilter) { 84 for (const auto &res : resourceFilter) {
73 if (configuredResources.contains(res)) { 85 if (configuredResources.contains(res)) {
86 if (filterResource(res)) {
87 continue;
88 }
74 resources.insert(res, configuredResources.value(res)); 89 resources.insert(res, configuredResources.value(res));
75 } else { 90 } else {
76 qWarning() << "Resource is not existing: " << res; 91 Warning() << "Resource is not existing: " << res;
77 } 92 }
78 } 93 }
79 } 94 }
@@ -100,7 +115,7 @@ QSharedPointer<QAbstractItemModel> Store::loadModel(Query query)
100 //* The result provider needs to live for as long as results are provided (until the last thread exits). 115 //* The result provider needs to live for as long as results are provided (until the last thread exits).
101 116
102 // Query all resources and aggregate results 117 // Query all resources and aggregate results
103 auto resources = getResources(query.resources, ApplicationDomain::getTypeName<DomainType>()); 118 auto resources = getResources(query.resources, query.accounts, ApplicationDomain::getTypeName<DomainType>());
104 auto aggregatingEmitter = AggregatingResultEmitter<typename DomainType::Ptr>::Ptr::create(); 119 auto aggregatingEmitter = AggregatingResultEmitter<typename DomainType::Ptr>::Ptr::create();
105 model->setEmitter(aggregatingEmitter); 120 model->setEmitter(aggregatingEmitter);
106 KAsync::iterate(resources.keys()) 121 KAsync::iterate(resources.keys())
@@ -181,7 +196,7 @@ KAsync::Job<void> Store::removeDataFromDisk(const QByteArray &identifier)
181KAsync::Job<void> Store::synchronize(const Sink::Query &query) 196KAsync::Job<void> Store::synchronize(const Sink::Query &query)
182{ 197{
183 Trace() << "synchronize" << query.resources; 198 Trace() << "synchronize" << query.resources;
184 auto resources = getResources(query.resources).keys(); 199 auto resources = getResources(query.resources, query.accounts).keys();
185 return KAsync::iterate(resources) 200 return KAsync::iterate(resources)
186 .template each<void, QByteArray>([query](const QByteArray &resource, KAsync::Future<void> &future) { 201 .template each<void, QByteArray>([query](const QByteArray &resource, KAsync::Future<void> &future) {
187 Trace() << "Synchronizing " << resource; 202 Trace() << "Synchronizing " << resource;
diff --git a/common/typeindex.cpp b/common/typeindex.cpp
index 1321469..fca083c 100644
--- a/common/typeindex.cpp
+++ b/common/typeindex.cpp
@@ -143,7 +143,7 @@ ResultSet TypeIndex::query(const Sink::Query &query, QSet<QByteArray> &appliedFi
143 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) { 143 for (auto it = mSortedProperties.constBegin(); it != mSortedProperties.constEnd(); it++) {
144 if (query.propertyFilter.contains(it.key()) && query.sortProperty == it.value()) { 144 if (query.propertyFilter.contains(it.key()) && query.sortProperty == it.value()) {
145 Index index(indexName(it.key(), it.value()), transaction); 145 Index index(indexName(it.key(), it.value()), transaction);
146 const auto lookupKey = getByteArray(query.propertyFilter.value(it.key())); 146 const auto lookupKey = getByteArray(query.propertyFilter.value(it.key()).value);
147 Trace() << "looking for " << lookupKey; 147 Trace() << "looking for " << lookupKey;
148 index.lookup(lookupKey, [&](const QByteArray &value) { keys << value; }, 148 index.lookup(lookupKey, [&](const QByteArray &value) { keys << value; },
149 [it](const Index::Error &error) { Warning() << "Error in index: " << error.message << it.key() << it.value(); }, true); 149 [it](const Index::Error &error) { Warning() << "Error in index: " << error.message << it.key() << it.value(); }, true);
@@ -156,7 +156,7 @@ ResultSet TypeIndex::query(const Sink::Query &query, QSet<QByteArray> &appliedFi
156 for (const auto &property : mProperties) { 156 for (const auto &property : mProperties) {
157 if (query.propertyFilter.contains(property)) { 157 if (query.propertyFilter.contains(property)) {
158 Index index(indexName(property), transaction); 158 Index index(indexName(property), transaction);
159 const auto lookupKey = getByteArray(query.propertyFilter.value(property)); 159 const auto lookupKey = getByteArray(query.propertyFilter.value(property).value);
160 index.lookup( 160 index.lookup(
161 lookupKey, [&](const QByteArray &value) { keys << value; }, [property](const Index::Error &error) { Warning() << "Error in index: " << error.message << property; }); 161 lookupKey, [&](const QByteArray &value) { keys << value; }, [property](const Index::Error &error) { Warning() << "Error in index: " << error.message << property; });
162 appliedFilters << property; 162 appliedFilters << property;