From 98d25e416adbeef2dfc34e940d946e712e5ca124 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Thu, 28 Jun 2018 15:27:18 -0700 Subject: [PATCH 1/2] Add object._setLink() --- src/js_realm_object.hpp | 46 ++++++++++++++++++++++ tests/js/object-tests.js | 82 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/src/js_realm_object.hpp b/src/js_realm_object.hpp index 4ccf84b6..c0a493d2 100644 --- a/src/js_realm_object.hpp +++ b/src/js_realm_object.hpp @@ -56,6 +56,7 @@ struct RealmObjectClass : ClassDefinition { static void linking_objects_count(ContextType, FunctionType, ObjectType, size_t, const ValueType [], ReturnValue &); static void get_object_id(ContextType, ObjectType, Arguments, ReturnValue &); static void is_same_object(ContextType, ObjectType, Arguments, ReturnValue &); + static void set_link(ContextType, ObjectType, Arguments, ReturnValue &); const std::string name = "RealmObject"; @@ -72,6 +73,7 @@ struct RealmObjectClass : ClassDefinition { {"linkingObjectsCount", wrap}, {"_objectId", wrap}, {"_isSameObject", wrap}, + {"_setLink", wrap}, }; }; @@ -140,6 +142,50 @@ bool RealmObjectClass::set_property(ContextType ctx, ObjectType object, const return true; } +template +void RealmObjectClass::set_link(ContextType ctx, ObjectType object, Arguments args, ReturnValue& return_value) { + args.validate_count(2); + + auto realm_object = get_internal>(object); + realm_object->realm()->verify_in_write(); + + NativeAccessor accessor(ctx, realm_object->realm(), realm_object->get_object_schema()); + std::string property_name = Value::validated_to_string(ctx, args[0], "propertyName"); + const Property* prop = realm_object->get_object_schema().property_for_name(property_name); + if (!prop) { + throw std::invalid_argument(util::format("No such property: %1", property_name)); + } + if (prop->type != realm::PropertyType::Object) { + throw TypeErrorException(accessor, realm_object->get_object_schema().name, *prop, args[1]); + } + auto& linked_schema = *realm_object->realm()->schema().find(prop->object_type); + auto linked_pk = linked_schema.primary_key_property(); + if (!linked_pk) { + throw std::invalid_argument("Linked object type must have a primary key."); + } + + auto table = realm_object->row().get_table(); + auto linked_table = table->get_link_target(prop->table_column); + + size_t row_ndx = realm::not_found; + if (linked_pk->type == realm::PropertyType::String) { + row_ndx = linked_table->find_first(linked_pk->table_column, + accessor.template unbox(args[1])); + } + else if (is_nullable(linked_pk->type)) { + row_ndx = linked_table->find_first(linked_pk->table_column, + accessor.template unbox>(args[1])); + } + else { + row_ndx = linked_table->find_first(linked_pk->table_column, + accessor.template unbox(args[1])); + } + + if (row_ndx != realm::not_found) { + realm_object->row().set_link(prop->table_column, row_ndx); + } +} + template std::vector> RealmObjectClass::get_property_names(ContextType ctx, ObjectType object) { auto realm_object = get_internal>(object); diff --git a/tests/js/object-tests.js b/tests/js/object-tests.js index 6b8fa25d..377d547a 100644 --- a/tests/js/object-tests.js +++ b/tests/js/object-tests.js @@ -473,5 +473,87 @@ module.exports = { TestCase.assertTrue(new Date('2017-12-07T20:16:03.837Z').toISOString() === objects[0].dateCol.toISOString()) realm.close() + }, + + testSetLink: function() { + const schema = [ + { + name: 'PrimaryInt', + primaryKey: 'pk', + properties: { + pk: 'int', + value: 'int' + } + }, + { + name: 'PrimaryOptionalInt', + primaryKey: 'pk', + properties: { + pk: 'int?', + value: 'int' + } + }, + { + name: 'PrimaryString', + primaryKey: 'pk', + properties: { + pk: 'string?', + value: 'int' + } + }, + { + name: 'Links', + properties: { + intLink: 'PrimaryInt', + optIntLink: 'PrimaryOptionalInt', + stringLink: 'PrimaryString' + } + } + ]; + + const realm = new Realm({schema: schema}); + realm.write(function() { + realm.create('PrimaryInt', {pk: 1, value: 2}) + realm.create('PrimaryInt', {pk: 2, value: 4}) + realm.create('PrimaryOptionalInt', {pk: 1, value: 2}) + realm.create('PrimaryOptionalInt', {pk: 2, value: 4}) + realm.create('PrimaryOptionalInt', {pk: null, value: 6}) + realm.create('PrimaryString', {pk: 'a', value: 2}) + realm.create('PrimaryString', {pk: 'b', value: 4}) + realm.create('PrimaryString', {pk: null, value: 6}) + + const obj = realm.create('Links', {}); + + obj._setLink('intLink', 3); + TestCase.assertEqual(obj.intLink, null); + obj._setLink('intLink', 1); + TestCase.assertEqual(obj.intLink.value, 2); + obj._setLink('intLink', 2); + TestCase.assertEqual(obj.intLink.value, 4); + obj._setLink('intLink', 3); + TestCase.assertEqual(obj.intLink.value, 4); + + obj._setLink('optIntLink', 3); + TestCase.assertEqual(obj.optIntLink, null); + obj._setLink('optIntLink', 1); + TestCase.assertEqual(obj.optIntLink.value, 2); + obj._setLink('optIntLink', 2); + TestCase.assertEqual(obj.optIntLink.value, 4); + obj._setLink('optIntLink', null); + TestCase.assertEqual(obj.optIntLink.value, 6); + obj._setLink('optIntLink', 3); + TestCase.assertEqual(obj.optIntLink.value, 6); + + obj._setLink('stringLink', 'c'); + TestCase.assertEqual(obj.stringLink, null); + obj._setLink('stringLink', 'a'); + TestCase.assertEqual(obj.stringLink.value, 2); + obj._setLink('stringLink', 'b'); + TestCase.assertEqual(obj.stringLink.value, 4); + obj._setLink('stringLink', null); + TestCase.assertEqual(obj.stringLink.value, 6); + obj._setLink('stringLink', 'c'); + TestCase.assertEqual(obj.stringLink.value, 6); + }); } }; From 6b38b1cbb3de8eda0abfb26973ea3711095a5b10 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Tue, 3 Jul 2018 09:58:00 -0700 Subject: [PATCH 2/2] Set the link to null if the linked object doesn't exist --- src/js_realm_object.hpp | 5 ++++- tests/js/object-tests.js | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/js_realm_object.hpp b/src/js_realm_object.hpp index c0a493d2..a42a9718 100644 --- a/src/js_realm_object.hpp +++ b/src/js_realm_object.hpp @@ -181,7 +181,10 @@ void RealmObjectClass::set_link(ContextType ctx, ObjectType object, Arguments accessor.template unbox(args[1])); } - if (row_ndx != realm::not_found) { + if (row_ndx == realm::not_found) { + realm_object->row().set_null(prop->table_column); + } + else { realm_object->row().set_link(prop->table_column, row_ndx); } } diff --git a/tests/js/object-tests.js b/tests/js/object-tests.js index 377d547a..8095f556 100644 --- a/tests/js/object-tests.js +++ b/tests/js/object-tests.js @@ -531,7 +531,7 @@ module.exports = { obj._setLink('intLink', 2); TestCase.assertEqual(obj.intLink.value, 4); obj._setLink('intLink', 3); - TestCase.assertEqual(obj.intLink.value, 4); + TestCase.assertEqual(obj.intLink, null); obj._setLink('optIntLink', 3); TestCase.assertEqual(obj.optIntLink, null); @@ -542,7 +542,7 @@ module.exports = { obj._setLink('optIntLink', null); TestCase.assertEqual(obj.optIntLink.value, 6); obj._setLink('optIntLink', 3); - TestCase.assertEqual(obj.optIntLink.value, 6); + TestCase.assertEqual(obj.optIntLink, null); obj._setLink('stringLink', 'c'); TestCase.assertEqual(obj.stringLink, null); @@ -553,7 +553,7 @@ module.exports = { obj._setLink('stringLink', null); TestCase.assertEqual(obj.stringLink.value, 6); obj._setLink('stringLink', 'c'); - TestCase.assertEqual(obj.stringLink.value, 6); + TestCase.assertEqual(obj.stringLink, null); }); } };