Add support for aggregates on arrays of primitives (#1401)
* Fix an unused variable warning in js_sync.hpp * Add support for aggregates on arrays of primitives * Update documentation and typescript declarations * Update collection.js
This commit is contained in:
parent
4bcef8baff
commit
643c19ea75
|
@ -5,6 +5,7 @@ X.Y.Z Release notes
|
|||
|
||||
### Enhancements
|
||||
* Added `update` method to `Realm.Results` to support bulk updates (#808).
|
||||
* Added support for aggregate functions on `Realm.Results` and `Realm.List` of primitive types.
|
||||
|
||||
### Bug fixes
|
||||
* None
|
||||
|
|
|
@ -239,8 +239,14 @@ class Collection {
|
|||
indexOf(object) {}
|
||||
|
||||
/**
|
||||
* Computes the minimum value of a property.
|
||||
* @param {string} property - The name of the property.
|
||||
* Returns the minimum value of the values in the collection or of the
|
||||
* given property among all the objects in the collection, or `undefined`
|
||||
* if the collection is empty.
|
||||
*
|
||||
* Only supported for int, float, double and date properties. `null` values
|
||||
* are ignored entirely by this method and will not be returned.
|
||||
*
|
||||
* @param {string} [property] - For a collection of objects, the property to take the minimum of.
|
||||
* @throws {Error} If no property with the name exists or if property is not numeric/date.
|
||||
* @returns {number} the minimum value.
|
||||
* @since 1.12.1
|
||||
|
@ -248,8 +254,14 @@ class Collection {
|
|||
min(property) {}
|
||||
|
||||
/**
|
||||
* Computes the maximum value of a property.
|
||||
* @param {string} property - The name of the property.
|
||||
* Returns the maximum value of the values in the collection or of the
|
||||
* given property among all the objects in the collection, or `undefined`
|
||||
* if the collection is empty.
|
||||
*
|
||||
* Only supported for int, float, double and date properties. `null` values
|
||||
* are ignored entirely by this method and will not be returned.
|
||||
*
|
||||
* @param {string} [property] - For a collection of objects, the property to take the maximum of.
|
||||
* @throws {Error} If no property with the name exists or if property is not numeric/date.
|
||||
* @returns {number} the maximum value.
|
||||
* @since 1.12.1
|
||||
|
@ -257,19 +269,29 @@ class Collection {
|
|||
max(property) {}
|
||||
|
||||
/**
|
||||
* Computes the sum of a property.
|
||||
* @param {string} property - The name of the property.
|
||||
* @throws {Error} If no property with the name exists or if property is not numeric/date.
|
||||
* Computes the sum of the values in the collection or of the given
|
||||
* property among all the objects in the collection, or 0 if the collection
|
||||
* is empty.
|
||||
*
|
||||
* Only supported for int, float and double properties. `null` values are
|
||||
* ignored entirely by this method.
|
||||
* @param {string} [property] - For a collection of objects, the property to take the sum of.
|
||||
* @throws {Error} If no property with the name exists or if property is not numeric.
|
||||
* @returns {number} the sum.
|
||||
* @since 1.12.1
|
||||
*/
|
||||
sum(property) {}
|
||||
|
||||
/**
|
||||
* Computes the average of a property.
|
||||
* @param {string} property - The name of the property.
|
||||
* @throws {Error} If no property with the name exists or if property is not numeric/date.
|
||||
* @returns {number} the average value.
|
||||
* Computes the average of the values in the collection or of the given
|
||||
* property among all the objects in the collection, or `undefined` if the collection
|
||||
* is empty.
|
||||
*
|
||||
* Only supported for int, float and double properties. `null` values are
|
||||
* ignored entirely by this method and will not be factored into the average.
|
||||
* @param {string} [property] - For a collection of objects, the property to take the average of.
|
||||
* @throws {Error} If no property with the name exists or if property is not numeric.
|
||||
* @returns {number} the sum.
|
||||
* @since 1.12.1
|
||||
*/
|
||||
avg(property) {}
|
||||
|
|
|
@ -144,33 +144,10 @@ declare namespace Realm {
|
|||
*/
|
||||
isValid(): boolean;
|
||||
|
||||
/**
|
||||
* Computes the minimum value.
|
||||
* @param {string} property
|
||||
* @returns number
|
||||
*/
|
||||
min(property: string): number;
|
||||
|
||||
/**
|
||||
* Computes the maximum value.
|
||||
* @param {string} property
|
||||
* @returns number
|
||||
*/
|
||||
max(property: string): number;
|
||||
|
||||
/**
|
||||
* Computes the sum.
|
||||
* @param {string} property
|
||||
* @returns number
|
||||
*/
|
||||
sum(property: string): number;
|
||||
|
||||
/**
|
||||
* Computes the average.
|
||||
* @param {string} property
|
||||
* @returns number
|
||||
*/
|
||||
avg(property: string): number;
|
||||
min(property?: string): number|Date|null;
|
||||
max(property?: string): number|Date|null;
|
||||
sum(property?: string): number|null;
|
||||
avg(property?: string): number;
|
||||
|
||||
/**
|
||||
* @param {string} query
|
||||
|
|
|
@ -78,12 +78,6 @@ struct ListClass : ClassDefinition<T, realm::js::List<T>, CollectionClass<T>> {
|
|||
static void is_valid(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void index_of(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
||||
// aggregate functions
|
||||
static void min(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void max(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void sum(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void avg(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
// observable
|
||||
static void add_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
@ -102,10 +96,10 @@ struct ListClass : ClassDefinition<T, realm::js::List<T>, CollectionClass<T>> {
|
|||
{"sorted", wrap<sorted>},
|
||||
{"isValid", wrap<is_valid>},
|
||||
{"indexOf", wrap<index_of>},
|
||||
{"min", wrap<min>},
|
||||
{"max", wrap<max>},
|
||||
{"sum", wrap<sum>},
|
||||
{"avg", wrap<avg>},
|
||||
{"min", wrap<compute_aggregate_on_collection<ListClass<T>, AggregateFunc::Min>>},
|
||||
{"max", wrap<compute_aggregate_on_collection<ListClass<T>, AggregateFunc::Max>>},
|
||||
{"sum", wrap<compute_aggregate_on_collection<ListClass<T>, AggregateFunc::Sum>>},
|
||||
{"avg", wrap<compute_aggregate_on_collection<ListClass<T>, AggregateFunc::Avg>>},
|
||||
{"addListener", wrap<add_listener>},
|
||||
{"removeListener", wrap<remove_listener>},
|
||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||
|
@ -134,26 +128,6 @@ void ListClass<T>::get_length(ContextType, ObjectType object, ReturnValue &retur
|
|||
return_value.set((uint32_t)list->size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::min(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
compute_aggregate_on_collection<ListClass<T>>(AggregateFunc::Min, ctx, this_object, argc, arguments, return_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::max(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
compute_aggregate_on_collection<ListClass<T>>(AggregateFunc::Max, ctx, this_object, argc, arguments, return_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::sum(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
compute_aggregate_on_collection<ListClass<T>>(AggregateFunc::Sum, ctx, this_object, argc, arguments, return_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::avg(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
compute_aggregate_on_collection<ListClass<T>>(AggregateFunc::Avg, ctx, this_object, argc, arguments, return_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ListClass<T>::get_type(ContextType ctx, ObjectType object, ReturnValue &return_value) {
|
||||
auto list = get_internal<T, ListClass<T>>(object);
|
||||
|
|
|
@ -89,12 +89,6 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||
|
||||
static void update(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
// aggregate functions
|
||||
static void min(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void max(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void sum(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
static void avg(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||
|
||||
// observable
|
||||
static void add_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
static void remove_listener(ContextType, ObjectType, Arguments, ReturnValue &);
|
||||
|
@ -112,10 +106,10 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||
{"filtered", wrap<filtered>},
|
||||
{"sorted", wrap<sorted>},
|
||||
{"isValid", wrap<is_valid>},
|
||||
{"min", wrap<min>},
|
||||
{"max", wrap<max>},
|
||||
{"sum", wrap<sum>},
|
||||
{"avg", wrap<avg>},
|
||||
{"min", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Min>>},
|
||||
{"max", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Max>>},
|
||||
{"sum", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Sum>>},
|
||||
{"avg", wrap<compute_aggregate_on_collection<ResultsClass<T>, AggregateFunc::Avg>>},
|
||||
{"addListener", wrap<add_listener>},
|
||||
{"removeListener", wrap<remove_listener>},
|
||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||
|
@ -214,26 +208,6 @@ void ResultsClass<T>::get_length(ContextType ctx, ObjectType object, ReturnValue
|
|||
return_value.set((uint32_t)results->size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::min(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
compute_aggregate_on_collection<ResultsClass<T>>(AggregateFunc::Min, ctx, this_object, argc, arguments, return_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::max(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
compute_aggregate_on_collection<ResultsClass<T>>(AggregateFunc::Max, ctx, this_object, argc, arguments, return_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::sum(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
compute_aggregate_on_collection<ResultsClass<T>>(AggregateFunc::Sum, ctx, this_object, argc, arguments, return_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::avg(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
compute_aggregate_on_collection<ResultsClass<T>>(AggregateFunc::Avg, ctx, this_object, argc, arguments, return_value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::get_type(ContextType, ObjectType object, ReturnValue &return_value) {
|
||||
auto results = get_internal<T, ResultsClass<T>>(object);
|
||||
|
|
|
@ -18,10 +18,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <regex>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
|
@ -40,11 +36,11 @@ namespace realm {
|
|||
namespace js {
|
||||
|
||||
inline realm::SyncManager& syncManagerShared() {
|
||||
static bool configured = []{
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, [] {
|
||||
ensure_directory_exists_for_file(default_realm_file_directory());
|
||||
SyncManager::shared().configure_file_system(default_realm_file_directory(), SyncManager::MetadataMode::NoEncryption);
|
||||
return true;
|
||||
}();
|
||||
});
|
||||
return SyncManager::shared();
|
||||
}
|
||||
|
||||
|
@ -627,8 +623,10 @@ void SyncClass<T>::populate_sync_config(ContextType ctx, ObjectType realm_constr
|
|||
|
||||
std::string raw_realm_url = Object::validated_get_string(ctx, sync_config_object, "url");
|
||||
if (shared_user->token_type() == SyncUser::TokenType::Admin) {
|
||||
static std::regex tilde("/~/");
|
||||
raw_realm_url = std::regex_replace(raw_realm_url, tilde, "/__auth/");
|
||||
size_t pos = raw_realm_url.find("/~/");
|
||||
if (pos != std::string::npos) {
|
||||
raw_realm_url.replace(pos + 1, 1, "__auth");
|
||||
}
|
||||
}
|
||||
|
||||
bool client_validate_ssl = true;
|
||||
|
|
|
@ -139,7 +139,7 @@ struct Value {
|
|||
static ValueType from_nonnull_binary(ContextType, BinaryData);
|
||||
static ValueType from_undefined(ContextType);
|
||||
static ValueType from_timestamp(ContextType, Timestamp);
|
||||
static ValueType from_mixed(ContextType, util::Optional<Mixed> &);
|
||||
static ValueType from_mixed(ContextType, const util::Optional<Mixed> &);
|
||||
|
||||
static ObjectType to_array(ContextType, const ValueType &);
|
||||
static bool to_boolean(ContextType, const ValueType &);
|
||||
|
@ -446,7 +446,7 @@ inline typename T::Value Value<T>::from_timestamp(typename T::Context ctx, Times
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename T::Value Value<T>::from_mixed(typename T::Context ctx, util::Optional<Mixed>& mixed) {
|
||||
inline typename T::Value Value<T>::from_mixed(typename T::Context ctx, const util::Optional<Mixed>& mixed) {
|
||||
if (!mixed) {
|
||||
return from_undefined(ctx);
|
||||
}
|
||||
|
|
|
@ -83,50 +83,42 @@ static inline void validate_argument_count_at_least(size_t count, size_t expecte
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline void compute_aggregate_on_collection(AggregateFunc func, typename T::ContextType ctx, typename T::ObjectType this_object, size_t argc, const typename T::ValueType arguments[], typename T::ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 1);
|
||||
std::string property_name = T::Value::validated_to_string(ctx, arguments[0]);
|
||||
template<typename T, AggregateFunc func>
|
||||
void compute_aggregate_on_collection(typename T::ContextType ctx, typename T::ObjectType this_object,
|
||||
typename T::Arguments args, typename T::ReturnValue &return_value) {
|
||||
|
||||
auto list = get_internal<typename T::Type, T>(this_object);
|
||||
|
||||
size_t column = 0;
|
||||
if (list->get_type() == realm::PropertyType::Object) {
|
||||
const ObjectSchema& object_schema = list->get_object_schema();
|
||||
std::string property_name = T::Value::validated_to_string(ctx, args[0]);
|
||||
const Property* property = object_schema.property_for_name(property_name);
|
||||
if (!property) {
|
||||
throw std::invalid_argument(util::format("No such property: %1", property_name));
|
||||
throw std::invalid_argument(util::format("Property '%1' does not exist on object '%2'",
|
||||
property_name, object_schema.name));
|
||||
}
|
||||
column = property->table_column;
|
||||
}
|
||||
else {
|
||||
args.validate_maximum(0);
|
||||
}
|
||||
|
||||
util::Optional<Mixed> mixed;
|
||||
switch (func) {
|
||||
case AggregateFunc::Min: {
|
||||
mixed = list->min(property->table_column);
|
||||
case AggregateFunc::Min:
|
||||
return_value.set(list->min(column));
|
||||
break;
|
||||
case AggregateFunc::Max:
|
||||
return_value.set(list->max(column));
|
||||
break;
|
||||
case AggregateFunc::Sum:
|
||||
return_value.set(list->sum(column));
|
||||
break;
|
||||
case AggregateFunc::Avg:
|
||||
return_value.set(list->average(column));
|
||||
break;
|
||||
}
|
||||
case AggregateFunc::Max: {
|
||||
mixed = list->max(property->table_column);
|
||||
break;
|
||||
}
|
||||
case AggregateFunc::Sum: {
|
||||
mixed = list->sum(property->table_column);
|
||||
break;
|
||||
}
|
||||
case AggregateFunc::Avg: {
|
||||
util::Optional<double> avg = list->average(property->table_column);
|
||||
if (!avg) {
|
||||
return_value.set_undefined();
|
||||
}
|
||||
else {
|
||||
return_value.set(*avg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
REALM_ASSERT(false && "Unknown aggregate function");
|
||||
REALM_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
return_value.set(T::Value::from_mixed(ctx, mixed));
|
||||
}
|
||||
|
||||
} // js
|
||||
|
|
|
@ -53,12 +53,26 @@ class ReturnValue<jsc::Types> {
|
|||
void set(uint32_t number) {
|
||||
m_value = JSValueMakeNumber(m_context, number);
|
||||
}
|
||||
void set(const util::Optional<realm::Mixed>& mixed) {
|
||||
m_value = Value<jsc::Types>::from_mixed(m_context, mixed);
|
||||
}
|
||||
void set_null() {
|
||||
m_value = JSValueMakeNull(m_context);
|
||||
}
|
||||
void set_undefined() {
|
||||
m_value = JSValueMakeUndefined(m_context);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(const util::Optional<T>& value) {
|
||||
if (value) {
|
||||
set(*value);
|
||||
}
|
||||
else {
|
||||
set_undefined();
|
||||
}
|
||||
}
|
||||
|
||||
operator JSValueRef() const {
|
||||
return m_value;
|
||||
}
|
||||
|
|
|
@ -64,12 +64,25 @@ class ReturnValue<node::Types> {
|
|||
void set(uint32_t number) {
|
||||
m_value.Set(number);
|
||||
}
|
||||
void set(realm::Mixed mixed) {
|
||||
m_value.Set(Value<node::Types>::from_mixed(nullptr, mixed));
|
||||
}
|
||||
void set_null() {
|
||||
m_value.SetNull();
|
||||
}
|
||||
void set_undefined() {
|
||||
m_value.SetUndefined();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set(util::Optional<T> value) {
|
||||
if (value) {
|
||||
set(*value);
|
||||
}
|
||||
else {
|
||||
m_value.SetUndefined();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // js
|
||||
|
|
|
@ -1009,17 +1009,14 @@ module.exports = {
|
|||
const NullableBasicTypesList = {
|
||||
name: 'NullableBasicTypesList',
|
||||
properties: {
|
||||
list: {type: 'list', objectType: 'NullableBasicTypesObject'},
|
||||
list: 'NullableBasicTypesObject[]',
|
||||
}
|
||||
};
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes, NullableBasicTypesList]});
|
||||
var object;
|
||||
|
||||
const realm = new Realm({schema: [schemas.NullableBasicTypes, NullableBasicTypesList]});
|
||||
const N = 50;
|
||||
|
||||
var list = [];
|
||||
for(var i = 0; i < N; i++) {
|
||||
const list = [];
|
||||
for (let i = 0; i < N; i++) {
|
||||
list.push({
|
||||
intCol: i+1,
|
||||
floatCol: i+1,
|
||||
|
@ -1028,6 +1025,7 @@ module.exports = {
|
|||
});
|
||||
}
|
||||
|
||||
let object;
|
||||
realm.write(() => {
|
||||
object = realm.create('NullableBasicTypesList', {list: list});
|
||||
});
|
||||
|
@ -1051,19 +1049,17 @@ module.exports = {
|
|||
const NullableBasicTypesList = {
|
||||
name: 'NullableBasicTypesList',
|
||||
properties: {
|
||||
list: {type: 'list', objectType: 'NullableBasicTypesObject'},
|
||||
list: 'NullableBasicTypesObject[]',
|
||||
}
|
||||
};
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes, NullableBasicTypesList]});
|
||||
var object;
|
||||
var objectEmptyList;
|
||||
const realm = new Realm({schema: [schemas.NullableBasicTypes, NullableBasicTypesList]});
|
||||
|
||||
const N = 50;
|
||||
const M = 10;
|
||||
|
||||
var list = [];
|
||||
for(var i = 0; i < N; i++) {
|
||||
const list = [];
|
||||
for (let i = 0; i < N; i++) {
|
||||
list.push({
|
||||
intCol: i+1,
|
||||
floatCol: i+1,
|
||||
|
@ -1072,15 +1068,11 @@ module.exports = {
|
|||
});
|
||||
}
|
||||
|
||||
for(var j = 0; j < M; j++) {
|
||||
list.push({
|
||||
intCol: null,
|
||||
floatCol: null,
|
||||
doubleCol: null,
|
||||
dateCol: null
|
||||
});
|
||||
for (let j = 0; j < M; j++) {
|
||||
list.push({});
|
||||
}
|
||||
|
||||
let object, objectEmptyList;
|
||||
realm.write(() => {
|
||||
object = realm.create('NullableBasicTypesList', {list: list});
|
||||
objectEmptyList = realm.create('NullableBasicTypesList', {list: []});
|
||||
|
@ -1088,7 +1080,6 @@ module.exports = {
|
|||
|
||||
TestCase.assertEqual(object.list.length, N + M);
|
||||
|
||||
|
||||
// int, float & double columns support all aggregate functions
|
||||
// the M null valued objects should be ignored
|
||||
['intCol', 'floatCol', 'doubleCol'].forEach(colName => {
|
||||
|
@ -1115,6 +1106,44 @@ module.exports = {
|
|||
TestCase.assertUndefined(objectEmptyList.list.max('dateCol'));
|
||||
},
|
||||
|
||||
testPrimitiveListAggregateFunctions: function() {
|
||||
const realm = new Realm({schema: [schemas.PrimitiveArrays]});
|
||||
let object;
|
||||
realm.write(() => {
|
||||
object = realm.create('PrimitiveArrays', {
|
||||
int: [1, 2, 3],
|
||||
float: [1.1, 2.2, 3.3],
|
||||
double: [1.11, 2.22, 3.33],
|
||||
date: [DATE1, DATE2, DATE3],
|
||||
|
||||
optInt: [1, null, 2],
|
||||
optFloat: [1.1, null, 3.3],
|
||||
optDouble: [1.11, null, 3.33],
|
||||
optDate: [DATE1, null, DATE3]
|
||||
});
|
||||
});
|
||||
|
||||
for (let prop of ['int', 'float', 'double', 'date', 'optInt', 'optFloat', 'optDouble', 'optDate']) {
|
||||
const list = object[prop];
|
||||
TestCase.assertSimilar(list.type, list.min(), list[0]);
|
||||
TestCase.assertSimilar(list.type, list.max(), list[2]);
|
||||
|
||||
if (list.type === 'date') {
|
||||
TestCase.assertThrowsContaining(() => list.sum(), "Cannot sum 'date' array: operation not supported")
|
||||
TestCase.assertThrowsContaining(() => list.avg(), "Cannot average 'date' array: operation not supported")
|
||||
continue;
|
||||
}
|
||||
|
||||
const sum = list[0] + list[1] + list[2];
|
||||
const avg = sum / (list[1] === null ? 2 : 3);
|
||||
TestCase.assertSimilar(list.type, list.sum(), sum);
|
||||
TestCase.assertSimilar(list.type, list.avg(), avg);
|
||||
}
|
||||
|
||||
TestCase.assertThrowsContaining(() => object.bool.min(), "Cannot min 'bool' array: operation not supported")
|
||||
TestCase.assertThrowsContaining(() => object.int.min("foo"), "Invalid arguments: at most 0 expected, but 1 supplied")
|
||||
},
|
||||
|
||||
testListAggregateFunctionsUnsupported: function() {
|
||||
const NullableBasicTypesList = {
|
||||
name: 'NullableBasicTypesList',
|
||||
|
@ -1123,13 +1152,12 @@ module.exports = {
|
|||
}
|
||||
};
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes, NullableBasicTypesList]});
|
||||
var object;
|
||||
const realm = new Realm({schema: [schemas.NullableBasicTypes, NullableBasicTypesList]});
|
||||
|
||||
const N = 5;
|
||||
|
||||
var list = [];
|
||||
for(var i = 0; i < N; i++) {
|
||||
for (let i = 0; i < N; i++) {
|
||||
list.push({
|
||||
intCol: i+1,
|
||||
floatCol: i+1,
|
||||
|
@ -1138,6 +1166,7 @@ module.exports = {
|
|||
});
|
||||
}
|
||||
|
||||
let object;
|
||||
realm.write(() => {
|
||||
object = realm.create('NullableBasicTypesList', {list: list});
|
||||
});
|
||||
|
@ -1145,38 +1174,33 @@ module.exports = {
|
|||
TestCase.assertEqual(object.list.length, N);
|
||||
|
||||
// bool, string & data columns don't support 'min'
|
||||
['boolCol', 'stringCol', 'dataCol'].forEach(colName => {
|
||||
TestCase.assertThrows(function() {
|
||||
object.list.min(colName);
|
||||
}
|
||||
)});
|
||||
['bool', 'string', 'data'].forEach(colName => {
|
||||
TestCase.assertThrowsContaining(() => object.list.min(colName + 'Col'),
|
||||
`Cannot min property '${colName}Col': operation not supported for '${colName}' properties`);
|
||||
});
|
||||
|
||||
// bool, string & data columns don't support 'max'
|
||||
['boolCol', 'stringCol', 'dataCol'].forEach(colName => {
|
||||
TestCase.assertThrows(function() {
|
||||
object.list.max(colName);
|
||||
}
|
||||
)});
|
||||
['bool', 'string', 'data'].forEach(colName => {
|
||||
TestCase.assertThrowsContaining(() => object.list.max(colName + 'Col'),
|
||||
`Cannot max property '${colName}Col': operation not supported for '${colName}' properties`);
|
||||
});
|
||||
|
||||
// bool, string, date & data columns don't support 'avg'
|
||||
['boolCol', 'stringCol', 'dateCol', 'dataCol'].forEach(colName => {
|
||||
TestCase.assertThrows(function() {
|
||||
object.list.avg(colName);
|
||||
}
|
||||
)});
|
||||
['bool', 'string', 'date', 'data'].forEach(colName => {
|
||||
TestCase.assertThrowsContaining(() => object.list.avg(colName + 'Col'),
|
||||
`Cannot average property '${colName}Col': operation not supported for '${colName}' properties`);
|
||||
});
|
||||
|
||||
// bool, string, date & data columns don't support 'sum'
|
||||
['boolCol', 'stringCol', 'dateCol', 'dataCol'].forEach(colName => {
|
||||
TestCase.assertThrows(function() {
|
||||
object.list.sum(colName);
|
||||
}
|
||||
)});
|
||||
['bool', 'string', 'date', 'data'].forEach(colName => {
|
||||
TestCase.assertThrowsContaining(() => object.list.sum(colName + 'Col'),
|
||||
`Cannot sum property '${colName}Col': operation not supported for '${colName}' properties`);
|
||||
});
|
||||
},
|
||||
|
||||
testListAggregateFunctionsWrongProperty: function() {
|
||||
var realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]});
|
||||
var object;
|
||||
var list;
|
||||
const realm = new Realm({schema: [schemas.PersonObject, schemas.PersonList]});
|
||||
let object;
|
||||
realm.write(() => {
|
||||
object = realm.create('PersonList', {list: [
|
||||
{name: 'Ari', age: 10},
|
||||
|
@ -1185,18 +1209,21 @@ module.exports = {
|
|||
]});
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
object.list.min('foo')
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
object.list.max('foo')
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
object.list.sum('foo')
|
||||
});
|
||||
TestCase.assertThrows(function() {
|
||||
object.list.avg('foo')
|
||||
});
|
||||
|
||||
TestCase.assertThrowsContaining(() => object.list.min('foo'),
|
||||
"Property 'foo' does not exist on object 'PersonObject'");
|
||||
TestCase.assertThrowsContaining(() => object.list.max('foo'),
|
||||
"Property 'foo' does not exist on object 'PersonObject'");
|
||||
TestCase.assertThrowsContaining(() => object.list.sum('foo'),
|
||||
"Property 'foo' does not exist on object 'PersonObject'");
|
||||
TestCase.assertThrowsContaining(() => object.list.avg('foo'),
|
||||
"Property 'foo' does not exist on object 'PersonObject'");
|
||||
TestCase.assertThrowsContaining(() => object.list.min(),
|
||||
"JS value must be of type 'string', got (undefined)");
|
||||
TestCase.assertThrowsContaining(() => object.list.max(),
|
||||
"JS value must be of type 'string', got (undefined)");
|
||||
TestCase.assertThrowsContaining(() => object.list.sum(),
|
||||
"JS value must be of type 'string', got (undefined)");
|
||||
TestCase.assertThrowsContaining(() => object.list.avg(),
|
||||
"JS value must be of type 'string', got (undefined)");
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue