Skip to content

Commit

Permalink
Support indexes on all compatible types (#875)
Browse files Browse the repository at this point in the history
* Support indexes on all indexable types

* Fix bad tests. Bool is actually indexable now.

However, it is not possible to use bool (or bool?) as primary key.

* Update test all_types.dart

* Add good test for all indexable types

* Update CHANGELOG

* Apply suggestions from code review

Co-authored-by: Nikola Irinchev <[email protected]>

* Add test of index speedup

* Remove Base. No longer needed

* Only display measurements on test failure

* Reinstate doc comment on RealmPropertyType

* Tweak comment regarding AOT compilation

Co-authored-by: Nikola Irinchev <[email protected]>
  • Loading branch information
nielsenko and nirinchev authored Nov 3, 2022
1 parent 58c01e9 commit 88a1729
Show file tree
Hide file tree
Showing 18 changed files with 608 additions and 82 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* Support results of primitives, ie. `RealmResult<int>`. (Issue [#162](https://github.com/realm/realm-dart/issues/162))
* Support notifications on all managed realm lists, including list of primitives, ie. `RealmList<int>.changes` is supported. ([#893](https://github.com/realm/realm-dart/pull/893))
* Support named backlinks on realm models. You can now add and annotate a realm object iterator field with `@Backlink(#fieldName)`. ([#996](https://github.com/realm/realm-dart/pull/996))
* Allow `@Indexed` attribute on all indexable type, and ensure appropriate indexes are created in the realm. ([#797](https://github.com/realm/realm-dart/issues/797))

### Fixed
* Fixed a wrong mapping for `AuthProviderType` returned by `User.provider` for google, facebook and apple credentials.
Expand Down
47 changes: 31 additions & 16 deletions common/lib/src/realm_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,47 @@ import 'dart:typed_data';
import 'package:objectid/objectid.dart';
import 'package:sane_uuid/uuid.dart';

Type _typeOf<T>() => T;

/// @nodoc
class Mapping<T> {
const Mapping({this.indexable = false});

final bool indexable;

// Types
Type get type => T;
Type get nullableType => _typeOf<T?>();
}

const _intMapping = Mapping<int>(indexable: true);
const _boolMapping = Mapping<bool>(indexable: true);

/// All supported `Realm` property types.
/// {@category Configuration}
enum RealmPropertyType {
int,
bool,
string,
// ignore: unused_field, constant_identifier_names
_3,
int(_intMapping),
bool(_boolMapping),
string(Mapping<String>(indexable: true)),
_3, // ignore: unused_field, constant_identifier_names
binary,
// ignore: unused_field, constant_identifier_names
_5,
_5, // ignore: unused_field, constant_identifier_names
mixed,
// ignore: unused_field, constant_identifier_names
_7,
timestamp,
_7, // ignore: unused_field, constant_identifier_names
timestamp(Mapping<DateTime>(indexable: true)),
float,
double,
decimal128,
object,
// ignore: unused_field, constant_identifier_names
_13,
_13, // ignore: unused_field, constant_identifier_names
linkingObjects,
objectid,
// ignore: unused_field, constant_identifier_names
_16,
uuid,
objectid(Mapping<ObjectId>(indexable: true)),
_16, // ignore: unused_field, constant_identifier_names
uuid(Mapping<Uuid>(indexable: true));

const RealmPropertyType([this.mapping = const Mapping<Never>()]);

final Mapping<dynamic> mapping;
}

/// All supported `Realm` collection types.
Expand Down
13 changes: 8 additions & 5 deletions generator/lib/src/field_element_ex.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,20 @@ extension FieldElementEx on FieldElement {
}

// Validate indexes
if ((primaryKey != null || indexed != null) &&
(![RealmPropertyType.string, RealmPropertyType.int, RealmPropertyType.objectid, RealmPropertyType.uuid].contains(type.realmType) ||
type.isRealmCollection)) {
if ((((primaryKey ?? indexed) != null) && !(type.realmType?.mapping.indexable ?? false)) || //
(primaryKey != null && type.isDartCoreBool)) {
final file = span!.file;
final annotation = (primaryKey ?? indexed)!.annotation;
final listOfValidTypes = RealmPropertyType.values //
.map((t) => t.mapping)
.where((m) => m.indexable && (m.type != bool || primaryKey == null))
.map((m) => m.type);

throw RealmInvalidGenerationSourceError(
'Realm only support indexes on String, int, and bool fields',
'Realm only supports the $annotation annotation on fields of type\n${listOfValidTypes.join(', ')}\nas well as their nullable versions',
element: this,
primarySpan: typeSpan(file),
primaryLabel: "$modelTypeName is not an indexable type",
primaryLabel: "$modelTypeName is not a valid type here",
todo: //
"Change the type of '$displayName', "
"or remove the $annotation annotation",
Expand Down
1 change: 1 addition & 0 deletions generator/lib/src/realm_model_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class RealmModelInfo {
if (f.name != f.realmName) 'mapTo': f.realmName,
if (f.optional) 'optional': f.optional,
if (f.isPrimaryKey) 'primaryKey': f.isPrimaryKey,
if (f.indexed) 'indexed': f.indexed,
if (f.realmType == RealmPropertyType.object) 'linkTarget': f.basicRealmTypeName,
if (f.realmType == RealmPropertyType.linkingObjects) ...{
'linkOriginProperty': f.linkOriginProperty!,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
Realm only support indexes on String, int, and bool fields
Realm only supports the @PrimaryKey() annotation on fields of type
int, String, DateTime, ObjectId, Uuid
as well as their nullable versions

in: asset:pkg/test/error_test_data/bool_not_allowed_as_primary_key.dart:9:8
Expand All @@ -8,7 +10,7 @@ in: asset:pkg/test/error_test_data/bool_not_allowed_as_primary_key.dart:9:8
│ ━━━━ in realm model for 'Foo'
8 │ @PrimaryKey()
9 │ late bool bad;
│ ^^^^ bool is not an indexable type
│ ^^^^ bool is not a valid type here
Change the type of 'bad', or remove the @PrimaryKey() annotation

This file was deleted.

This file was deleted.

3 changes: 1 addition & 2 deletions generator/test/error_test_data/not_an_indexable_type.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import 'dart:ffi';
import 'package:realm_common/realm_common.dart';

//part 'not_an_indexable_type.g.dart';

@RealmModel()
class _Bad {
@Indexed()
late Double notAnIndexableType;
late double notAnIndexableType;
}
16 changes: 9 additions & 7 deletions generator/test/error_test_data/not_an_indexable_type.expected
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
Realm only support indexes on String, int, and bool fields
Realm only supports the @Indexed() annotation on fields of type
int, bool, String, DateTime, ObjectId, Uuid
as well as their nullable versions

in: asset:pkg/test/error_test_data/not_an_indexable_type.dart:9:8
in: asset:pkg/test/error_test_data/not_an_indexable_type.dart:8:8
6 │ @RealmModel()
7 │ class _Bad {
5 │ @RealmModel()
6 │ class _Bad {
│ ━━━━ in realm model for 'Bad'
8 │ @Indexed()
9 │ late Double notAnIndexableType;
│ ^^^^^^ Double is not an indexable type
7 │ @Indexed()
8 │ late double notAnIndexableType;
│ ^^^^^^ double is not a valid type here
Change the type of 'notAnIndexableType', or remove the @Indexed() annotation

15 changes: 9 additions & 6 deletions generator/test/good_test_data/all_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,29 @@ import 'package:realm_common/realm_common.dart';
@RealmModel()
@MapTo('MyFoo')
class _Foo {
@Indexed()
int x = 0;
late _Bar? bar;
}

@RealmModel()
class _Bar {
@PrimaryKey()
late String id;
late bool aBool, another;
late String name;
@Indexed()
late bool aBool, another; // both are indexed!
var data = Uint8List(16);
// late RealmAny any; // not supported yet
@MapTo('tidspunkt')
@Indexed()
var timestamp = DateTime.now();
var aDouble = 0.0;
// late Decimal128 decimal; // not supported yet
_Foo? foo;
// late ObjectId id;
// late Uuid uuid; // not supported yet
@Indexed()
late ObjectId objectId;
@Indexed()
late Uuid uuid;
@Ignored()
var theMeaningOfEverything = 42;
var list = [0]; // list of ints with default value
Expand All @@ -32,8 +37,6 @@ class _Bar {
@Indexed()
String? anOptionalString;

late ObjectId objectId;

@Backlink(#bar)
late Iterable<_Foo> foos;
}
Expand Down
47 changes: 28 additions & 19 deletions generator/test/good_test_data/all_types.expected
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Foo extends _Foo with RealmEntity, RealmObjectBase, RealmObject {
static SchemaObject _initSchema() {
RealmObjectBase.registerFactory(Foo._);
return const SchemaObject(ObjectType.realmObject, Foo, 'MyFoo', [
SchemaProperty('x', RealmPropertyType.int),
SchemaProperty('x', RealmPropertyType.int, indexed: true),
SchemaProperty('bar', RealmPropertyType.object,
optional: true, linkTarget: 'Bar'),
]);
Expand All @@ -53,10 +53,11 @@ class Bar extends _Bar with RealmEntity, RealmObjectBase, RealmObject {
static var _defaultsSet = false;

Bar(
String id,
String name,
bool aBool,
bool another,
ObjectId objectId, {
ObjectId objectId,
Uuid uuid, {
Uint8List data = Uint8List(16),
DateTime timestamp = DateTime.now(),
double aDouble = 0.0,
Expand All @@ -71,24 +72,25 @@ class Bar extends _Bar with RealmEntity, RealmObjectBase, RealmObject {
'aDouble': 0.0,
});
}
RealmObjectBase.set(this, 'id', id);
RealmObjectBase.set(this, 'name', name);
RealmObjectBase.set(this, 'aBool', aBool);
RealmObjectBase.set(this, 'another', another);
RealmObjectBase.set(this, 'data', data);
RealmObjectBase.set(this, 'tidspunkt', timestamp);
RealmObjectBase.set(this, 'aDouble', aDouble);
RealmObjectBase.set(this, 'foo', foo);
RealmObjectBase.set(this, 'anOptionalString', anOptionalString);
RealmObjectBase.set(this, 'objectId', objectId);
RealmObjectBase.set(this, 'uuid', uuid);
RealmObjectBase.set(this, 'anOptionalString', anOptionalString);
RealmObjectBase.set<RealmList<int>>(this, 'list', RealmList<int>(list));
}

Bar._();

@override
String get id => RealmObjectBase.get<String>(this, 'id') as String;
String get name => RealmObjectBase.get<String>(this, 'name') as String;
@override
set id(String value) => RealmObjectBase.set(this, 'id', value);
set name(String value) => RealmObjectBase.set(this, 'name', value);

@override
bool get aBool => RealmObjectBase.get<bool>(this, 'aBool') as bool;
Expand Down Expand Up @@ -123,6 +125,18 @@ class Bar extends _Bar with RealmEntity, RealmObjectBase, RealmObject {
@override
set foo(covariant Foo? value) => RealmObjectBase.set(this, 'foo', value);

@override
ObjectId get objectId =>
RealmObjectBase.get<ObjectId>(this, 'objectId') as ObjectId;
@override
set objectId(ObjectId value) => RealmObjectBase.set(this, 'objectId', value);

@override
Uuid get uuid =>
RealmObjectBase.get<Uuid>(this, 'uuid') as Uuid;
@override
set uuid(Uuid value) => RealmObjectBase.set(this, 'uuid', value);

@override
RealmList<int> get list =>
RealmObjectBase.get<int>(this, 'list') as RealmList<int>;
Expand All @@ -136,12 +150,6 @@ class Bar extends _Bar with RealmEntity, RealmObjectBase, RealmObject {
set anOptionalString(String? value) =>
RealmObjectBase.set(this, 'anOptionalString', value);

@override
ObjectId get objectId =>
RealmObjectBase.get<ObjectId>(this, 'objectId') as ObjectId;
@override
set objectId(ObjectId value) => RealmObjectBase.set(this, 'objectId', value);

@override
RealmResults<Foo> get foos =>
RealmObjectBase.get<Foo>(this, 'foos') as RealmResults<Foo>;
Expand All @@ -161,20 +169,21 @@ class Bar extends _Bar with RealmEntity, RealmObjectBase, RealmObject {
static SchemaObject _initSchema() {
RealmObjectBase.registerFactory(Bar._);
return const SchemaObject(ObjectType.realmObject, Bar, 'Bar', [
SchemaProperty('id', RealmPropertyType.string, primaryKey: true),
SchemaProperty('aBool', RealmPropertyType.bool),
SchemaProperty('another', RealmPropertyType.bool),
SchemaProperty('name', RealmPropertyType.string, primaryKey: true),
SchemaProperty('aBool', RealmPropertyType.bool, indexed: true),
SchemaProperty('another', RealmPropertyType.bool, indexed: true),
SchemaProperty('data', RealmPropertyType.binary),
SchemaProperty('tidspunkt', RealmPropertyType.timestamp,
mapTo: 'tidspunkt'),
mapTo: 'tidspunkt', indexed: true),
SchemaProperty('aDouble', RealmPropertyType.double),
SchemaProperty('foo', RealmPropertyType.object,
optional: true, linkTarget: 'MyFoo'),
SchemaProperty('objectId', RealmPropertyType.objectid, indexed: true),
SchemaProperty('uuid', RealmPropertyType.uuid, indexed: true),
SchemaProperty('list', RealmPropertyType.int,
collectionType: RealmCollectionType.list),
SchemaProperty('anOptionalString', RealmPropertyType.string,
optional: true),
SchemaProperty('objectId', RealmPropertyType.objectid),
optional: true, indexed: true),
SchemaProperty('foos', RealmPropertyType.linkingObjects,
linkOriginProperty: 'bar',
collectionType: RealmCollectionType.list,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class Child1 extends _Child1 with RealmEntity, RealmObjectBase, EmbeddedObject {
SchemaProperty('_value', RealmPropertyType.string, mapTo: '_value'),
SchemaProperty('_parent', RealmPropertyType.object,
mapTo: '_parent', optional: true, linkTarget: 'Parent'),
SchemaProperty('indexedString', RealmPropertyType.string),
SchemaProperty('indexedString', RealmPropertyType.string, indexed: true),
]);
}
}
Expand Down
31 changes: 31 additions & 0 deletions generator/test/good_test_data/indexable_types.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:realm_common/realm_common.dart';

// part 'indexable_types.g.dart';

@RealmModel()
class _Indexable {
@Indexed()
late bool aBool;
@Indexed()
bool? aNullableBool;
@Indexed()
late int anInt;
@Indexed()
int? aNullableInt;
@Indexed()
late String aString;
@Indexed()
String? aNullableString;
@Indexed()
late ObjectId anObjectId;
@Indexed()
ObjectId? aNullableObjectId;
@Indexed()
late Uuid anUuid;
@Indexed()
Uuid? aNullableUuid;
@Indexed()
late DateTime aDateTime;
@Indexed()
DateTime? aNullableDateTime;
}
Loading

0 comments on commit 88a1729

Please sign in to comment.