diff options
-rw-r--r-- | common/datastorequery.cpp | 121 | ||||
-rw-r--r-- | common/datastorequery.h | 7 | ||||
-rw-r--r-- | common/domain/applicationdomaintype.h | 8 | ||||
-rw-r--r-- | common/domain/mail.cpp | 80 | ||||
-rw-r--r-- | common/modelresult.cpp | 6 | ||||
-rw-r--r-- | common/typeindex.cpp | 68 | ||||
-rw-r--r-- | common/typeindex.h | 39 |
7 files changed, 251 insertions, 78 deletions
diff --git a/common/datastorequery.cpp b/common/datastorequery.cpp index 95df1a0..7c0fdea 100644 --- a/common/datastorequery.cpp +++ b/common/datastorequery.cpp | |||
@@ -141,51 +141,78 @@ public: | |||
141 | } | 141 | } |
142 | }; | 142 | }; |
143 | 143 | ||
144 | /* class Reduction : public FilterBase { */ | 144 | class Reduce : public FilterBase { |
145 | /* public: */ | 145 | public: |
146 | /* typedef QSharedPointer<Reduction> Ptr; */ | 146 | typedef QSharedPointer<Reduce> Ptr; |
147 | |||
148 | QHash<QByteArray, QVariant> mAggregateValues; | ||
149 | QByteArray mReductionProperty; | ||
150 | QByteArray mSelectionProperty; | ||
151 | enum SelectionComparator { | ||
152 | Max | ||
153 | /* Min, */ | ||
154 | /* First */ | ||
155 | }; | ||
156 | SelectionComparator mSelectionComparator; | ||
147 | 157 | ||
148 | /* QHash<QByteArray, QDateTime> aggregateValues; */ | 158 | Reduce(const QByteArray &reductionProperty, const QByteArray &selectionProperty, SelectionComparator comparator, FilterBase::Ptr source, DataStoreQuery *store) |
159 | : FilterBase(source, store), | ||
160 | mReductionProperty(reductionProperty), | ||
161 | mSelectionProperty(selectionProperty), | ||
162 | mSelectionComparator(comparator) | ||
163 | { | ||
149 | 164 | ||
150 | /* Reduction(FilterBase::Ptr source, DataStoreQuery *store) */ | 165 | } |
151 | /* : FilterBase(source, store) */ | ||
152 | /* { */ | ||
153 | 166 | ||
154 | /* } */ | 167 | virtual ~Reduce(){} |
155 | 168 | ||
156 | /* virtual ~Reduction(){} */ | 169 | static QByteArray getByteArray(const QVariant &value) |
157 | 170 | { | |
158 | /* bool next(const std::function<void(const QByteArray &uid, const Sink::EntityBuffer &entityBuffer)> &callback) Q_DECL_OVERRIDE { */ | 171 | if (value.type() == QVariant::DateTime) { |
159 | /* bool foundValue = false; */ | 172 | return value.toDateTime().toString().toLatin1(); |
160 | /* while(!foundValue && mSource->next([this, callback, &foundValue](const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { */ | 173 | } |
161 | /* const auto operation = entityBuffer.operation(); */ | 174 | if (value.isValid() && !value.toByteArray().isEmpty()) { |
162 | /* SinkTrace() << "Filter: " << uid << operation; */ | 175 | return value.toByteArray(); |
163 | /* //Always accept removals. They can't match the filter since the data is gone. */ | 176 | } |
164 | /* if (operation == Sink::Operation_Removal) { */ | 177 | return QByteArray(); |
165 | /* callback(uid, entityBuffer); */ | 178 | } |
166 | /* foundValue = true; */ | ||
167 | /* } else if (matchesFilter(uid, entityBuffer)) { */ | ||
168 | /* callback(uid, entityBuffer); */ | ||
169 | /* foundValue = true; */ | ||
170 | /* } */ | ||
171 | /* return false; */ | ||
172 | /* })) */ | ||
173 | /* {} */ | ||
174 | /* return foundValue; */ | ||
175 | /* } */ | ||
176 | 179 | ||
177 | /* bool matchesFilter(const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { */ | 180 | static bool compare(const QVariant &left, const QVariant &right, SelectionComparator comparator) |
178 | /* for (const auto &filterProperty : propertyFilter.keys()) { */ | 181 | { |
179 | /* const auto property = getProperty(entityBuffer.entity(), filterProperty); */ | 182 | if (comparator == Max) { |
180 | /* const auto comparator = propertyFilter.value(filterProperty); */ | 183 | return left > right; |
181 | /* if (!comparator.matches(property)) { */ | 184 | } |
182 | /* SinkTrace() << "Filtering entity due to property mismatch on filter: " << filterProperty << property << ":" << comparator.value; */ | 185 | return false; |
183 | /* return false; */ | 186 | } |
184 | /* } */ | 187 | |
185 | /* } */ | 188 | bool next(const std::function<void(Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer)> &callback) Q_DECL_OVERRIDE { |
186 | /* return true; */ | 189 | bool foundValue = false; |
187 | /* } */ | 190 | while(!foundValue && mSource->next([this, callback, &foundValue](Sink::Operation operation, const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { |
188 | /* }; */ | 191 | auto reductionValue = getProperty(entityBuffer.entity(), mReductionProperty); |
192 | if (!mAggregateValues.contains(getByteArray(reductionValue))) { | ||
193 | QVariant selectionResultValue; | ||
194 | QByteArray selectionResult; | ||
195 | auto results = indexLookup(mReductionProperty, reductionValue); | ||
196 | for (const auto r : results) { | ||
197 | readEntity(r, [&, this](const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { | ||
198 | auto selectionValue = getProperty(entityBuffer.entity(), mSelectionProperty); | ||
199 | if (!selectionResultValue.isValid() || compare(selectionValue, selectionResultValue, mSelectionComparator)) { | ||
200 | selectionResultValue = selectionValue; | ||
201 | selectionResult = uid; | ||
202 | } | ||
203 | }); | ||
204 | } | ||
205 | readEntity(selectionResult, [&, this](const QByteArray &uid, const Sink::EntityBuffer &entityBuffer) { | ||
206 | callback(Sink::Operation_Creation, uid, entityBuffer); | ||
207 | foundValue = true; | ||
208 | }); | ||
209 | } | ||
210 | return false; | ||
211 | })) | ||
212 | {} | ||
213 | return foundValue; | ||
214 | } | ||
215 | }; | ||
189 | 216 | ||
190 | DataStoreQuery::DataStoreQuery(const Sink::Query &query, const QByteArray &type, Sink::Storage::Transaction &transaction, TypeIndex &typeIndex, std::function<QVariant(const Sink::Entity &entity, const QByteArray &property)> getProperty) | 217 | DataStoreQuery::DataStoreQuery(const Sink::Query &query, const QByteArray &type, Sink::Storage::Transaction &transaction, TypeIndex &typeIndex, std::function<QVariant(const Sink::Entity &entity, const QByteArray &property)> getProperty) |
191 | : mQuery(query), mTransaction(transaction), mType(type), mTypeIndex(typeIndex), mDb(Storage::mainDatabase(mTransaction, mType)), mGetProperty(getProperty) | 218 | : mQuery(query), mTransaction(transaction), mType(type), mTypeIndex(typeIndex), mDb(Storage::mainDatabase(mTransaction, mType)), mGetProperty(getProperty) |
@@ -230,6 +257,11 @@ QVariant DataStoreQuery::getProperty(const Sink::Entity &entity, const QByteArra | |||
230 | return mGetProperty(entity, property); | 257 | return mGetProperty(entity, property); |
231 | } | 258 | } |
232 | 259 | ||
260 | QVector<QByteArray> DataStoreQuery::indexLookup(const QByteArray &property, const QVariant &value) | ||
261 | { | ||
262 | return mTypeIndex.lookup(property, value, mTransaction); | ||
263 | } | ||
264 | |||
233 | /* ResultSet DataStoreQuery::filterAndSortSet(ResultSet &resultSet, const FilterFunction &filter, const QByteArray &sortProperty) */ | 265 | /* ResultSet DataStoreQuery::filterAndSortSet(ResultSet &resultSet, const FilterFunction &filter, const QByteArray &sortProperty) */ |
234 | /* { */ | 266 | /* { */ |
235 | /* const bool sortingRequired = !sortProperty.isEmpty(); */ | 267 | /* const bool sortingRequired = !sortProperty.isEmpty(); */ |
@@ -347,11 +379,10 @@ void DataStoreQuery::setupQuery() | |||
347 | /* baseSet = Sort::Ptr::create(baseSet, mQuery.sortProperty); */ | 379 | /* baseSet = Sort::Ptr::create(baseSet, mQuery.sortProperty); */ |
348 | /* } */ | 380 | /* } */ |
349 | 381 | ||
350 | /* if (mQuery.threadLeaderOnly) { */ | 382 | if (mQuery.threadLeaderOnly) { |
351 | /* auto reduce = Reduce::Ptr::create(baseSet, this); */ | 383 | auto reduce = Reduce::Ptr::create("threadId", "date", Reduce::Max, baseSet, this); |
352 | 384 | baseSet = reduce; | |
353 | /* baseSet = reduce; */ | 385 | } |
354 | /* } */ | ||
355 | 386 | ||
356 | mCollector = Collector::Ptr::create(baseSet, this); | 387 | mCollector = Collector::Ptr::create(baseSet, this); |
357 | } | 388 | } |
diff --git a/common/datastorequery.h b/common/datastorequery.h index c9f6a3a..ea61780 100644 --- a/common/datastorequery.h +++ b/common/datastorequery.h | |||
@@ -44,6 +44,7 @@ protected: | |||
44 | typedef std::function<void(const QByteArray &uid, const Sink::EntityBuffer &entityBuffer)> BufferCallback; | 44 | typedef std::function<void(const QByteArray &uid, const Sink::EntityBuffer &entityBuffer)> BufferCallback; |
45 | 45 | ||
46 | virtual QVariant getProperty(const Sink::Entity &entity, const QByteArray &property); | 46 | virtual QVariant getProperty(const Sink::Entity &entity, const QByteArray &property); |
47 | QVector<QByteArray> indexLookup(const QByteArray &property, const QVariant &value); | ||
47 | 48 | ||
48 | virtual void readEntity(const QByteArray &key, const BufferCallback &resultCallback); | 49 | virtual void readEntity(const QByteArray &key, const BufferCallback &resultCallback); |
49 | 50 | ||
@@ -100,6 +101,12 @@ public: | |||
100 | return mDatastore->getProperty(entity, property); | 101 | return mDatastore->getProperty(entity, property); |
101 | } | 102 | } |
102 | 103 | ||
104 | QVector<QByteArray> indexLookup(const QByteArray &property, const QVariant &value) | ||
105 | { | ||
106 | Q_ASSERT(mDatastore); | ||
107 | return mDatastore->indexLookup(property, value); | ||
108 | } | ||
109 | |||
103 | virtual void skip() { mSource->skip(); }; | 110 | virtual void skip() { mSource->skip(); }; |
104 | 111 | ||
105 | //Returns true for as long as a result is available | 112 | //Returns true for as long as a result is available |
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h index c853397..c824251 100644 --- a/common/domain/applicationdomaintype.h +++ b/common/domain/applicationdomaintype.h | |||
@@ -79,6 +79,12 @@ | |||
79 | void set##NAME(const QByteArray &value) { setProperty(NAME::name, QVariant::fromValue(value)); } \ | 79 | void set##NAME(const QByteArray &value) { setProperty(NAME::name, QVariant::fromValue(value)); } \ |
80 | QByteArray get##NAME() const { return getProperty(NAME::name).value<QByteArray>(); } \ | 80 | QByteArray get##NAME() const { return getProperty(NAME::name).value<QByteArray>(); } \ |
81 | 81 | ||
82 | #define SINK_INDEX_PROPERTY(TYPE, NAME, LOWERCASENAME) \ | ||
83 | struct NAME { \ | ||
84 | static constexpr const char *name = #LOWERCASENAME; \ | ||
85 | typedef TYPE Type; \ | ||
86 | }; \ | ||
87 | |||
82 | 88 | ||
83 | namespace Sink { | 89 | namespace Sink { |
84 | namespace ApplicationDomain { | 90 | namespace ApplicationDomain { |
@@ -240,6 +246,7 @@ struct SINK_EXPORT Mail : public Entity { | |||
240 | SINK_PROPERTY(bool, Sent, sent); | 246 | SINK_PROPERTY(bool, Sent, sent); |
241 | SINK_EXTRACTED_PROPERTY(QByteArray, MessageId, messageId); | 247 | SINK_EXTRACTED_PROPERTY(QByteArray, MessageId, messageId); |
242 | SINK_EXTRACTED_PROPERTY(QByteArray, ParentMessageId, parentMessageId); | 248 | SINK_EXTRACTED_PROPERTY(QByteArray, ParentMessageId, parentMessageId); |
249 | SINK_INDEX_PROPERTY(QByteArray, ThreadId, threadId); | ||
243 | }; | 250 | }; |
244 | 251 | ||
245 | /** | 252 | /** |
@@ -378,6 +385,7 @@ class SINK_EXPORT TypeImplementation; | |||
378 | #undef SINK_EXTRACTED_PROPERTY | 385 | #undef SINK_EXTRACTED_PROPERTY |
379 | #undef SINK_BLOB_PROPERTY | 386 | #undef SINK_BLOB_PROPERTY |
380 | #undef SINK_REFERENCE_PROPERTY | 387 | #undef SINK_REFERENCE_PROPERTY |
388 | #undef SINK_INDEX_PROPERTY | ||
381 | 389 | ||
382 | Q_DECLARE_METATYPE(Sink::ApplicationDomain::ApplicationDomainType) | 390 | Q_DECLARE_METATYPE(Sink::ApplicationDomain::ApplicationDomainType) |
383 | Q_DECLARE_METATYPE(Sink::ApplicationDomain::ApplicationDomainType::Ptr) | 391 | Q_DECLARE_METATYPE(Sink::ApplicationDomain::ApplicationDomainType::Ptr) |
diff --git a/common/domain/mail.cpp b/common/domain/mail.cpp index 483a2f2..2b6eb84 100644 --- a/common/domain/mail.cpp +++ b/common/domain/mail.cpp | |||
@@ -60,6 +60,10 @@ static TypeIndex &getIndex() | |||
60 | index->addPropertyWithSorting<QByteArray, QDateTime>(Mail::Folder::name, Mail::Date::name); | 60 | index->addPropertyWithSorting<QByteArray, QDateTime>(Mail::Folder::name, Mail::Date::name); |
61 | index->addProperty<QByteArray>(Mail::MessageId::name); | 61 | index->addProperty<QByteArray>(Mail::MessageId::name); |
62 | index->addProperty<QByteArray>(Mail::ParentMessageId::name); | 62 | index->addProperty<QByteArray>(Mail::ParentMessageId::name); |
63 | |||
64 | index->addProperty<Mail::MessageId>(); | ||
65 | index->addSecondaryProperty<Mail::MessageId, Mail::ThreadId>(); | ||
66 | index->addSecondaryProperty<Mail::ThreadId, Mail::MessageId>(); | ||
63 | } | 67 | } |
64 | return *index; | 68 | return *index; |
65 | } | 69 | } |
@@ -120,42 +124,44 @@ static QString stripOffPrefixes(const QString &subject) | |||
120 | 124 | ||
121 | static void updateThreadingIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) | 125 | static void updateThreadingIndex(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) |
122 | { | 126 | { |
123 | auto messageId = bufferAdaptor.getProperty(Mail::MessageId::name).toByteArray(); | 127 | auto messageId = bufferAdaptor.getProperty(Mail::MessageId::name); |
124 | auto parentMessageId = bufferAdaptor.getProperty(Mail::ParentMessageId::name).toByteArray(); | 128 | auto parentMessageId = bufferAdaptor.getProperty(Mail::ParentMessageId::name); |
125 | auto subject = bufferAdaptor.getProperty(Mail::Subject::name).toString(); | 129 | auto subject = bufferAdaptor.getProperty(Mail::Subject::name); |
126 | 130 | ||
127 | Index msgIdIndex("msgId", transaction); | 131 | auto normalizedSubject = stripOffPrefixes(subject.toString()).toUtf8(); |
128 | Index msgIdThreadIdIndex("msgIdThreadId", transaction); | ||
129 | Index subjectThreadIdIndex("subjectThreadId", transaction); | ||
130 | 132 | ||
131 | //Add the message to the index | 133 | QVector<QByteArray> thread; |
132 | Q_ASSERT(msgIdIndex.lookup(messageId).isEmpty()); | ||
133 | msgIdIndex.add(messageId, identifier); | ||
134 | 134 | ||
135 | auto normalizedSubject = stripOffPrefixes(subject).toUtf8(); | 135 | //a child already registered our thread. |
136 | thread = getIndex().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId, transaction); | ||
136 | 137 | ||
137 | QByteArray thread; | ||
138 | //If parent is already available, add to thread of parent | 138 | //If parent is already available, add to thread of parent |
139 | if (!parentMessageId.isEmpty() && !msgIdIndex.lookup(parentMessageId).isEmpty()) { | 139 | if (thread.isEmpty() && parentMessageId.isValid()) { |
140 | thread = msgIdThreadIdIndex.lookup(parentMessageId); | 140 | thread = getIndex().secondaryLookup<Mail::MessageId, Mail::ThreadId>(parentMessageId, transaction); |
141 | msgIdThreadIdIndex.add(messageId, thread); | 141 | SinkTrace() << "Found parent"; |
142 | subjectThreadIdIndex.add(normalizedSubject, thread); | 142 | } |
143 | } else { | 143 | if (thread.isEmpty()) { |
144 | //Try to lookup the thread by subject: | 144 | //Try to lookup the thread by subject: |
145 | thread = subjectThreadIdIndex.lookup(normalizedSubject); | 145 | thread = getIndex().secondaryLookup<Mail::Subject, Mail::ThreadId>(normalizedSubject, transaction); |
146 | if (!thread.isEmpty()) { | 146 | if (thread.isEmpty()) { |
147 | msgIdThreadIdIndex.add(messageId, thread); | 147 | SinkTrace() << "Created a new thread "; |
148 | thread << QUuid::createUuid().toByteArray(); | ||
148 | } else { | 149 | } else { |
149 | thread = QUuid::createUuid().toByteArray(); | ||
150 | subjectThreadIdIndex.add(normalizedSubject, thread); | ||
151 | if (!parentMessageId.isEmpty()) { | ||
152 | //Register parent with thread for when it becomes available | ||
153 | msgIdThreadIdIndex.add(parentMessageId, thread); | ||
154 | } | ||
155 | } | 150 | } |
156 | } | 151 | } |
157 | Q_ASSERT(!thread.isEmpty()); | 152 | |
158 | msgIdThreadIdIndex.add(messageId, thread); | 153 | //We should have found the thread by now |
154 | if (!thread.isEmpty()) { | ||
155 | if (parentMessageId.isValid()) { | ||
156 | //Register parent with thread for when it becomes available | ||
157 | getIndex().index<Mail::MessageId, Mail::ThreadId>(parentMessageId, thread.first(), transaction); | ||
158 | } | ||
159 | getIndex().index<Mail::MessageId, Mail::ThreadId>(messageId, thread.first(), transaction); | ||
160 | getIndex().index<Mail::ThreadId, Mail::MessageId>(thread.first(), messageId, transaction); | ||
161 | getIndex().index<Mail::Subject, Mail::ThreadId>(normalizedSubject, thread.first(), transaction); | ||
162 | } else { | ||
163 | SinkWarning() << "Couldn't find a thread for: " << messageId; | ||
164 | } | ||
159 | } | 165 | } |
160 | 166 | ||
161 | void TypeImplementation<Mail>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) | 167 | void TypeImplementation<Mail>::index(const QByteArray &identifier, const BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction) |
@@ -214,13 +220,21 @@ QSharedPointer<WritePropertyMapper<TypeImplementation<Mail>::BufferBuilder> > Ty | |||
214 | 220 | ||
215 | DataStoreQuery::Ptr TypeImplementation<Mail>::prepareQuery(const Sink::Query &query, Sink::Storage::Transaction &transaction) | 221 | DataStoreQuery::Ptr TypeImplementation<Mail>::prepareQuery(const Sink::Query &query, Sink::Storage::Transaction &transaction) |
216 | { | 222 | { |
217 | 223 | auto mapper = initializeReadPropertyMapper(); | |
218 | 224 | return DataStoreQuery::Ptr::create(query, ApplicationDomain::getTypeName<Mail>(), transaction, getIndex(), [mapper, &transaction](const Sink::Entity &entity, const QByteArray &property) -> QVariant { | |
219 | auto mapper = initializeReadPropertyMapper(); | 225 | if (property == Mail::ThreadId::name) { |
220 | return DataStoreQuery::Ptr::create(query, ApplicationDomain::getTypeName<Mail>(), transaction, getIndex(), [mapper](const Sink::Entity &entity, const QByteArray &property) { | ||
221 | |||
222 | const auto localBuffer = Sink::EntityBuffer::readBuffer<Buffer>(entity.local()); | 226 | const auto localBuffer = Sink::EntityBuffer::readBuffer<Buffer>(entity.local()); |
227 | Q_ASSERT(localBuffer); | ||
228 | auto messageId = mapper->getProperty(Mail::MessageId::name, localBuffer); | ||
229 | //This is an index property that we have too lookup | ||
230 | auto thread = getIndex().secondaryLookup<Mail::MessageId, Mail::ThreadId>(messageId, transaction); | ||
231 | Q_ASSERT(!thread.isEmpty()); | ||
232 | return thread.first(); | ||
233 | } else { | ||
234 | const auto localBuffer = Sink::EntityBuffer::readBuffer<Buffer>(entity.local()); | ||
235 | Q_ASSERT(localBuffer); | ||
223 | return mapper->getProperty(property, localBuffer); | 236 | return mapper->getProperty(property, localBuffer); |
224 | }); | 237 | } |
238 | }); | ||
225 | } | 239 | } |
226 | 240 | ||
diff --git a/common/modelresult.cpp b/common/modelresult.cpp index d13bba9..add84aa 100644 --- a/common/modelresult.cpp +++ b/common/modelresult.cpp | |||
@@ -289,6 +289,12 @@ template <class T, class Ptr> | |||
289 | void ModelResult<T, Ptr>::modify(const Ptr &value) | 289 | void ModelResult<T, Ptr>::modify(const Ptr &value) |
290 | { | 290 | { |
291 | auto childId = qHash(*value); | 291 | auto childId = qHash(*value); |
292 | if (!mEntities.contains(childId)) { | ||
293 | //Happens because the DatabaseQuery emits modifiations also if the item used to be filtered. | ||
294 | SinkTrace() << "Tried to modify a value that is not yet part of the model"; | ||
295 | add(value); | ||
296 | return; | ||
297 | } | ||
292 | auto id = parentId(value); | 298 | auto id = parentId(value); |
293 | // Ignore updates we get before the initial fetch is done | 299 | // Ignore updates we get before the initial fetch is done |
294 | if (!mEntityChildrenFetched.contains(id)) { | 300 | if (!mEntityChildrenFetched.contains(id)) { |
diff --git a/common/typeindex.cpp b/common/typeindex.cpp index 1b04966..f537493 100644 --- a/common/typeindex.cpp +++ b/common/typeindex.cpp | |||
@@ -168,3 +168,71 @@ QVector<QByteArray> TypeIndex::query(const Sink::Query &query, QSet<QByteArray> | |||
168 | SinkTrace() << "No matching index"; | 168 | SinkTrace() << "No matching index"; |
169 | return keys; | 169 | return keys; |
170 | } | 170 | } |
171 | |||
172 | QVector<QByteArray> TypeIndex::lookup(const QByteArray &property, const QVariant &value, Sink::Storage::Transaction &transaction) | ||
173 | { | ||
174 | SinkTrace() << "Index lookup on property: " << property << mSecondaryProperties.keys() << mProperties; | ||
175 | if (mProperties.contains(property)) { | ||
176 | QVector<QByteArray> keys; | ||
177 | Index index(indexName(property), transaction); | ||
178 | const auto lookupKey = getByteArray(value); | ||
179 | index.lookup( | ||
180 | lookupKey, [&](const QByteArray &value) { keys << value; }, [property](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << property; }); | ||
181 | SinkTrace() << "Index lookup on " << property << " found " << keys.size() << " keys."; | ||
182 | return keys; | ||
183 | } else if (mSecondaryProperties.contains(property)) { | ||
184 | //Lookups on secondary indexes first lookup the key, and then lookup the results again to resolve to entity id's | ||
185 | QVector<QByteArray> keys; | ||
186 | auto resultProperty = mSecondaryProperties.value(property); | ||
187 | |||
188 | QVector<QByteArray> secondaryKeys; | ||
189 | Index index(indexName(property + resultProperty), transaction); | ||
190 | const auto lookupKey = getByteArray(value); | ||
191 | index.lookup( | ||
192 | lookupKey, [&](const QByteArray &value) { secondaryKeys << value; }, [property](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << property; }); | ||
193 | SinkTrace() << "Looked up secondary keys: " << secondaryKeys; | ||
194 | for (const auto &secondary : secondaryKeys) { | ||
195 | keys += lookup(resultProperty, secondary, transaction); | ||
196 | } | ||
197 | return keys; | ||
198 | } else { | ||
199 | SinkWarning() << "Tried to lookup " << property << " but couldn't find value"; | ||
200 | } | ||
201 | return QVector<QByteArray>(); | ||
202 | } | ||
203 | |||
204 | template <> | ||
205 | void TypeIndex::index<QByteArray, QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::Transaction &transaction) | ||
206 | { | ||
207 | Index(indexName(leftName + rightName), transaction).add(getByteArray(leftValue), getByteArray(rightValue)); | ||
208 | } | ||
209 | |||
210 | template <> | ||
211 | void TypeIndex::index<QString, QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::Transaction &transaction) | ||
212 | { | ||
213 | Index(indexName(leftName + rightName), transaction).add(getByteArray(leftValue), getByteArray(rightValue)); | ||
214 | } | ||
215 | |||
216 | template <> | ||
217 | QVector<QByteArray> TypeIndex::secondaryLookup<QByteArray>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &value, Sink::Storage::Transaction &transaction) | ||
218 | { | ||
219 | QVector<QByteArray> keys; | ||
220 | Index index(indexName(leftName + rightName), transaction); | ||
221 | const auto lookupKey = getByteArray(value); | ||
222 | index.lookup( | ||
223 | lookupKey, [&](const QByteArray &value) { keys << value; }, [=](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << value; }); | ||
224 | |||
225 | return keys; | ||
226 | } | ||
227 | |||
228 | template <> | ||
229 | QVector<QByteArray> TypeIndex::secondaryLookup<QString>(const QByteArray &leftName, const QByteArray &rightName, const QVariant &value, Sink::Storage::Transaction &transaction) | ||
230 | { | ||
231 | QVector<QByteArray> keys; | ||
232 | Index index(indexName(leftName + rightName), transaction); | ||
233 | const auto lookupKey = getByteArray(value); | ||
234 | index.lookup( | ||
235 | lookupKey, [&](const QByteArray &value) { keys << value; }, [=](const Index::Error &error) { SinkWarning() << "Error in index: " << error.message << value; }); | ||
236 | |||
237 | return keys; | ||
238 | } | ||
diff --git a/common/typeindex.h b/common/typeindex.h index f5a32b9..7266f02 100644 --- a/common/typeindex.h +++ b/common/typeindex.h | |||
@@ -34,16 +34,55 @@ public: | |||
34 | template <typename T, typename S> | 34 | template <typename T, typename S> |
35 | void addPropertyWithSorting(const QByteArray &property, const QByteArray &sortProperty); | 35 | void addPropertyWithSorting(const QByteArray &property, const QByteArray &sortProperty); |
36 | 36 | ||
37 | template <typename T> | ||
38 | void addProperty() | ||
39 | { | ||
40 | addProperty<typename T::Type>(T::name); | ||
41 | } | ||
42 | |||
43 | template <typename T> | ||
44 | void addPropertyWithSorting() | ||
45 | { | ||
46 | addPropertyWithSorting<typename T::Type>(T::name); | ||
47 | } | ||
48 | |||
49 | template <typename Left, typename Right> | ||
50 | void addSecondaryProperty() | ||
51 | { | ||
52 | mSecondaryProperties.insert(Left::name, Right::name); | ||
53 | } | ||
37 | void add(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); | 54 | void add(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); |
38 | void remove(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); | 55 | void remove(const QByteArray &identifier, const Sink::ApplicationDomain::BufferAdaptor &bufferAdaptor, Sink::Storage::Transaction &transaction); |
39 | 56 | ||
40 | QVector<QByteArray> query(const Sink::Query &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction); | 57 | QVector<QByteArray> query(const Sink::Query &query, QSet<QByteArray> &appliedFilters, QByteArray &appliedSorting, Sink::Storage::Transaction &transaction); |
58 | QVector<QByteArray> lookup(const QByteArray &property, const QVariant &value, Sink::Storage::Transaction &transaction); | ||
59 | |||
60 | template <typename Left, typename Right> | ||
61 | QVector<QByteArray> secondaryLookup(const QVariant &value, Sink::Storage::Transaction &transaction) | ||
62 | { | ||
63 | return secondaryLookup<typename Left::Type>(Left::name, Right::name, value, transaction); | ||
64 | } | ||
65 | |||
66 | template <typename Type> | ||
67 | QVector<QByteArray> secondaryLookup(const QByteArray &leftName, const QByteArray &rightName, const QVariant &value, Sink::Storage::Transaction &transaction); | ||
68 | |||
69 | template <typename Left, typename Right> | ||
70 | void index(const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::Transaction &transaction) | ||
71 | { | ||
72 | index<typename Left::Type, typename Right::Type>(Left::name, Right::name, leftValue, rightValue, transaction); | ||
73 | } | ||
74 | |||
75 | template <typename LeftType, typename RightType> | ||
76 | void index(const QByteArray &leftName, const QByteArray &rightName, const QVariant &leftValue, const QVariant &rightValue, Sink::Storage::Transaction &transaction); | ||
77 | |||
41 | 78 | ||
42 | private: | 79 | private: |
43 | QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const; | 80 | QByteArray indexName(const QByteArray &property, const QByteArray &sortProperty = QByteArray()) const; |
44 | QByteArray mType; | 81 | QByteArray mType; |
45 | QByteArrayList mProperties; | 82 | QByteArrayList mProperties; |
46 | QMap<QByteArray, QByteArray> mSortedProperties; | 83 | QMap<QByteArray, QByteArray> mSortedProperties; |
84 | //<Property, ResultProperty> | ||
85 | QMap<QByteArray, QByteArray> mSecondaryProperties; | ||
47 | QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, Sink::Storage::Transaction &transaction)>> mIndexer; | 86 | QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, Sink::Storage::Transaction &transaction)>> mIndexer; |
48 | QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::Transaction &transaction)>> mSortIndexer; | 87 | QHash<QByteArray, std::function<void(const QByteArray &identifier, const QVariant &value, const QVariant &sortValue, Sink::Storage::Transaction &transaction)>> mSortIndexer; |
49 | }; | 88 | }; |