Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backlink support #996

Merged
merged 10 commits into from
Oct 28, 2022
Merged

Backlink support #996

merged 10 commits into from
Oct 28, 2022

Conversation

nielsenko
Copy link
Contributor

@nielsenko nielsenko commented Oct 27, 2022

We now support named back links

@RealmModel()
class _Source {
  _Target? target
}

@RealmModel()
class _Target {
  @Backlink(#target)
  late Iterable<_Source> backlinks;
}

...
final target = Target();
final source = realm.write(() => realm.add(Source(target: target)));

expect(source.target, target);
expect(target.backlinks, [source]);

Resolves: #693

@coveralls
Copy link

coveralls commented Oct 27, 2022

Pull Request Test Coverage Report for Build 3346094796

  • 92 of 97 (94.85%) changed or added relevant lines in 12 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage increased (+0.2%) to 89.611%

Changes Missing Coverage Covered Lines Changed/Added Lines %
generator/lib/src/field_element_ex.dart 29 30 96.67%
generator/lib/src/class_element_ex.dart 2 6 33.33%
Totals Coverage Status
Change from base Build 3343652406: 0.2%
Covered Lines: 2648
Relevant Lines: 2955

💛 - Coveralls

@nielsenko nielsenko force-pushed the kn/backlinks branch 2 times, most recently from dde705d to d795e54 Compare October 27, 2022 09:33
@@ -161,6 +161,9 @@ extension ClassElementEx on ClassElement {
todo: 'Remove the @PrimaryKey annotation from the field or set the model type to a value different from ObjectType.embeddedObject.');
}

// Computed fields go last. This is important for the schema generation.
mappedFields.sort((a, b) => a.isComputed ^ b.isComputed ? (a.isComputed ? 1 : -1) : -1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a bit too much math. Isn't easier to use sortedby? Like, on line 152, we could do something like:

      final mappedFields = fields.realmInfo.sortedBy<num>((f) => f.isComputed ? 0 : 1);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would need to pull in import 'package:collection/collection.dart and I dislike the need to add <num> bit to sortedBy (<int> won't do).

Also List.sort is in-place, but apparently not stable 🤔 ... nor is sortedBy

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me think a bit about this. I would like a stable sort, so that we only do the minimal needed changes to the ordering

test/backlinks_test.dart Outdated Show resolved Hide resolved
@nielsenko nielsenko marked this pull request as ready for review October 27, 2022 17:46
Copy link
Contributor

@blagoev blagoev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its good. I have small suggestions.

generator/lib/src/dart_type_ex.dart Show resolved Hide resolved
generator/lib/src/dart_type_ex.dart Outdated Show resolved Hide resolved
generator/lib/src/dart_type_ex.dart Show resolved Hide resolved
generator/test/test_util.dart Outdated Show resolved Hide resolved
test/backlinks_test.dart Outdated Show resolved Hide resolved
test/backlinks_test.dart Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
common/lib/src/realm_common_base.dart Outdated Show resolved Hide resolved
common/lib/src/realm_common_base.dart Outdated Show resolved Hide resolved
common/lib/src/realm_common_base.dart Outdated Show resolved Hide resolved
@nielsenko nielsenko force-pushed the kn/backlinks branch 5 times, most recently from 80e108b to 63c2b68 Compare October 28, 2022 10:53
generator/lib/src/field_element_ex.dart Outdated Show resolved Hide resolved
generator/lib/src/field_element_ex.dart Outdated Show resolved Hide resolved
generator/test/test_util.dart Outdated Show resolved Hide resolved
lib/src/realm_object.dart Outdated Show resolved Hide resolved
test/backlinks_test.dart Outdated Show resolved Hide resolved
@nielsenko nielsenko force-pushed the kn/backlinks branch 2 times, most recently from b04f002 to 3352c30 Compare October 28, 2022 13:04
@@ -149,7 +152,8 @@ extension ClassElementEx on ClassElement {

final objectType = ObjectType.values[modelInfo.value.getField('type')!.getField('index')!.toIntValue()!];

final mappedFields = fields.realmInfo.toList();
// Realm Core requires computed properties at the end so we sort them at generation time versus doing it at runtime every time.
final mappedFields = fields.realmInfo.toList()..sort((a, b) => a.isComputed ^ b.isComputed ? (a.isComputed ? 1 : -1) : -1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not sure this sorting is super readable. How about:

Suggested change
final mappedFields = fields.realmInfo.toList()..sort((a, b) => a.isComputed ^ b.isComputed ? (a.isComputed ? 1 : -1) : -1);
final mappedFields = fields.realmInfo.toList()..sort((a, b) => a.isComputed == b.isComputed ? 0 : (a.isComputed ? 1 : -1));

Or even simpler:

Suggested change
final mappedFields = fields.realmInfo.toList()..sort((a, b) => a.isComputed ^ b.isComputed ? (a.isComputed ? 1 : -1) : -1);
final mappedFields = fields.realmInfo.toList()..sort((a, b) => a.isComputed ? 1 : -1);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a dealbreaker though - it should still be correct, just feels a little complicated to understand.

Copy link
Contributor Author

@nielsenko nielsenko Oct 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like to read it like: If a and b are different wrt. computed, then if a is computed, a goes last, otherwise a goes first.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it matter that they're different though? I think my last suggestion reads like: "if a is computed, it goes last", which would be equivalent to "sort this array of bools in ascending order".

Co-authored-by: blagoev <lubo@blagoev.com>
@nielsenko nielsenko merged commit fcc825b into master Oct 28, 2022
@nielsenko nielsenko deleted the kn/backlinks branch October 28, 2022 14:17
@Taco55
Copy link

Taco55 commented Nov 6, 2022

After trying this new feature I get the following error after running the generator:

[SEVERE] realm:realm_generator on lib/schemas/category.dart:

Invalid argument(s): Element (FieldElementImpl) List<$Category> categories is not defined in this library.
#0      ResolvedLibraryResultImpl.getElementDeclaration.<anonymous closure> (package:analyzer/src/dart/analysis/results.dart:251:9)
#1      ListMixin.firstWhere (dart:collection/list.dart:166:38)
#2      ResolvedLibraryResultImpl.getElementDeclaration (package:analyzer/src/dart/analysis/results.dart:247:28)
#3      getDeclarationFromElement (package:realm_generator/src/element.dart:38:34)
#4      FieldElementEx.declarationAstNode (package:realm_generator/src/field_element_ex.dart:40:46)
#5      ElementEx.declarationAstNode (package:realm_generator/src/element.dart:72:43)
#6      ElementEx._annotationsInfoOfExact (package:realm_generator/src/element.dart:79:18)
#7      _SyncStarIterator.moveNext (dart:async-patch/async_patch.dart:710:21)
#8      new _GrowableList._ofOther (dart:core-patch/growable_array.dart:202:26)
#9      new _GrowableList.of (dart:core-patch/growable_array.dart:152:26)
#10     new List.of (dart:core-patch/array_patch.dart:51:28)
#11     Iterable.toList (dart:core/iterable.dart:470:12)
#12     ElementEx.annotationInfoOfExact (package:realm_generator/src/element.dart:89:58)
#13     FieldElementEx.realmInfo (package:realm_generator/src/field_element_ex.dart:250:44)
#14     _extension#0.realmInfo (package:realm_generator/src/class_element_ex.dart:37:22)
#15     _SyncStarIterator.moveNext (dart:async-patch/async_patch.dart:710:21)
#16     new _GrowableList._ofOther (dart:core-patch/growable_array.dart:202:26)
#17     new _GrowableList.of (dart:core-patch/growable_array.dart:152:26)
#18     new List.of (dart:core-patch/array_patch.dart:51:28)
#19     Iterable.toList (dart:core/iterable.dart:470:12)
#20     ClassElementEx.realmInfo (package:realm_generator/src/class_element_ex.dart:156:45)
#21     _extension#0.realmInfo.<anonymous closure> (package:realm_generator/src/realm_object_generator.dart:71:58)
#22     MappedIterator.moveNext (dart:_internal/iterable.dart:391:20)
#23     WhereIterator.moveNext (dart:_internal/iterable.dart:438:22)
#24     CastIterator.moveNext (dart:_internal/cast.dart:61:30)
#25     ExpandIterator.moveNext (dart:_internal/iterable.dart:477:21)
#26     Iterable.join (dart:core/iterable.dart:423:19)
#27     RealmObjectGenerator.generate.<anonymous closure>.<anonymous closure> (package:realm_generator/src/realm_object_generator.dart:61:69)
#28     _rootRun (dart:async/zone.dart:1391:13)
#29     _CustomZone.run (dart:async/zone.dart:1293:19)
#30     _runZoned (dart:async/zone.dart:1829:10)
#31     runZonedGuarded (dart:async/zone.dart:1817:12)
#32     scopeSession (package:realm_generator/src/session.dart:44:16)
#33     RealmObjectGenerator.generate.<anonymous closure> (package:realm_generator/src/realm_object_generator.dart:59:16)
<asynchronous suspension>
#34     measure.<anonymous closure> (package:realm_generator/src/measure.dart:50:18)
<asynchronous suspension>
#35     measure (package:realm_generator/src/measure.dart:47:7)
<asynchronous suspension>
#36     RealmObjectGenerator.generate (package:realm_generator/src/realm_object_generator.dart:56:12)
<asynchronous suspension>
#37     _generate (package:source_gen/src/builder.dart:352:23)
<asynchronous suspension>
#38     Stream.toList.<anonymous closure> (dart:async/stream.dart:1349:9)
<asynchronous suspension>

    ╷
7   │ @RealmModel()
8   │ class $Category {
    │       ━━━━━━━━━ in realm model for 'Category'
... │
32  │   @Backlink(#categories)
33  │   late Iterable<$Item> items;
    │                                 ^^^^^^^^^^^^^^ !
    ╵
Unexpected error. Please open an issue on: https://github.com/realm/realm-dart

I get the same error for Many-to-one and many-to-many relationships. Still anything wrong?

@nielsenko
Copy link
Contributor Author

@Taco55 This is because the backlink is cross file. It was first reported here #1015. A fix is already merged, but not released yet. You can work around the issue by ensuring that the class with the backlink field is defined in the same file as the linking class.

Sorry for the inconvenience.

@Taco55
Copy link

Taco55 commented Nov 7, 2022

@nielsenko Thanks for the quick answer. I will check it out!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Backlink support
5 participants