mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-09 13:55:49 +00:00
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)
|
2.0.0-rc19 Release notes (2017-10-7)
|
||||||
=============================================================
|
=============================================================
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
@ -28,4 +28,12 @@
|
|||||||
* @memberof Realm
|
* @memberof Realm
|
||||||
*/
|
*/
|
||||||
class Results extends Collection {
|
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 {
|
export default class Results extends Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Non-mutating methods:
|
||||||
createMethods(Results.prototype, objectTypes.RESULTS, [
|
createMethods(Results.prototype, objectTypes.RESULTS, [
|
||||||
'filtered',
|
'filtered',
|
||||||
'sorted',
|
'sorted',
|
||||||
@ -40,6 +41,11 @@ createMethods(Results.prototype, objectTypes.RESULTS, [
|
|||||||
'removeAllListeners',
|
'removeAllListeners',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Mutating methods:
|
||||||
|
createMethods(Results.prototype, objectTypes.RESULTS, [
|
||||||
|
'update',
|
||||||
|
], true);
|
||||||
|
|
||||||
export function createResults(realmId, info) {
|
export function createResults(realmId, info) {
|
||||||
return createCollection(Results.prototype, realmId, info);
|
return createCollection(Results.prototype, realmId, info);
|
||||||
}
|
}
|
||||||
|
8
lib/index.d.ts
vendored
8
lib/index.d.ts
vendored
@ -250,7 +250,13 @@ declare namespace Realm {
|
|||||||
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Results.html }
|
* @see { @link https://realm.io/docs/javascript/latest/api/Realm.Results.html }
|
||||||
*/
|
*/
|
||||||
interface Results<T> extends Collection<T> {
|
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: {
|
const Results: {
|
||||||
|
@ -87,6 +87,8 @@ struct ResultsClass : ClassDefinition<T, realm::js::Results<T>, CollectionClass<
|
|||||||
template<typename Fn>
|
template<typename Fn>
|
||||||
static void index_of(ContextType, Fn&, Arguments, ReturnValue &);
|
static void index_of(ContextType, Fn&, Arguments, ReturnValue &);
|
||||||
|
|
||||||
|
static void update(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||||
|
|
||||||
// aggregate functions
|
// aggregate functions
|
||||||
static void min(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
static void min(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &);
|
||||||
static void max(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>},
|
{"removeListener", wrap<remove_listener>},
|
||||||
{"removeAllListeners", wrap<remove_all_listeners>},
|
{"removeAllListeners", wrap<remove_all_listeners>},
|
||||||
{"indexOf", wrap<index_of>},
|
{"indexOf", wrap<index_of>},
|
||||||
|
{"update", wrap<update>},
|
||||||
};
|
};
|
||||||
|
|
||||||
PropertyMap<T> const properties = {
|
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>
|
template<typename T>
|
||||||
void ResultsClass<T>::index_of(ContextType ctx, ObjectType this_object,
|
void ResultsClass<T>::index_of(ContextType ctx, ObjectType this_object,
|
||||||
Arguments args, ReturnValue &return_value) {
|
Arguments args, ReturnValue &return_value) {
|
||||||
|
@ -626,4 +626,231 @@ module.exports = {
|
|||||||
TestCase.assertEqual(results.length, 0);
|
TestCase.assertEqual(results.length, 0);
|
||||||
TestCase.assertEqual(calls, 2);
|
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…
x
Reference in New Issue
Block a user