Realm C++ SDK Version v2.0.0

results.hpp

1
2//
3// Copyright 2022 Realm Inc.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16//
18
19#ifndef CPPREALM_RESULTS_HPP
20#define CPPREALM_RESULTS_HPP
21
22#include <cpprealm/internal/bridge/mixed.hpp>
23#include <cpprealm/internal/bridge/query.hpp>
24#include <cpprealm/internal/bridge/results.hpp>
25#include <cpprealm/internal/bridge/table.hpp>
26#include <cpprealm/macros.hpp>
27#include <cpprealm/notifications.hpp>
28#include <cpprealm/schema.hpp>
29#include <cpprealm/rbool.hpp>
30
31namespace realm {
32 struct mutable_sync_subscription_set;
33}
34
35namespace realm {
36
37 using sort_descriptor = internal::bridge::sort_descriptor;
38
39 template<typename>
40 struct results;
41 template<typename T, typename Derived, typename ShouldEnable = void>
43 template<typename T, typename Derived>
45
46 template<typename T, typename Derived>
49 : m_parent(parent) {
50 }
52 Derived *collection;
53 std::vector<uint64_t> deletions;
54 std::vector<uint64_t> insertions;
55 std::vector<uint64_t> modifications;
56
57 // This flag indicates whether the underlying object which is the source of this
58 // collection was deleted. This applies to lists, dictionaries and sets.
59 // This enables notifiers to report a change on empty collections that have been deleted.
60 bool collection_root_was_deleted = false;
61
62 [[nodiscard]] bool empty() const noexcept {
63 return deletions.empty() && insertions.empty() && modifications.empty() &&
64 !collection_root_was_deleted;
65 }
66 };
67
68 size_t size() {
69 return m_parent.size();
70 }
71
72 virtual ~results_common_base() = default;
73 Derived where(const std::string &query, const std::vector<realm::mixed>& arguments) {
74 std::vector<internal::bridge::mixed> mixed_args;
75 for(auto& a : arguments)
76 mixed_args.push_back(serialize(a));
77 return Derived(internal::bridge::results(m_parent.get_realm(),
78 m_parent.get_table().query(query, std::move(mixed_args))));
79 }
80
81 Derived where(std::function<rbool(managed<T>&)>&& fn) {
82 static_assert(sizeof(managed<T>), "Must declare schema for T");
83 auto realm = m_parent.get_realm();
84 auto schema = realm.schema().find(managed<T>::schema.name);
85 auto group = realm.read_group();
86 auto table_ref = group.get_table(schema.table_key());
87 rbool query = rbool(internal::bridge::query(table_ref));
88 auto query_object = managed<T>::prepare_for_query(realm, &query);
89 auto full_query = fn(query_object).q;
90 return Derived(internal::bridge::results(m_parent.get_realm(), full_query));
91 }
92
94 explicit results_callback_wrapper(std::function<void(results_change)>&& fn,
95 Derived *c,
96 bool ignore_initial_notification = true)
97 : m_handler(std::move(fn)),
98 collection(c),
99 m_ignore_changes_in_initial_notification(ignore_initial_notification) {}
100
101 void before(const realm::internal::bridge::collection_change_set&) override {}
102
103 void after(internal::bridge::collection_change_set const &changes) final {
104 if (m_ignore_changes_in_initial_notification) {
105 m_ignore_changes_in_initial_notification = false;
106 m_handler({collection, {}, {}, {}});
107 } else if (changes.empty()) {
108 m_handler({collection, {}, {}, {}});
109 } else if (!changes.collection_root_was_deleted() || !changes.deletions().empty()) {
110 m_handler({
111 collection,
112 to_vector(changes.deletions()),
113 to_vector(changes.insertions()),
114 to_vector(changes.modifications()),
115 });
116 }
117 }
118
119 Derived *collection;
120
121 private:
122 std::function<void(results_change)> m_handler;
123 bool m_ignore_changes_in_initial_notification;
124 std::vector<uint64_t> to_vector(const internal::bridge::index_set &index_set) {
125 auto vector = std::vector<uint64_t>();
126 for (auto index: index_set.as_indexes()) {
127 vector.push_back(index);
128 }
129 return vector;
130 };
131 };
132
133 realm::notification_token observe(std::function<void(results_change)>&& handler) {
134 auto r = std::make_shared<internal::bridge::results>(m_parent.get_realm(), m_parent.get_realm().table_for_object_type(managed<T>::schema.name));
135 realm::notification_token token = r->add_notification_callback(std::make_shared<results_callback_wrapper>(std::move(handler), static_cast<Derived*>(this)));
136 token.m_realm = r->get_realm();
137 token.m_results = r;
138 return token;
139 }
140
141 Derived freeze() {
142 auto frozen_realm = m_parent.get_realm().freeze();
143 return Derived(internal::bridge::results(frozen_realm, frozen_realm.table_for_object_type(managed<T>::schema.name)));
144 }
145
146 Derived thaw() {
147 auto thawed_realm = m_parent.get_realm().thaw();
148 return Derived(internal::bridge::results(thawed_realm, thawed_realm.table_for_object_type(managed<T>::schema.name)));
149 }
150
151 bool is_frozen() {
152 return m_parent.get_realm().is_frozen();
153 }
154
155 Derived sort(const std::string& key_path, bool ascending) {
156 return Derived(m_parent.sort({{key_path, ascending}}));
157 }
158
159 Derived sort(const std::vector<sort_descriptor>& sort_descriptors) {
160 return Derived(m_parent.sort(sort_descriptors));
161 }
162
163 protected:
164 internal::bridge::results m_parent;
165 template <auto> friend struct linking_objects;
166 };
167
168 template<typename T>
169 using results_is_primitive = std::enable_if_t<!managed<T>::is_object && !std::is_enum_v<T> && !internal::type_info::is_variant_t<T>::value>;
170 template<typename T>
171 using results_is_enum = std::enable_if_t<!managed<T>::is_object && std::is_enum_v<T> && !internal::type_info::is_variant_t<T>::value>;
172 template<typename T>
173 using results_is_mixed = std::enable_if_t<!managed<T>::is_object && !std::is_enum_v<T> && internal::type_info::is_variant_t<T>::value>;
174
175 template<typename T, typename Derived>
176 struct results_base<T, Derived, results_is_primitive<T>> : public results_common_base<T, Derived> {
177 explicit results_base(internal::bridge::results &&parent)
178 : results_common_base<T, Derived>(std::move(parent)) {
179 }
180
181 T operator[](size_t index) {
182 if (index >= this->m_parent.size())
183 throw std::out_of_range("Index out of range.");
184 return internal::bridge::get<T>(this->m_parent, index);
185 }
186
187 class iterator {
188 public:
189 using difference_type = size_t;
190 using value_type = T;
191 using iterator_category = std::input_iterator_tag;
192
193 bool operator!=(const iterator &other) const {
194 return !(*this == other);
195 }
196
197 bool operator==(const iterator &other) const {
198 return (m_parent == other.m_parent) && (m_idx == other.m_idx);
199 }
200
201 value_type operator*() noexcept {
202 return m_parent->operator[](m_idx);
203 }
204
205 iterator &operator++() {
206 m_idx++;
207 return *this;
208 }
209
210 iterator operator++(int i) {
211 m_idx += i;
212 return *this;
213 }
214
215 explicit iterator(size_t idx, Derived *parent)
216 : m_idx(idx), m_parent(parent) {
217 }
218 private:
219 size_t m_idx;
220 Derived *m_parent;
221 };
222
223 iterator begin() {
224 return iterator(0, static_cast<Derived*>(this));
225 }
226
227 iterator end() {
228 return iterator(this->m_parent.size(), static_cast<Derived*>(this));
229 }
230 };
231
232 template<typename T, typename Derived>
233 struct results_base<T, Derived, results_is_mixed<T>> : public results_common_base<T, Derived> {
234 explicit results_base(internal::bridge::results &&parent)
235 : results_common_base<T, Derived>(std::move(parent)) {
236 }
237
238 T operator[](size_t index) {
239 if (index >= this->m_parent.size())
240 throw std::out_of_range("Index out of range.");
241 return deserialize<T>(internal::bridge::get<internal::bridge::mixed>(this->m_parent, index));
242 }
243
244 // TODO: The current impl of realm::mixed does not allow managed object types,
245 // to be accessed from the iterator as it would be required to be wrapped in a
246 // managed<> template. As these templates require on a col & obj key as they act as managed
247 // properties on an object this use case is broken. Ideally we should replace realm::mixed to
248 // not be a std::variant, but rather be a type-safe union we define in the SDK so that
249 // realm::mixed could have a managed context itself.
250 class iterator {
251 public:
252 using difference_type = size_t;
253 using value_type = T;
254 using iterator_category = std::input_iterator_tag;
255
256 bool operator!=(const iterator &other) const {
257 return !(*this == other);
258 }
259
260 bool operator==(const iterator &other) const {
261 return (m_parent == other.m_parent) && (m_idx == other.m_idx);
262 }
263
264 value_type operator*() noexcept {
265 return m_parent->operator[](m_idx);
266 }
267
268 iterator &operator++() {
269 m_idx++;
270 return *this;
271 }
272
273 iterator operator++(int i) {
274 m_idx += i;
275 return *this;
276 }
277
278 explicit iterator(size_t idx, Derived *parent)
279 : m_idx(idx), m_parent(parent) {
280 }
281 private:
282 size_t m_idx;
283 Derived *m_parent;
284 };
285
286 iterator begin() {
287 return iterator(0, static_cast<Derived*>(this));
288 }
289
290 iterator end() {
291 return iterator(this->m_parent.size(), static_cast<Derived*>(this));
292 }
293 };
294
295 template<typename T, typename Derived>
296 struct results_base<T, Derived, results_is_enum<T>> : public results_common_base<T, Derived> {
297 explicit results_base(internal::bridge::results &&parent)
298 : results_common_base<T, Derived>(std::move(parent)) {
299 }
300
301 T operator[](size_t index) {
302 if (index >= this->m_parent.size())
303 throw std::out_of_range("Index out of range.");
304 return static_cast<T>(internal::bridge::get<int64_t>(this->m_parent, index));
305 }
306
307 class iterator {
308 public:
309 using difference_type = size_t;
310 using value_type = T;
311 using iterator_category = std::input_iterator_tag;
312
313 bool operator!=(const iterator &other) const {
314 return !(*this == other);
315 }
316
317 bool operator==(const iterator &other) const {
318 return (m_parent == other.m_parent) && (m_idx == other.m_idx);
319 }
320
321 value_type operator*() noexcept {
322 return m_parent->operator[](m_idx);
323 }
324
325 iterator &operator++() {
326 m_idx++;
327 return *this;
328 }
329
330 iterator operator++(int i) {
331 m_idx += i;
332 return *this;
333 }
334
335 explicit iterator(size_t idx, Derived *parent)
336 : m_idx(idx), m_parent(parent) {
337 }
338 private:
339 size_t m_idx;
340 Derived *m_parent;
341 };
342
343 iterator begin() {
344 return iterator(0, static_cast<Derived*>(this));
345 }
346
347 iterator end() {
348 return iterator(this->m_parent.size(), static_cast<Derived*>(this));
349 }
350 };
351
352 template<typename T, typename Derived>
353 struct results_base<T, Derived, std::enable_if_t<managed<T>::is_object>> : public results_common_base<T, Derived> {
354 explicit results_base(internal::bridge::results &&parent)
355 : results_common_base<T, Derived>(std::move(parent)) {
356 }
357
358 managed<T, void> operator[](size_t index) {
359 if (index >= this->m_parent.size())
360 throw std::out_of_range("Index out of range.");
361 return managed<T, void>(internal::bridge::get<internal::bridge::obj>(this->m_parent, index), this->m_parent.get_realm());
362 }
363
364 class iterator {
365 public:
366 using difference_type = size_t;
368 using iterator_category = std::input_iterator_tag;
369
370 bool operator!=(const iterator &other) const {
371 return !(*this == other);
372 }
373
374 bool operator==(const iterator &other) const {
375 return (m_parent == other.m_parent) && (m_idx == other.m_idx);
376 }
377
378 value_type operator*() noexcept {
379 internal::bridge::obj obj = internal::bridge::get<internal::bridge::obj>(m_parent->m_parent, m_idx);
380 return managed<T, void>(std::move(obj), this->m_parent->m_parent.get_realm());
381 }
382
383 iterator &operator++() {
384 m_idx++;
385 return *this;
386 }
387
388 iterator operator++(int i) {
389 m_idx += i;
390 return *this;
391 }
392
393 explicit iterator(size_t idx, Derived *parent)
394 : m_idx(idx), m_parent(parent) {
395 }
396 private:
397 size_t m_idx;
398 Derived *m_parent;
399 };
400
401 iterator begin() {
402 return iterator(0, static_cast<Derived*>(this));
403 }
404
405 iterator end() {
406 return iterator(this->m_parent.size(), static_cast<Derived*>(this));
407 }
408 };
409
410 template<typename T>
411 struct results : public results_base<T, results<T>> {
412 using value_type = T;
413 explicit results(internal::bridge::results &&parent)
414 : results_base<T, results<T>>(std::move(parent)) {
415 }
416 };
417
418 template <auto ptr>
420 static inline auto Ptr = ptr;
422
423 static_assert(sizeof(managed<typename internal::ptr_type_extractor<ptr>::class_type>), "Type is not managed by the Realm");
424 };
425
426 template <auto ptr> struct managed<linking_objects<ptr>> : managed_base {
427 using iterator = typename results<typename internal::ptr_type_extractor<ptr>::class_type>::iterator;
429
430 linking_objects<ptr> detach() const {
431 return {};
432 }
433
434 iterator begin() {
435 return iterator(0, get_results());
436 }
437
438 iterator end() {
439 auto r = get_results();
440 return iterator(r.size(), r);
441 }
442
443 size_t size() {
444 return get_results().size();
445 }
446 managed<Class> operator[](size_t idx) {
447 return get_results()[idx];
448 }
449
450 private:
451 results<Class> get_results() {
452 auto table = m_obj->get_table();
453 if (!table.is_valid(m_obj->get_key())) {
454 throw std::logic_error("Object has been deleted or invalidated.");
455 }
456
457 internal::bridge::obj* obj = m_obj;
458 auto schema = m_realm->schema().find(managed<Class>::schema.name);
459 auto linking_property = schema.property_for_name(managed<Class>::schema.template name_for_property<ptr>());
460 if (!linking_property.column_key()) {
461 throw std::logic_error("Invalid column key for origin property.");
462 }
463
464 internal::bridge::results results(*m_realm, obj->get_backlink_view(m_realm->get_table(schema.table_key()), linking_property.column_key()));
465 return ::realm::results<Class>(std::move(results));
466 }
467 };
468}
469
470
471#endif //CPPREALM_RESULTS_HPP
Definition: object.hpp:63
Definition: obj.hpp:123
Definition: results.hpp:46
Definition: schema.hpp:51
Definition: results.hpp:419
Definition: macros.hpp:286
Definition: obj.hpp:62
Definition: notifications.hpp:38
Definition: results.hpp:42
Definition: results.hpp:47
Definition: results.hpp:411