Add support for bulk updates (#808)
This commit is contained in:
parent
957a6dd292
commit
4bcef8baff
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,3 +1,14 @@
|
|||
X.Y.Z Release notes
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
* None
|
||||
|
||||
### Enhancements
|
||||
* Added `update` method to `Realm.Results` to support bulk updates (#808).
|
||||
|
||||
### Bug fixes
|
||||
* None
|
||||
|
||||
2.0.0-rc19 Release notes (2017-10-7)
|
||||
=============================================================
|
||||
### Breaking changes
|
||||
|
|
|
@ -28,4 +28,12 @@
|
|||
* @memberof Realm
|
||||
*/
|
||||
class Results extends Collection {
|
||||
/**
|
||||
* Bulk update objects in the collection.
|
||||
* @param {string} property - The name of the property.
|
||||
* @param {string} value - The updated property value.
|
||||
* @throws {Error} If no property with the name exists.
|
||||
* @since 2.0.0-rc20
|
||||
*/
|
||||
update(property, value) {}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import { createMethods } from './util';
|
|||
export default class Results extends Collection {
|
||||
}
|
||||
|
||||
// Non-mutating methods:
|
||||
createMethods(Results.prototype, objectTypes.RESULTS, [
|
||||
'filtered',
|
||||
'sorted',
|
||||
|
@ -40,6 +41,11 @@ createMethods(Results.prototype, objectTypes.RESULTS, [
|
|||
'removeAllListeners',
|
||||
]);
|
||||
|
||||
// Mutating methods:
|
||||
createMethods(Results.prototype, objectTypes.RESULTS, [
|
||||
'update',
|
||||
], true);
|
||||
|
||||
export function createResults(realmId, info) {
|
||||
return createCollection(Results.prototype, realmId, info);
|
||||
}
|
||||
|
|
|
@ -250,7 +250,13 @@ declare namespace Realm {
|
|||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Results.html }
|
||||
*/
|
||||
interface Results<T> extends Collection<T> {
|
||||
|
||||
/**
|
||||
* Bulk update objects in the collection.
|
||||
* @param {string} property
|
||||
* @param {any} value
|
||||
* @returns void
|
||||
*/
|
||||
update(property: string, value: any): void;
|
||||
}
|
||||
|
||||
const Results: {
|
||||
|
|
|
@ -87,6 +87,8 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||
template<typename Fn>
|
||||
static void index_of(ContextType, Fn&, Arguments, ReturnValue &);
|
||||
|
||||
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 &);
|
||||
|
@ -118,6 +120,7 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||
{"removeListener", wrap<remove_listener>},
|
||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||
{"indexOf", wrap<index_of>},
|
||||
{"update", wrap<update>},
|
||||
};
|
||||
|
||||
PropertyMap<T> const properties = {
|
||||
|
@ -298,6 +301,32 @@ void ResultsClass<T>::index_of(ContextType ctx, Fn& fn, Arguments args, ReturnVa
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::update(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
validate_argument_count(argc, 2);
|
||||
|
||||
std::string property = Value::validated_to_string(ctx, arguments[0], "property");
|
||||
auto results = get_internal<T, ResultsClass<T>>(this_object);
|
||||
|
||||
auto schema = results->get_object_schema();
|
||||
if (!schema.property_for_name(StringData(property))) {
|
||||
throw std::invalid_argument(util::format("No such property: %1", property));
|
||||
}
|
||||
|
||||
auto realm = results->get_realm();
|
||||
if (!realm->is_in_transaction()) {
|
||||
throw std::runtime_error("Can only 'update' objects within a transaction.");
|
||||
}
|
||||
|
||||
// TODO: This approach just moves the for-loop from JS to C++
|
||||
// Ideally, we'd implement this in OS or Core in an optimized fashion
|
||||
for (auto i = results->size(); i > 0; i--) {
|
||||
auto realm_object = realm::Object(realm, schema, results->get(i - 1));
|
||||
auto obj = RealmObjectClass<T>::create_instance(ctx, realm_object);
|
||||
RealmObjectClass<T>::set_property(ctx, obj, property, arguments[1]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ResultsClass<T>::index_of(ContextType ctx, ObjectType this_object,
|
||||
Arguments args, ReturnValue &return_value) {
|
||||
|
|
|
@ -626,4 +626,231 @@ module.exports = {
|
|||
TestCase.assertEqual(results.length, 0);
|
||||
TestCase.assertEqual(calls, 2);
|
||||
},
|
||||
|
||||
testResultsUpdate: function() {
|
||||
const N = 5;
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', { intCol: 10 });
|
||||
}
|
||||
});
|
||||
|
||||
// update should work on a basic result set
|
||||
var results = realm.objects('NullableBasicTypesObject');
|
||||
TestCase.assertEqual(results.length, 5);
|
||||
realm.write(() => {
|
||||
results.update('intCol', 20);
|
||||
});
|
||||
TestCase.assertEqual(results.length, 5);
|
||||
TestCase.assertEqual(realm.objects('NullableBasicTypesObject').filtered('intCol = 20').length, 5);
|
||||
|
||||
// update should work on a filtered result set
|
||||
results = realm.objects('NullableBasicTypesObject').filtered('intCol = 20');
|
||||
realm.write(() => {
|
||||
results.update('intCol', 10);
|
||||
});
|
||||
TestCase.assertEqual(results.length, 0);
|
||||
TestCase.assertEqual(realm.objects('NullableBasicTypesObject').filtered('intCol = 10').length, 5);
|
||||
|
||||
// update should work on a sorted result set
|
||||
results = realm.objects('NullableBasicTypesObject').filtered('intCol == 10').sorted('intCol');
|
||||
realm.write(() => {
|
||||
results.update('intCol', 20);
|
||||
});
|
||||
TestCase.assertEqual(results.length, 0);
|
||||
TestCase.assertEqual(realm.objects('NullableBasicTypesObject').filtered('intCol = 20').length, 5);
|
||||
|
||||
// update should work on a result snapshot
|
||||
results = realm.objects('NullableBasicTypesObject').filtered('intCol == 20').snapshot();
|
||||
realm.write(() => {
|
||||
results.update('intCol', 10);
|
||||
});
|
||||
TestCase.assertEqual(results.length, 5); // snapshot length should not change
|
||||
TestCase.assertEqual(realm.objects('NullableBasicTypesObject').filtered('intCol = 10').length, 5);
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testResultsUpdateDataTypes: function() {
|
||||
const N = 5;
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
boolCol: false,
|
||||
stringCol: 'hello',
|
||||
intCol: 10,
|
||||
floatCol: 10.0,
|
||||
doubleCol: 10.0,
|
||||
dateCol: new Date(10)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const testCases = [
|
||||
// col name, initial filter, initial filter pre-update count, initial filter post-update count, updated value, updated filter, updated filter post-update count
|
||||
[ 'boolCol', 'boolCol = false', N, 0, true, 'boolCol = true', N ],
|
||||
[ 'stringCol', 'stringCol = "hello"', N, 0, 'world', 'stringCol = "world"', N ],
|
||||
[ 'intCol', 'intCol = 10', N, 0, 20, 'intCol = 20', N ],
|
||||
[ 'floatCol', 'floatCol = 10.0', N, 0, 20.0, 'floatCol = 20.0', N ],
|
||||
[ 'doubleCol', 'doubleCol = 10.0', N, 0, 20.0, 'doubleCol = 20.0', N ],
|
||||
];
|
||||
|
||||
testCases.forEach(function(testCase) {
|
||||
var results = realm.objects('NullableBasicTypesObject').filtered(testCase[1]);
|
||||
TestCase.assertEqual(results.length, testCase[2]);
|
||||
|
||||
realm.write(() => {
|
||||
results.update(testCase[0], testCase[4]);
|
||||
});
|
||||
|
||||
TestCase.assertEqual(results.length, testCase[3]);
|
||||
|
||||
results = realm.objects('NullableBasicTypesObject').filtered(testCase[5]);
|
||||
TestCase.assertEqual(results.length, testCase[6]);
|
||||
});
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testResultUpdateDateColumn: function() {
|
||||
const N = 5;
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
|
||||
// date column
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
dateCol: new Date(1000)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var results = realm.objects('NullableBasicTypesObject').filtered('dateCol = $0', new Date(1000));
|
||||
TestCase.assertEqual(results.length, N);
|
||||
|
||||
realm.write(() => {
|
||||
results.update('dateCol', new Date(2000));
|
||||
});
|
||||
|
||||
TestCase.assertEqual(results.length, 0);
|
||||
results = realm.objects('NullableBasicTypesObject').filtered('dateCol = $0', new Date(2000));
|
||||
TestCase.assertEqual(results.length, N);
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testResultsUpdateDataColumn: function() {
|
||||
const N = 5;
|
||||
|
||||
var RANDOM_DATA = new Uint8Array([
|
||||
0xd8, 0x21, 0xd6, 0xe8, 0x00, 0x57, 0xbc, 0xb2, 0x6a, 0x15, 0x77, 0x30, 0xac, 0x77, 0x96, 0xd9,
|
||||
0x67, 0x1e, 0x40, 0xa7, 0x6d, 0x52, 0x83, 0xda, 0x07, 0x29, 0x9c, 0x70, 0x38, 0x48, 0x4e, 0xff,
|
||||
]);
|
||||
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
|
||||
// date column
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
dataCol: null
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var results = realm.objects('NullableBasicTypesObject');
|
||||
TestCase.assertEqual(results.length, N);
|
||||
|
||||
realm.write(() => {
|
||||
results.update('dataCol', RANDOM_DATA);
|
||||
});
|
||||
|
||||
for(var i = 0; i < results.length; i++) {
|
||||
TestCase.assertArraysEqual(new Uint8Array(results[i].dataCol), RANDOM_DATA);
|
||||
}
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testResultsUpdateEmpty() {
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
|
||||
var emptyResults = realm.objects('NullableBasicTypesObject').filtered('stringCol = "hello"');
|
||||
TestCase.assertEqual(emptyResults.length, 0);
|
||||
|
||||
realm.write(() => {
|
||||
emptyResults.update('stringCol', 'no-op');
|
||||
});
|
||||
|
||||
TestCase.assertEqual(emptyResults.length, 0);
|
||||
TestCase.assertEqual(realm.objects('NullableBasicTypesObject').filtered('stringCol = "no-op"').length, 0);
|
||||
|
||||
realm.close();
|
||||
},
|
||||
|
||||
testResultsUpdateInvalidated() {
|
||||
var realm = new Realm({schema: [schemas.TestObject]});
|
||||
realm.write(function() {
|
||||
for (var i = 10; i > 0; i--) {
|
||||
realm.create('TestObject', [i]);
|
||||
}
|
||||
});
|
||||
|
||||
var resultsVariants = [
|
||||
realm.objects('TestObject'),
|
||||
realm.objects('TestObject').filtered('doubleCol > 1'),
|
||||
realm.objects('TestObject').filtered('doubleCol > 1').sorted('doubleCol'),
|
||||
realm.objects('TestObject').filtered('doubleCol > 1').snapshot()
|
||||
];
|
||||
|
||||
// test isValid
|
||||
resultsVariants.forEach(function(objects) {
|
||||
TestCase.assertEqual(objects.isValid(), true);
|
||||
});
|
||||
|
||||
// close and test update
|
||||
realm.close();
|
||||
realm = new Realm({
|
||||
schemaVersion: 1,
|
||||
schema: [schemas.TestObject, schemas.BasicTypes]
|
||||
});
|
||||
|
||||
resultsVariants.forEach(function(objects) {
|
||||
TestCase.assertEqual(objects.isValid(), false);
|
||||
TestCase.assertThrows(function() { objects.update('doubleCol', 42); });
|
||||
});
|
||||
},
|
||||
|
||||
testResultsUpdateWrongProperty() {
|
||||
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
|
||||
|
||||
const N = 5;
|
||||
realm.write(() => {
|
||||
for(var i = 0; i < N; i++) {
|
||||
realm.create('NullableBasicTypesObject', {
|
||||
stringCol: 'hello'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var results = realm.objects('NullableBasicTypesObject').filtered('stringCol = "hello"');
|
||||
TestCase.assertEqual(results.length, N);
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
realm.write(() => {
|
||||
results.update('unknownCol', 'world');
|
||||
});
|
||||
});
|
||||
|
||||
TestCase.assertThrows(function() {
|
||||
results.update('stringCol', 'world');
|
||||
});
|
||||
|
||||
realm.close();
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue