File indexing completed on 2024-05-12 05:26:02
0001 /* 0002 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Lesser General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2.1 of the License, or (at your option) version 3, or any 0008 * later version accepted by the membership of KDE e.V. (or its 0009 * successor approved by the membership of KDE e.V.), which shall 0010 * act as a proxy defined in Section 6 of version 3 of the license. 0011 * 0012 * This library is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 * Lesser General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU Lesser General Public 0018 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 0019 */ 0020 #pragma once 0021 0022 #include "sink_export.h" 0023 #include <QByteArrayList> 0024 #include <QHash> 0025 #include <QSet> 0026 #include "applicationdomaintype.h" 0027 0028 namespace Sink { 0029 0030 class SINK_EXPORT QueryBase 0031 { 0032 public: 0033 struct SINK_EXPORT Comparator { 0034 enum Comparators { 0035 Invalid, 0036 Equals, 0037 Contains, 0038 In, 0039 Within, 0040 Overlap, 0041 Fulltext 0042 }; 0043 0044 Comparator(); 0045 Comparator(const QVariant &v); 0046 Comparator(const QVariant &v, Comparators c); 0047 bool matches(const QVariant &v) const; 0048 bool operator==(const Comparator &other) const; 0049 0050 QVariant value; 0051 Comparators comparator; 0052 }; 0053 0054 class SINK_EXPORT Filter { 0055 public: 0056 QByteArrayList ids; 0057 QHash<QByteArrayList, Comparator> propertyFilter; 0058 bool operator==(const Filter &other) const; 0059 }; 0060 0061 QueryBase() = default; 0062 QueryBase(const QByteArray &type) : mType(type) {} 0063 0064 bool operator==(const QueryBase &other) const; 0065 0066 Comparator getFilter(const QByteArray &property) const 0067 { 0068 return mBaseFilterStage.propertyFilter.value({property}); 0069 } 0070 0071 Comparator getFilter(const QByteArrayList &properties) const 0072 { 0073 return mBaseFilterStage.propertyFilter.value(properties); 0074 } 0075 0076 template <class T> 0077 Comparator getFilter() const 0078 { 0079 return getFilter(T::name); 0080 } 0081 0082 template <class T1, class T2, class... Rest> 0083 Comparator getFilter() const 0084 { 0085 return getFilter({T1::name, T2::name, Rest::name...}); 0086 } 0087 0088 bool hasFilter(const QByteArray &property) const 0089 { 0090 return mBaseFilterStage.propertyFilter.contains({property}); 0091 } 0092 0093 template <class T> 0094 bool hasFilter() const 0095 { 0096 return hasFilter(T::name); 0097 } 0098 0099 void setId(const QByteArray &id) 0100 { 0101 mId = id; 0102 } 0103 0104 QByteArray id() const 0105 { 0106 return mId; 0107 } 0108 0109 void setBaseFilters(const QHash<QByteArrayList, Comparator> &filter) 0110 { 0111 mBaseFilterStage.propertyFilter = filter; 0112 } 0113 0114 void setFilter(const Filter &filter) 0115 { 0116 mBaseFilterStage = filter; 0117 } 0118 0119 QHash<QByteArrayList, Comparator> getBaseFilters() const 0120 { 0121 return mBaseFilterStage.propertyFilter; 0122 } 0123 0124 Filter getFilter() const 0125 { 0126 return mBaseFilterStage; 0127 } 0128 0129 QByteArrayList ids() const 0130 { 0131 return mBaseFilterStage.ids; 0132 } 0133 0134 void filter(const QByteArray &id) 0135 { 0136 mBaseFilterStage.ids << id; 0137 } 0138 0139 void filter(const QByteArrayList &ids) 0140 { 0141 mBaseFilterStage.ids << ids; 0142 } 0143 0144 void filter(const QByteArray &property, const QueryBase::Comparator &comparator) 0145 { 0146 mBaseFilterStage.propertyFilter.insert({property}, comparator); 0147 } 0148 0149 void filter(const QByteArrayList &properties, const QueryBase::Comparator &comparator) 0150 { 0151 mBaseFilterStage.propertyFilter.insert(properties, comparator); 0152 } 0153 0154 void setType(const QByteArray &type) 0155 { 0156 mType = type; 0157 } 0158 0159 template<typename T> 0160 void setType() 0161 { 0162 setType(ApplicationDomain::getTypeName<T>()); 0163 } 0164 0165 QByteArray type() const 0166 { 0167 return mType; 0168 } 0169 0170 void setSortProperty(const QByteArray &property) 0171 { 0172 mSortProperty = property; 0173 } 0174 0175 QByteArray sortProperty() const 0176 { 0177 return mSortProperty; 0178 } 0179 0180 class FilterStage { 0181 public: 0182 virtual ~FilterStage(){}; 0183 }; 0184 0185 QList<QSharedPointer<FilterStage>> getFilterStages() 0186 { 0187 return mFilterStages; 0188 } 0189 0190 0191 class Aggregator { 0192 public: 0193 enum Operation { 0194 Count, 0195 Collect 0196 }; 0197 0198 Aggregator(const QByteArray &p, Operation o, const QByteArray &c = QByteArray()) 0199 : resultProperty(p), 0200 operation(o), 0201 propertyToCollect(c) 0202 { 0203 } 0204 0205 QByteArray resultProperty; 0206 Operation operation; 0207 QByteArray propertyToCollect; 0208 }; 0209 0210 class Reduce : public FilterStage { 0211 public: 0212 0213 class Selector { 0214 public: 0215 enum Comparator { 0216 Min, //get the minimum value 0217 Max, //get the maximum value 0218 }; 0219 0220 template <typename SelectionProperty> 0221 static Selector max() 0222 { 0223 return Selector(SelectionProperty::name, Max); 0224 } 0225 0226 template <typename SelectionProperty> 0227 static Selector min() 0228 { 0229 return Selector(SelectionProperty::name, Min); 0230 } 0231 0232 Selector(const QByteArray &p, Comparator c) 0233 : property(p), 0234 comparator(c) 0235 { 0236 } 0237 0238 QByteArray property; 0239 Comparator comparator; 0240 }; 0241 0242 struct PropertySelector { 0243 QByteArray resultProperty; 0244 Selector selector; 0245 }; 0246 0247 Reduce(const QByteArray &p, const Selector &s) 0248 : property(p), 0249 selector(s) 0250 { 0251 } 0252 0253 Reduce &count(const QByteArray &resultProperty = "count") 0254 { 0255 aggregators << Aggregator(resultProperty, Aggregator::Count); 0256 return *this; 0257 } 0258 0259 /** 0260 * Collect all properties and make them available as a QList as the virtual properite with the name @param resultProperty 0261 */ 0262 template <typename T> 0263 Reduce &collect(const QByteArray &resultProperty) 0264 { 0265 aggregators << Aggregator(resultProperty, Aggregator::Collect, T::name); 0266 return *this; 0267 } 0268 0269 template <typename T> 0270 Reduce &collect() 0271 { 0272 return collect<T>(QByteArray{T::name} + QByteArray{"Collected"}); 0273 } 0274 0275 /** 0276 * Select a property and make it available as the virtual properite with the name @param resultProperty. 0277 * 0278 * This allows to make a different choice for this property than for the main selector of the reduction, 0279 * so we can e.g. select the subject of the first email sorted by date, while otherwise selecting the latest email. 0280 * 0281 * Please note that this will reuse the selection property of the main selector. 0282 */ 0283 template <typename T> 0284 Reduce &select(Selector::Comparator comparator, const QByteArray &resultProperty) 0285 { 0286 propertySelectors << PropertySelector{resultProperty, Selector{T::name, comparator}}; 0287 return *this; 0288 } 0289 0290 template <typename T> 0291 Reduce &select(Selector::Comparator comparator) 0292 { 0293 return select<T>(comparator, QByteArray{T::name} + QByteArray{"Selected"}); 0294 } 0295 0296 0297 //Reduce on property 0298 QByteArray property; 0299 Selector selector; 0300 QList<Aggregator> aggregators; 0301 QList<PropertySelector> propertySelectors; 0302 }; 0303 0304 Reduce &reduce(const QByteArray &name, const Reduce::Selector &s) 0305 { 0306 auto reduction = QSharedPointer<Reduce>::create(name, s); 0307 mFilterStages << reduction; 0308 return *reduction; 0309 } 0310 0311 template <typename T> 0312 Reduce &reduce(const Reduce::Selector &s) 0313 { 0314 return reduce(T::name, s); 0315 } 0316 0317 /** 0318 * For every entity resolve referenced entities and collect properties from them. 0319 */ 0320 class ReferenceResolver : public FilterStage { 0321 public: 0322 ReferenceResolver(const QByteArray &p) 0323 : referenceProperty(p) 0324 { 0325 } 0326 0327 template <typename T> 0328 ReferenceResolver &collect(const QByteArray &resultProperty) 0329 { 0330 aggregators << Aggregator(resultProperty, Aggregator::Collect, T::name); 0331 return *this; 0332 } 0333 0334 template <typename T> 0335 ReferenceResolver &collect() 0336 { 0337 return collect<T>(QByteArray{T::name} + QByteArray{"Collected"}); 0338 } 0339 0340 QByteArray referenceProperty; 0341 bool recursive = true; 0342 QList<Aggregator> aggregators; 0343 }; 0344 0345 ReferenceResolver &resolveReference(const QByteArray &property) 0346 { 0347 auto referenceResolver = QSharedPointer<ReferenceResolver>::create(property); 0348 mFilterStages << referenceResolver; 0349 return *referenceResolver; 0350 } 0351 0352 /** 0353 * "Bloom" on a property. 0354 * 0355 * For every encountered value of a property, 0356 * a result set is generated containing all entries with the same value. 0357 * 0358 * Example: 0359 * For an input set of one mail; return all emails with the same threadId. 0360 */ 0361 class Bloom : public FilterStage { 0362 public: 0363 //Property to bloom on 0364 QByteArray property; 0365 Bloom(const QByteArray &p) 0366 : property(p) 0367 { 0368 } 0369 }; 0370 0371 template <typename T> 0372 void bloom() 0373 { 0374 mFilterStages << QSharedPointer<Bloom>::create(T::name); 0375 } 0376 0377 int limit() const 0378 { 0379 return mLimit; 0380 } 0381 0382 qint64 baseRevision{0}; 0383 0384 typedef std::function<bool(const Sink::ApplicationDomain::ApplicationDomainType &)> FilterFunction; 0385 0386 void setPostQueryFilter(const FilterFunction &filter) { 0387 mPostQueryFilter = filter; 0388 } 0389 0390 FilterFunction getPostQueryFilter() const 0391 { 0392 return mPostQueryFilter; 0393 } 0394 0395 protected: 0396 int mLimit{0}; 0397 0398 private: 0399 FilterFunction mPostQueryFilter; 0400 Filter mBaseFilterStage; 0401 QList<QSharedPointer<FilterStage>> mFilterStages; 0402 QByteArray mType; 0403 QByteArray mSortProperty; 0404 QByteArray mId; 0405 }; 0406 0407 /** 0408 * A query that matches a set of entities. 0409 */ 0410 class SINK_EXPORT Query : public QueryBase 0411 { 0412 public: 0413 enum Flag 0414 { 0415 NoFlags = 0, 0416 /** Leave the query running and continuously update the result set. */ 0417 LiveQuery = 1, 0418 /** Run the query synchronously. */ 0419 SynchronousQuery = 2, 0420 /** Include status updates via notifications */ 0421 UpdateStatus = 4 0422 }; 0423 Q_DECLARE_FLAGS(Flags, Flag) 0424 0425 template <typename T> 0426 Query &request() 0427 { 0428 requestedProperties << T::name; 0429 return *this; 0430 } 0431 0432 template <typename T> 0433 Query &requestTree() 0434 { 0435 mParentProperty = T::name; 0436 return *this; 0437 } 0438 0439 Query &requestTree(const QByteArray &parentProperty) 0440 { 0441 mParentProperty = parentProperty; 0442 return *this; 0443 } 0444 0445 QByteArray parentProperty() const 0446 { 0447 return mParentProperty; 0448 } 0449 0450 template <typename T> 0451 Query &sort() 0452 { 0453 setSortProperty(T::name); 0454 return *this; 0455 } 0456 0457 template <typename T> 0458 Query &filter(const typename T::Type &value) 0459 { 0460 filter(T::name, QVariant::fromValue(value)); 0461 return *this; 0462 } 0463 0464 template <typename T> 0465 Query &containsFilter(const QByteArray &value) 0466 { 0467 static_assert(std::is_same<typename T::Type, QByteArrayList>::value, "The contains filter is only implemented for QByteArray in QByteArrayList"); 0468 QueryBase::filter(T::name, QueryBase::Comparator(QVariant::fromValue(value), QueryBase::Comparator::Contains)); 0469 return *this; 0470 } 0471 0472 template <typename T> 0473 Query &filter(const QueryBase::Comparator &comparator) 0474 { 0475 QueryBase::filter(T::name, comparator); 0476 return *this; 0477 } 0478 0479 template <typename T1, typename T2, typename... Rest> 0480 Query &filter(const QueryBase::Comparator &comparator) 0481 { 0482 QueryBase::filter({T1::name, T2::name, Rest::name...}, comparator); 0483 return *this; 0484 } 0485 0486 Query &filter(const QByteArray &id) 0487 { 0488 QueryBase::filter(id); 0489 return *this; 0490 } 0491 0492 Query &filter(const QByteArrayList &ids) 0493 { 0494 QueryBase::filter(ids); 0495 return *this; 0496 } 0497 0498 Query &filter(const QByteArray &property, const QueryBase::Comparator &comparator) 0499 { 0500 QueryBase::filter(property, comparator); 0501 return *this; 0502 } 0503 0504 template <typename T> 0505 Query &filter(const ApplicationDomain::Entity &value) 0506 { 0507 filter(T::name, QVariant::fromValue(ApplicationDomain::Reference{value.identifier()})); 0508 return *this; 0509 } 0510 0511 template <typename T> 0512 Query &filter(const Query &query) 0513 { 0514 auto q = query; 0515 q.setType(ApplicationDomain::getTypeName<typename T::ReferenceType>()); 0516 filter(T::name, QVariant::fromValue(q)); 0517 return *this; 0518 } 0519 0520 0521 Query(const ApplicationDomain::Entity &value) 0522 { 0523 filter(value.identifier()); 0524 resourceFilter(value.resourceInstanceIdentifier()); 0525 } 0526 0527 Query(Flags flags = Flags()) : mFlags(flags) 0528 { 0529 } 0530 0531 QByteArrayList requestedProperties; 0532 0533 int limit() const 0534 { 0535 return mLimit; 0536 } 0537 0538 Query &limit(int l) 0539 { 0540 mLimit = l; 0541 return *this; 0542 } 0543 0544 void setFlags(Flags flags) 0545 { 0546 mFlags = flags; 0547 } 0548 0549 Flags flags() const 0550 { 0551 return mFlags; 0552 } 0553 0554 bool liveQuery() const 0555 { 0556 return mFlags.testFlag(LiveQuery); 0557 } 0558 0559 bool synchronousQuery() const 0560 { 0561 return mFlags.testFlag(SynchronousQuery); 0562 } 0563 0564 Filter getResourceFilter() const 0565 { 0566 return mResourceFilter; 0567 } 0568 0569 Query &resourceFilter(const QByteArray &id) 0570 { 0571 mResourceFilter.ids << id; 0572 return *this; 0573 } 0574 0575 template <typename T> 0576 Query &resourceFilter(const ApplicationDomain::ApplicationDomainType &entity) 0577 { 0578 mResourceFilter.propertyFilter.insert({T::name}, Comparator(entity.identifier())); 0579 return *this; 0580 } 0581 0582 Query &resourceFilter(const QByteArray &name, const Comparator &comparator) 0583 { 0584 mResourceFilter.propertyFilter.insert({name}, comparator); 0585 return *this; 0586 } 0587 0588 template <typename T> 0589 Query &resourceContainsFilter(const QVariant &value) 0590 { 0591 return resourceFilter(T::name, Comparator(value, Comparator::Contains)); 0592 } 0593 0594 template <typename T> 0595 Query &resourceFilter(const QVariant &value) 0596 { 0597 return resourceFilter(T::name, value); 0598 } 0599 0600 bool operator==(const Query &other) const; 0601 0602 private: 0603 friend class SyncScope; 0604 Flags mFlags; 0605 Filter mResourceFilter; 0606 QByteArray mParentProperty; 0607 }; 0608 0609 class SyncScope : public QueryBase { 0610 public: 0611 using QueryBase::QueryBase; 0612 0613 SyncScope() = default; 0614 0615 SyncScope(const Query &other) 0616 : QueryBase(other), 0617 mResourceFilter(other.mResourceFilter) 0618 { 0619 0620 } 0621 0622 template <typename T> 0623 SyncScope(const T &o) 0624 : QueryBase() 0625 { 0626 resourceFilter(o.resourceInstanceIdentifier()); 0627 filter(o.identifier()); 0628 setType(ApplicationDomain::getTypeName<T>()); 0629 } 0630 0631 Query::Filter getResourceFilter() const 0632 { 0633 return mResourceFilter; 0634 } 0635 0636 SyncScope &resourceFilter(const QByteArray &id) 0637 { 0638 mResourceFilter.ids << id; 0639 return *this; 0640 } 0641 0642 template <typename T> 0643 SyncScope &resourceFilter(const ApplicationDomain::ApplicationDomainType &entity) 0644 { 0645 mResourceFilter.propertyFilter.insert({T::name}, Comparator(entity.identifier())); 0646 return *this; 0647 } 0648 0649 SyncScope &resourceFilter(const QByteArray &name, const Comparator &comparator) 0650 { 0651 mResourceFilter.propertyFilter.insert({name}, comparator); 0652 return *this; 0653 } 0654 0655 template <typename T> 0656 SyncScope &resourceContainsFilter(const QVariant &value) 0657 { 0658 return resourceFilter(T::name, Comparator(value, Comparator::Contains)); 0659 } 0660 0661 template <typename T> 0662 SyncScope &resourceFilter(const QVariant &value) 0663 { 0664 return resourceFilter(T::name, value); 0665 } 0666 0667 template <typename T> 0668 SyncScope &filter(const Query::Comparator &comparator) 0669 { 0670 return filter(T::name, comparator); 0671 } 0672 0673 SyncScope &filter(const QByteArray &id) 0674 { 0675 QueryBase::filter(id); 0676 return *this; 0677 } 0678 0679 SyncScope &filter(const QByteArrayList &ids) 0680 { 0681 QueryBase::filter(ids); 0682 return *this; 0683 } 0684 0685 SyncScope &filter(const QByteArray &property, const Query::Comparator &comparator) 0686 { 0687 QueryBase::filter(property, comparator); 0688 return *this; 0689 } 0690 0691 private: 0692 Query::Filter mResourceFilter; 0693 }; 0694 0695 } 0696 0697 SINK_EXPORT QDebug operator<<(QDebug dbg, const Sink::QueryBase::Comparator &); 0698 SINK_EXPORT QDebug operator<<(QDebug dbg, const Sink::QueryBase &); 0699 SINK_EXPORT QDebug operator<<(QDebug dbg, const Sink::Query &); 0700 SINK_EXPORT QDataStream &operator<< (QDataStream &stream, const Sink::QueryBase &query); 0701 SINK_EXPORT QDataStream &operator>> (QDataStream &stream, Sink::QueryBase &query); 0702 0703 Q_DECLARE_OPERATORS_FOR_FLAGS(Sink::Query::Flags) 0704 Q_DECLARE_METATYPE(Sink::QueryBase); 0705 Q_DECLARE_METATYPE(Sink::Query); 0706 Q_DECLARE_METATYPE(Sink::SyncScope);