added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / results.hpp
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2015 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 //
17 ////////////////////////////////////////////////////////////////////////////
18
19 #ifndef REALM_RESULTS_HPP
20 #define REALM_RESULTS_HPP
21
22 #include "collection_notifications.hpp"
23 #include "impl/collection_notifier.hpp"
24 #include "property.hpp"
25
26 #include <realm/table_view.hpp>
27 #include <realm/util/optional.hpp>
28
29 namespace realm {
30 class Mixed;
31 class ObjectSchema;
32
33 namespace _impl {
34     class ResultsNotifier;
35 }
36
37 class Results {
38 public:
39     // Results can be either be backed by nothing, a thin wrapper around a table,
40     // or a wrapper around a query and a sort order which creates and updates
41     // the tableview as needed
42     Results();
43     Results(std::shared_ptr<Realm> r, Table& table);
44     Results(std::shared_ptr<Realm> r, Query q, DescriptorOrdering o = {});
45     Results(std::shared_ptr<Realm> r, TableView tv, DescriptorOrdering o = {});
46     Results(std::shared_ptr<Realm> r, LinkViewRef lv, util::Optional<Query> q = {}, SortDescriptor s = {});
47     ~Results();
48
49     // Results is copyable and moveable
50     Results(Results&&);
51     Results& operator=(Results&&);
52     Results(const Results&);
53     Results& operator=(const Results&);
54
55     // Get the Realm
56     std::shared_ptr<Realm> get_realm() const { return m_realm; }
57
58     // Object schema describing the vendored object type
59     const ObjectSchema &get_object_schema() const;
60
61     // Get a query which will match the same rows as is contained in this Results
62     // Returned query will not be valid if the current mode is Empty
63     Query get_query() const;
64
65     // Get the list of sort and distinct operations applied for this Results.
66     DescriptorOrdering const& get_descriptor_ordering() const noexcept { return m_descriptor_ordering; }
67
68     // Get a tableview containing the same rows as this Results
69     TableView get_tableview();
70
71     // Get the object type which will be returned by get()
72     StringData get_object_type() const noexcept;
73
74     PropertyType get_type() const;
75
76     // Get the LinkView this Results is derived from, if any
77     LinkViewRef get_linkview() const { return m_link_view; }
78
79     // Get the size of this results
80     // Can be either O(1) or O(N) depending on the state of things
81     size_t size();
82
83     // Get the row accessor for the given index
84     // Throws OutOfBoundsIndexException if index >= size()
85     template<typename T = RowExpr>
86     T get(size_t index);
87
88     // Get the boxed row accessor for the given index
89     // Throws OutOfBoundsIndexException if index >= size()
90     template<typename Context>
91     auto get(Context&, size_t index);
92
93     // Get a row accessor for the first/last row, or none if the results are empty
94     // More efficient than calling size()+get()
95     template<typename T = RowExpr>
96     util::Optional<T> first();
97     template<typename T = RowExpr>
98     util::Optional<T> last();
99
100     // Get the index of the first row matching the query in this table
101     size_t index_of(Query&& q);
102
103     // Get the first index of the given value in this results, or not_found
104     // Throws DetachedAccessorException if row is not attached
105     // Throws IncorrectTableException if row belongs to a different table
106     template<typename T>
107     size_t index_of(T const& value);
108
109     // Delete all of the rows in this Results from the Realm
110     // size() will always be zero afterwards
111     // Throws InvalidTransactionException if not in a write transaction
112     void clear();
113
114     // Create a new Results by further filtering or sorting this Results
115     Results filter(Query&& q) const;
116     Results sort(SortDescriptor&& sort) const;
117     Results sort(std::vector<std::pair<std::string, bool>> const& keypaths) const;
118
119     // Create a new Results by removing duplicates
120     Results distinct(DistinctDescriptor&& uniqueness) const;
121     Results distinct(std::vector<std::string> const& keypaths) const;
122
123     // Return a snapshot of this Results that never updates to reflect changes in the underlying data.
124     Results snapshot() const &;
125     Results snapshot() &&;
126
127     // Get the min/max/average/sum of the given column
128     // All but sum() returns none when there are zero matching rows
129     // sum() returns 0, except for when it returns none
130     // Throws UnsupportedColumnTypeException for sum/average on timestamp or non-numeric column
131     // Throws OutOfBoundsIndexException for an out-of-bounds column
132     util::Optional<Mixed> max(size_t column=0);
133     util::Optional<Mixed> min(size_t column=0);
134     util::Optional<double> average(size_t column=0);
135     util::Optional<Mixed> sum(size_t column=0);
136
137     enum class Mode {
138         Empty, // Backed by nothing (for missing tables)
139         Table, // Backed directly by a Table
140         Query, // Backed by a query that has not yet been turned into a TableView
141         LinkView,  // Backed directly by a LinkView
142         TableView, // Backed by a TableView created from a Query
143     };
144     // Get the currrent mode of the Results
145     // Ideally this would not be public but it's needed for some KVO stuff
146     Mode get_mode() const { return m_mode; }
147
148     // Is this Results associated with a Realm that has not been invalidated?
149     bool is_valid() const;
150
151     // The Results object has been invalidated (due to the Realm being invalidated)
152     // All non-noexcept functions can throw this
153     struct InvalidatedException : public std::logic_error {
154         InvalidatedException() : std::logic_error("Access to invalidated Results objects") {}
155     };
156
157     // The input index parameter was out of bounds
158     struct OutOfBoundsIndexException : public std::out_of_range {
159         OutOfBoundsIndexException(size_t r, size_t c);
160         const size_t requested;
161         const size_t valid_count;
162     };
163
164     // The input Row object is not attached
165     struct DetatchedAccessorException : public std::logic_error {
166         DetatchedAccessorException() : std::logic_error("Atempting to access an invalid object") {}
167     };
168
169     // The input Row object belongs to a different table
170     struct IncorrectTableException : public std::logic_error {
171         IncorrectTableException(StringData e, StringData a, const std::string &error) :
172             std::logic_error(error), expected(e), actual(a) {}
173         const StringData expected;
174         const StringData actual;
175     };
176
177     // The requested aggregate operation is not supported for the column type
178     struct UnsupportedColumnTypeException : public std::logic_error {
179         size_t column_index;
180         StringData column_name;
181         PropertyType property_type;
182
183         UnsupportedColumnTypeException(size_t column, const Table* table, const char* operation);
184     };
185
186     // Create an async query from this Results
187     // The query will be run on a background thread and delivered to the callback,
188     // and then rerun after each commit (if needed) and redelivered if it changed
189     template<typename Func>
190     NotificationToken async(Func&& target);
191     NotificationToken add_notification_callback(CollectionChangeCallback cb) &;
192
193     bool wants_background_updates() const { return m_wants_background_updates; }
194
195     // Returns whether the rows are guaranteed to be in table order.
196     bool is_in_table_order() const;
197
198     // Helper type to let ResultsNotifier update the tableview without giving access
199     // to any other privates or letting anyone else do so
200     class Internal {
201         friend class _impl::ResultsNotifier;
202         static void set_table_view(Results& results, TableView&& tv);
203     };
204
205     template<typename Context> auto first(Context&);
206     template<typename Context> auto last(Context&);
207
208     template<typename Context, typename T>
209     size_t index_of(Context&, T value);
210
211     // Execute the query immediately if needed. When the relevant query is slow, size()
212     // may cost similar time compared with creating the tableview. Use this function to
213     // avoid running the query twice for size() and other accessors.
214     void evaluate_query_if_needed(bool wants_notifications = true);
215
216 private:
217     enum class UpdatePolicy {
218         Auto,  // Update automatically to reflect changes in the underlying data.
219         Never, // Never update.
220     };
221
222     std::shared_ptr<Realm> m_realm;
223     mutable const ObjectSchema *m_object_schema = nullptr;
224     Query m_query;
225     TableView m_table_view;
226     LinkViewRef m_link_view;
227     TableRef m_table;
228     DescriptorOrdering m_descriptor_ordering;
229
230     _impl::CollectionNotifier::Handle<_impl::ResultsNotifier> m_notifier;
231
232     Mode m_mode = Mode::Empty;
233     UpdatePolicy m_update_policy = UpdatePolicy::Auto;
234     bool m_has_used_table_view = false;
235     bool m_wants_background_updates = true;
236
237     bool update_linkview();
238
239     void validate_read() const;
240     void validate_write() const;
241
242     void prepare_async();
243
244     template<typename T>
245     util::Optional<T> try_get(size_t);
246
247     template<typename Int, typename Float, typename Double, typename Timestamp>
248     util::Optional<Mixed> aggregate(size_t column,
249                                     const char* name,
250                                     Int agg_int, Float agg_float,
251                                     Double agg_double, Timestamp agg_timestamp);
252     void prepare_for_aggregate(size_t column, const char* name);
253
254     void set_table_view(TableView&& tv);
255
256     template<typename Fn>
257     auto dispatch(Fn&&) const;
258 };
259
260 template<typename Func>
261 NotificationToken Results::async(Func&& target)
262 {
263     return this->add_notification_callback([target = std::forward<Func>(target)](CollectionChangeSet const&, std::exception_ptr e) {
264         target(e);
265     });
266 }
267
268 template<typename Fn>
269 auto Results::dispatch(Fn&& fn) const
270 {
271     return switch_on_type(get_type(), std::forward<Fn>(fn));
272 }
273
274 template<typename Context>
275 auto Results::get(Context& ctx, size_t row_ndx)
276 {
277     return dispatch([&](auto t) { return ctx.box(this->get<std::decay_t<decltype(*t)>>(row_ndx)); });
278 }
279
280 template<typename Context>
281 auto Results::first(Context& ctx)
282 {
283     // GCC 4.9 complains about `ctx` not being defined within the lambda without this goofy capture
284     return dispatch([this, ctx = &ctx](auto t) {
285         auto value = this->first<std::decay_t<decltype(*t)>>();
286         return value ? static_cast<decltype(ctx->no_value())>(ctx->box(*value)) : ctx->no_value();
287     });
288 }
289
290 template<typename Context>
291 auto Results::last(Context& ctx)
292 {
293     return dispatch([&](auto t) {
294         auto value = this->last<std::decay_t<decltype(*t)>>();
295         return value ? static_cast<decltype(ctx.no_value())>(ctx.box(*value)) : ctx.no_value();
296     });
297 }
298
299 template<typename Context, typename T>
300 size_t Results::index_of(Context& ctx, T value)
301 {
302     return dispatch([&](auto t) { return this->index_of(ctx.template unbox<std::decay_t<decltype(*t)>>(value)); });
303 }
304 } // namespace realm
305
306 #endif // REALM_RESULTS_HPP