swift
datastoreobjectlist.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #ifndef SWIFT_MISC_DB_DATABASEOBJECTLIST_H
7 #define SWIFT_MISC_DB_DATABASEOBJECTLIST_H
8 
9 #include <QJsonArray>
10 #include <QMap>
11 #include <QSet>
12 #include <QString>
13 
14 #include "misc/db/datastore.h"
15 #include "misc/jsonexception.h"
16 #include "misc/mapbuilder.h"
17 #include "misc/setbuilder.h"
19 
20 namespace swift::misc::db
21 {
25  template <class OBJ, class CONTAINER, typename KEYTYPE>
26  class IDatastoreObjectList : public ITimestampObjectList<OBJ, CONTAINER>
27  {
28  static constexpr bool hasIntegerKey =
29  std::is_base_of_v<IDatastoreObjectWithIntegerKey, OBJ> && std::is_same_v<int, KEYTYPE>;
30  static constexpr bool hasStringKey =
31  std::is_base_of_v<IDatastoreObjectWithStringKey, OBJ> && std::is_base_of_v<QString, KEYTYPE>;
32  static_assert(hasIntegerKey || hasStringKey,
33  "ObjectType needs to implement IDatastoreObjectWithXXXXKey and have appropriate KeyType");
34 
35  public:
37  OBJ findByKey(KEYTYPE key, const OBJ &notFound = OBJ()) const
38  {
39  return this->container().findFirstByOrDefault(&OBJ::getDbKey, key, notFound);
40  }
41 
43  CONTAINER findByKeys(const QSet<KEYTYPE> &keys) const
44  {
45  CONTAINER objects;
46  if (keys.isEmpty()) { return objects; }
48  {
49  if (!keys.contains(obj.getDbKey())) { continue; }
50  objects.push_back(obj);
51  }
52  return objects;
53  }
54 
56  CONTAINER findObjectsWithDbKey() const
57  {
58  CONTAINER objects;
60  {
61  if (!obj.hasValidDbKey()) { continue; }
62  objects.push_back(obj);
63  }
64  return objects;
65  }
66 
68  CONTAINER findObjectsWithoutDbKey() const
69  {
70  CONTAINER objects;
72  {
73  if (obj.hasValidDbKey()) { continue; }
74  objects.push_back(obj);
75  }
76  return objects;
77  }
78 
80  OBJ maxKeyObject() const
81  {
82  if (this->container().isEmpty()) { return OBJ(); }
83  const OBJ max = *std::max_element(this->container().begin(), this->container().end(),
84  [](const OBJ &obj1, const OBJ &obj2) {
85  bool v1 = obj1.hasValidDbKey();
86  bool v2 = obj2.hasValidDbKey();
87  if (v1 && v2) { return obj1.getDbKey() < obj2.getDbKey(); }
88  return v2;
89  });
90  return max;
91  }
92 
94  void sortByKey() { this->container().sort(swift::misc::predicates::MemberLess(&OBJ::getDbKey)); }
95 
98  {
100  for (const OBJ &obj : ITimestampObjectList<OBJ, CONTAINER>::container())
101  {
102  if (!obj.hasValidDbKey()) { continue; }
103  keys.insert(obj.getDbKey());
104  }
105  return keys;
106  }
107 
110  {
112  for (const OBJ &obj : ITimestampObjectList<OBJ, CONTAINER>::container())
113  {
114  if (!obj.hasValidDbKey()) { continue; }
115  map.insert(obj.getDbKey(), obj);
116  }
117  return map;
118  }
119 
122  {
124  for (const OBJ &obj : ITimestampObjectList<OBJ, CONTAINER>::container())
125  {
126  if (!obj.hasValidDbKey()) { continue; }
127  keys.insert(obj.getDbKeyAsString());
128  }
129  return keys;
130  }
131 
133  QString dbKeysAsString(const QString &separator) const
134  {
135  if (ITimestampObjectList<OBJ, CONTAINER>::container().isEmpty()) { return {}; }
137  QString s;
138  for (const QString &k : keys)
139  {
140  if (s.isEmpty()) { s += k; }
141  else { s += separator + k; }
142  }
143  return s;
144  }
145 
147  KEYTYPE getMaxKey(bool *ok = nullptr) const
148  {
149  QSet<KEYTYPE> keys(this->toDbKeySet());
150  if (keys.isEmpty())
151  {
152  if (ok) { *ok = false; }
153  return KEYTYPE();
154  }
155  KEYTYPE max = *std::max_element(keys.begin(), keys.end());
156  if (ok) { *ok = true; }
157  return max;
158  }
159 
162  {
163  if (keys.isEmpty()) { return 0; }
164  if (this->container().isEmpty()) { return 0; }
165  CONTAINER newValues;
166  for (const OBJ &obj : ITimestampObjectList<OBJ, CONTAINER>::container())
167  {
168  if (keys.contains(obj.getDbKey())) { continue; }
169  newValues.push_back(obj);
170  }
171  const int delta = this->container().size() - newValues.size();
172  if (delta > 0) { this->container() = newValues; }
173  return delta;
174  }
175 
178  {
179  if (this->container().isEmpty()) { return 0; }
180  CONTAINER newValues;
181  for (const OBJ &obj : ITimestampObjectList<OBJ, CONTAINER>::container())
182  {
183  if (!obj.hasValidDbKey()) { continue; }
184  newValues.push_back(obj);
185  }
186  int delta = this->container().size() - newValues.size();
187  this->container() = newValues;
188  return delta;
189  }
190 
192  int replaceOrAddObjectsByKey(const CONTAINER &container)
193  {
194  if (container.isEmpty()) { return 0; }
195  if (this->container().isEmpty())
196  {
197  this->container() = container;
198  return this->container().size();
199  }
200  CONTAINER newValues(this->container());
201  const QSet<KEYTYPE> keys(container.toDbKeySet());
202  newValues.removeObjectsWithKeys(keys);
203  int removeSize = newValues.size(); // size after removing data
204  newValues.push_back(container);
205  this->container() = newValues;
206  return newValues.size() - removeSize;
207  }
208 
211  {
212  CONTAINER objs(this->container());
213  objs.removeObjectsWithoutDbKey();
214  if (objs.isEmpty()) { return {}; }
215  return objs.latestTimestamp();
216  }
217 
220  {
221  CONTAINER objs(this->container());
222  objs.removeObjectsWithoutDbKey();
223  if (objs.isEmpty()) { return {}; }
224  return objs.oldestDbTimestamp();
225  }
226 
228  int countWithValidDbKey(bool withKey) const
229  {
230  int count = 0;
231  for (const OBJ &obj : ITimestampObjectList<OBJ, CONTAINER>::container())
232  {
233  if (obj.hasValidDbKey() && withKey) { count++; }
234  }
235  return count;
236  }
237 
239  int countWithValidDbKey() const { return this->countWithValidDbKey(true); }
240 
243  {
244  return std::any_of(ITimestampObjectList<OBJ, CONTAINER>::container().cbegin(),
246  [](const OBJ &obj) { return !obj.hasValidDbKey(); });
247  }
248 
250  bool containsDbKey(KEYTYPE key) const
251  {
252  return std::any_of(ITimestampObjectList<OBJ, CONTAINER>::container().cbegin(),
254  [&](const OBJ &obj) { return obj.hasValidDbKey() && obj.getDbKey() == key; });
255  }
256 
259  static CONTAINER fromMultipleJsonFormats(const QJsonObject &jsonObject)
260  {
261  // also accept cache format
262  if (jsonObject.isEmpty())
263  {
264  const CONTAINER c;
265  return c;
266  }
267 
268  // cache or settings format?
269  if (json::looksLikeSwiftDataObjectJson(jsonObject))
270  {
271  const QJsonObject cacheObj = json::swiftDataObjectValue(jsonObject);
272  CONTAINER container;
273  private_ns::CValueObjectMetaInfoHelper::convertFromMemoizedJson(
274  cacheObj, container, true, 0); // handles both, memoized or "normal" convertFromJson
275  return container;
276  }
277 
278  // plain vanilla container, does not match memoized objects
279  if (json::looksLikeSwiftContainerJson(jsonObject))
280  {
281  CONTAINER container;
282  container.convertFromJson(jsonObject);
283  return container;
284  }
285 
286  // still as type/value pair
287  if (json::looksLikeSwiftTypeValuePairJson(jsonObject))
288  {
289  const QJsonObject valueObject = jsonObject.value("value").toObject();
290  CONTAINER container;
291  private_ns::CValueObjectMetaInfoHelper::convertFromMemoizedJson(
292  valueObject, container, true, 0); // handles both, memoized or "normal" convertFromJson
293  return container;
294  }
295 
296  // DB format, as array
297  if (jsonObject.contains("data"))
298  {
299  return IDatastoreObjectList::fromDatabaseJson(jsonObject.value("data").toArray());
300  }
301 
302  // no idea what this is
303  throw CJsonException("Unsupported JSON format");
304  }
305 
308  static CONTAINER fromMultipleJsonFormats(const QString &jsonString)
309  {
310  // also accept cache format
311  if (jsonString.isEmpty())
312  {
313  const CONTAINER c;
314  return c;
315  }
316 
317  const QJsonObject jo = json::jsonObjectFromString(jsonString, false);
319  }
320 
323  static CONTAINER fromDatabaseJson(const QJsonArray &array)
324  {
325  CONTAINER container;
326  for (const QJsonValue &value : array) { container.push_back(OBJ::fromDatabaseJson(value.toObject())); }
327  return container;
328  }
329 
332  static CONTAINER fromDatabaseJson(const QJsonArray &array, const QString &prefix)
333  {
334  CONTAINER container;
335  for (const QJsonValue &value : array)
336  {
337  container.push_back(OBJ::fromDatabaseJson(value.toObject(), prefix));
338  }
339  return container;
340  }
341 
342  protected:
344  IDatastoreObjectList() = default;
345  };
346 } // namespace swift::misc::db
347 
348 #endif // SWIFT_MISC_DB_DATABASEOBJECTLIST_H
Thrown when a convertFromJson method encounters an unrecoverable error in JSON data.
Definition: jsonexception.h:24
Build a QMap more efficiently when calling insert() in a for loop.
Definition: mapbuilder.h:26
Build a QSet more efficiently when calling insert() in a for loop.
Definition: setbuilder.h:25
void insert(const T &value)
Add an element to the set. Runs in amortized constant time.
Definition: setbuilder.h:29
List of objects with timestamp. Such objects should implement.
const CONTAINER & container() const
Container.
List of objects read from database. Such objects should implement.
int replaceOrAddObjectsByKey(const CONTAINER &container)
Update or insert data (based on DB key)
QDateTime oldestDbTimestamp() const
Latest DB timestamp (means objects with DB key)
QSet< QString > toDbKeyStringSet() const
All keys as string set (also integer keys will be converted to string)
int removeObjectsWithKeys(const QSet< KEYTYPE > &keys)
Remove objects with keys.
int removeObjectsWithoutDbKey()
Remove objects without key.
OBJ findByKey(KEYTYPE key, const OBJ &notFound=OBJ()) const
Object with key, notFound otherwise.
static CONTAINER fromDatabaseJson(const QJsonArray &array, const QString &prefix)
From DB JSON.
OBJ maxKeyObject() const
Object with max.key.
KEYTYPE getMaxKey(bool *ok=nullptr) const
Max.key value (making sense with integer key)
QDateTime latestDbTimestamp() const
Latest DB timestamp (means objects with DB key)
QMap< KEYTYPE, OBJ > toDbKeyValueMap() const
As map with DB key/object.
static CONTAINER fromDatabaseJson(const QJsonArray &array)
From DB JSON with default prefixes.
QSet< KEYTYPE > toDbKeySet() const
All keys as set.
QString dbKeysAsString(const QString &separator) const
The DB keys as string.
IDatastoreObjectList()=default
Constructor.
static CONTAINER fromMultipleJsonFormats(const QJsonObject &jsonObject)
From multiple JSON formats.
int countWithValidDbKey() const
Number of entries with valid DB key.
CONTAINER findObjectsWithoutDbKey() const
Objects without DB key.
CONTAINER findByKeys(const QSet< KEYTYPE > &keys) const
Object with key, notFound otherwise.
static CONTAINER fromMultipleJsonFormats(const QString &jsonString)
From multiple JSON formats.
bool containsAnyObjectWithoutKey() const
Any object without key?
int countWithValidDbKey(bool withKey) const
Number of objects with/without key.
bool containsDbKey(KEYTYPE key) const
Contains object with key?
CONTAINER findObjectsWithDbKey() const
Objects with DB key.
QJsonObject jsonObjectFromString(const QString &json, bool acceptCacheFormat)
JSON Object from string.
Definition: json.cpp:413
auto MemberLess(Ts... vs)
Predicate which compares the return values of some member functions of two objects.
Definition: predicates.h:43
T::const_iterator begin(const LockFreeReader< T > &reader)
Non-member begin() and end() for so LockFree containers can be used in ranged for loops.
Definition: lockfree.h:332
T::const_iterator end(const LockFreeReader< T > &reader)
Non-member begin() and end() for so LockFree containers can be used in ranged for loops.
Definition: lockfree.h:338
bool contains(QLatin1StringView key) const const
bool isEmpty() const const
QJsonValue value(QLatin1StringView key) const const
QJsonArray toArray() const const
QJsonObject toObject() const const
QSet< T >::iterator begin()
bool contains(const QSet< T > &other) const const
QSet< T >::iterator end()
bool isEmpty() const const
bool isEmpty() const const