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);