From 9f7602e71da469d48b4074bc13802cd546d0f433 Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Wed, 21 Oct 2020 03:13:21 +0200 Subject: [PATCH 01/13] added basic functions to relate --- lib/src/bindings/bindings.dart | 56 ++++++++++++++++++++++++++++++++ lib/src/bindings/signatures.dart | 36 ++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/lib/src/bindings/bindings.dart b/lib/src/bindings/bindings.dart index 16d852586..d1b8f3954 100644 --- a/lib/src/bindings/bindings.dart +++ b/lib/src/bindings/bindings.dart @@ -50,6 +50,12 @@ class _ObjectBoxBindings { obx_model_entity_last_property_id; int Function(Pointer model, int entity_id, int entity_uid) obx_model_last_entity_id; + int Function(Pointer model, Pointer target_entity, int entity_id, + int entity_uid) obx_model_property_relation; + int Function(Pointer model, int relation_id, int relation_uid, + int target_id, int target_uid) obx_model_relation; + void Function(Pointer model, int relation_id, int relation_uid) + obx_model_last_relation_id; // object store management Pointer Function() obx_opt; @@ -109,6 +115,13 @@ class _ObjectBoxBindings { obx_box_count; int Function(Pointer box, Pointer is_empty) obx_box_is_empty; + // box relations + int Function(Pointer box, int relation_id, int source_id, int target_id) + obx_box_rel_put, obx_box_rel_remove; + + Pointer Function(Pointer box, int relation_id, int id) + obx_box_rel_get_ids, obx_box_rel_get_backlink_ids; + // query builder obx_query_builder_dart_t obx_qb_create; obx_qb_close_dart_t obx_qb_close; @@ -155,6 +168,18 @@ class _ObjectBoxBindings { obx_qb_order_dart_t obx_qb_order; + Pointer Function(Pointer builder, int property_id) + obx_qb_link_property; + Pointer Function( + Pointer builder, int source_entity_id, int source_property_id) + obx_qb_backlink_property; + Pointer Function(Pointer builder, int relation_id) + obx_qb_link_standalone; + Pointer Function(Pointer builder, int relation_id) + obx_qb_backlink_standalone; + Pointer Function(Pointer builder, int linked_entity_id, + int begin_property_id, int end_property_id) obx_qb_link_time; + // query obx_query_t obx_query_create; obx_query_close_dart_t obx_query_close; @@ -309,6 +334,14 @@ class _ObjectBoxBindings { obx_model_last_entity_id = _fn('obx_model_last_entity_id') .asFunction(); + obx_model_relation = + _fn('obx_model_relation').asFunction(); + obx_model_property_relation = + _fn('obx_model_property_relation') + .asFunction(); + obx_model_last_relation_id = + _fn('obx_model_last_relation_id') + .asFunction(); // object store management obx_opt = _fn('obx_opt').asFunction(); @@ -372,6 +405,15 @@ class _ObjectBoxBindings { obx_box_is_empty = _fn('obx_box_is_empty').asFunction(); + // box relations + obx_box_rel_put = + _fn('obx_box_rel_put').asFunction(); + obx_box_rel_remove = _fn('').asFunction(); + obx_box_rel_get_ids = _fn('').asFunction(); + obx_box_rel_get_backlink_ids = _fn( + 'obx_box_rel_get_backlink_ids') + .asFunction(); + // query builder obx_qb_create = _fn('obx_query_builder').asFunction(); @@ -487,6 +529,20 @@ class _ObjectBoxBindings { obx_qb_order = _fn('obx_qb_order').asFunction(); + obx_qb_link_property = + _fn('obx_qb_link_property').asFunction(); + obx_qb_backlink_property = + _fn('obx_qb_backlink_property') + .asFunction(); + obx_qb_link_standalone = + _fn('obx_qb_link_standalone') + .asFunction(); + obx_qb_backlink_standalone = + _fn('obx_qb_backlink_standalone') + .asFunction(); + obx_qb_link_time = + _fn('obx_qb_link_time').asFunction(); + // query obx_query_create = _fn('obx_query').asFunction(); obx_query_close = diff --git a/lib/src/bindings/signatures.dart b/lib/src/bindings/signatures.dart index 57de43906..99302dcbe 100644 --- a/lib/src/bindings/signatures.dart +++ b/lib/src/bindings/signatures.dart @@ -41,6 +41,19 @@ typedef obx_model_entity_last_property_id_native_t = Int32 Function( Pointer model, Uint32 property_id, Uint64 property_uid); typedef obx_model_last_entity_id_native_t = Int32 Function( Pointer model, Uint32 entity_id, Uint64 entity_uid); +typedef obx_model_property_relation_native_t = Int32 Function( + Pointer model, + Pointer target_entity, + Uint32 entity_id, + Uint64 entity_uid); +typedef obx_model_relation_native_t = Int32 Function( + Pointer model, + Uint32 relation_id, + Uint64 relation_uid, + Uint32 target_id, + Uint64 target_uid); +typedef obx_model_last_relation_id_native_t = Void Function( + Pointer model, Uint32 relation_id, Uint64 relation_uid); // object store management typedef obx_opt_native_t = Pointer Function(); @@ -106,6 +119,15 @@ typedef obx_box_count_native_t = Int32 Function( typedef obx_box_is_empty_native_t = Int32 Function( Pointer box, Pointer is_empty); +typedef obx_box_rel_put_native_t = Int32 Function( + Pointer box, Uint32 relation_id, Uint64 source_id, Uint64 target_id); +typedef obx_box_rel_remove_native_t = Int32 Function( + Pointer box, Uint32 relation_id, Uint64 source_id, Uint64 target_id); +typedef obx_box_rel_get_ids_native_t = Pointer Function( + Pointer box, Uint32 relation_id, Uint64 id); +typedef obx_box_rel_get_backlink_ids_native_t = Pointer Function( + Pointer box, Uint32 relation_id, Uint64 id); + // no typedef for non-functions yet, see https://github.com/dart-lang/language/issues/65 // typedef obx_err = Int32 // typedef Pointer -> char[] @@ -180,6 +202,20 @@ typedef obx_qb_order_native_t = Int32 Function( typedef obx_qb_order_dart_t = int Function( Pointer builder, int property_id, int flags); +typedef obx_qb_link_property_native_t = Pointer Function( + Pointer builder, Uint32 property_id); +typedef obx_qb_backlink_property_native_t = Pointer Function( + Pointer builder, Uint32 source_entity_id, Uint32 source_property_id); +typedef obx_qb_link_standalone_native_t = Pointer Function( + Pointer builder, Uint32 relation_id); +typedef obx_qb_backlink_standalone_native_t = Pointer Function( + Pointer builder, Uint32 relation_id); +typedef obx_qb_link_time_native_t = Pointer Function( + Pointer builder, + Uint32 linked_entity_id, + Uint32 begin_property_id, + Uint32 end_property_id); + // query typedef obx_query_t = Pointer Function(Pointer builder); From de39cbc8a649932deaf349715c2600c8ecb94e98 Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Wed, 21 Oct 2020 06:26:05 +0200 Subject: [PATCH 02/13] add code generation for fields of @Entity --- generator/lib/src/code_chunks.dart | 3 +++ generator/lib/src/entity_resolver.dart | 28 ++++++++++++++++++----- test/entity.dart | 31 ++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index ca208c779..3eb8c2535 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -57,6 +57,9 @@ class CodeChunks { case OBXPropertyType.String: fieldType = "String"; break; + case OBXPropertyType.Relation: + fieldType = 'Relation'; + break; float: case OBXPropertyType.Double: fieldType = "Double"; diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index 7e866c0e6..97833f945 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -2,12 +2,20 @@ import 'dart:async'; import 'dart:convert'; import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; import 'package:build/build.dart'; import 'package:objectbox/objectbox.dart' as obx; import 'package:objectbox/src/bindings/constants.dart'; import 'package:objectbox/src/modelinfo/index.dart'; import 'package:source_gen/source_gen.dart'; +extension RelationCheck on DartType { + String get listSubType { + final name = this.toString(); + return name.substring('List<'.length, name.length - 1); + } +} + /// EntityResolver finds all classes with an @Entity annotation and generates '.objectbox.info' files in build cache. /// It's using some tools from source_gen but defining its custom builder because source_gen expects only dart code. class EntityResolver extends Builder { @@ -17,7 +25,7 @@ class EntityResolver extends Builder { '.dart': [suffix] }; - final _annotationChecker = const TypeChecker.fromRuntime(obx.Entity); + final _entityChecker = const TypeChecker.fromRuntime(obx.Entity); final _propertyChecker = const TypeChecker.fromRuntime(obx.Property); final _idChecker = const TypeChecker.fromRuntime(obx.Id); final _transientChecker = const TypeChecker.fromRuntime(obx.Transient); @@ -30,9 +38,12 @@ class EntityResolver extends Builder { // generate for all entities final entities = >[]; - for (var annotatedEl in libReader.annotatedWith(_annotationChecker)) { + final annotatedWithEntity = libReader.annotatedWith(_entityChecker); + for (var annotatedEl in annotatedWithEntity) { entities.add(generateForAnnotatedElement( - annotatedEl.element, annotatedEl.annotation) + annotatedEl.element, + annotatedEl.annotation, + annotatedWithEntity.map((a) => a.element.name).toSet()) .toMap()); } @@ -43,8 +54,8 @@ class EntityResolver extends Builder { buildStep.inputId.changeExtension(suffix), json); } - ModelEntity generateForAnnotatedElement( - Element elementBare, ConstantReader annotation) { + ModelEntity generateForAnnotatedElement(Element elementBare, + ConstantReader annotation, Set relatableEntityNames) { if (elementBare is! ClassElement) { throw InvalidGenerationSourceError( "in target ${elementBare.name}: annotated element isn't a class"); @@ -112,6 +123,13 @@ class EntityResolver extends Builder { // dart: 8 bytes // ob: 8 bytes fieldType = OBXPropertyType.Double; + } else if (relatableEntityNames.contains(fieldTypeDart.toString()) || + (fieldTypeDart.isDartCoreList && + relatableEntityNames.contains(fieldTypeDart.listSubType))) { + fieldType = OBXPropertyType.Relation; + // TODO remove + // log.warning(relatableEntityNames.join(' ') + + // '? ${fieldTypeDart.toString()} ${fieldTypeDart.isDartCoreList ? fieldTypeDart.listSubType : ""}'); } else { log.warning( " skipping property '${f.name}' in entity '${element.name}', as it has the unsupported type '${fieldTypeDart.toString()}'"); diff --git a/test/entity.dart b/test/entity.dart index aaaeef2d4..3e3146122 100644 --- a/test/entity.dart +++ b/test/entity.dart @@ -63,4 +63,35 @@ class TestEntity { omit = -1; disregard = 1; } + + RelatedEntityA relA; + RelatedEntityB relB; + List listA; + List listB; + + TestEntity.relate({this.relA, this.relB, this.listA, this.listB}); +} + +@Entity() +class RelatedEntityA { + @Id() + int id; + + int tInt; + bool tBool; + RelatedEntityB relB; + + RelatedEntityA({this.id, this.tInt, this.tBool, this.relB}); +} + +@Entity() +class RelatedEntityB { + @Id() + int id; + + String tString; + double tDouble; + RelatedEntityA relA; + + RelatedEntityB({this.id, this.tString, this.tDouble, this.relA}); } From fa3c4866d61a5fa6222c8fb172ce362eba545a19 Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Thu, 22 Oct 2020 06:14:42 +0200 Subject: [PATCH 03/13] add obx_model_property_relation, obx_model_last_relation_id, retiredRelationUids of course all the tests are broken. --- generator/lib/src/code_builder.dart | 7 +++++++ generator/lib/src/entity_resolver.dart | 3 --- lib/objectbox.dart | 1 + lib/src/bindings/bindings.dart | 6 ++++-- lib/src/model.dart | 9 +++++++++ lib/src/modelinfo/iduid.dart | 4 ++++ lib/src/modelinfo/modelinfo.dart | 10 +++++++++- lib/src/query/query.dart | 5 +++++ lib/src/util.dart | 6 ++++++ 9 files changed, 45 insertions(+), 6 deletions(-) diff --git a/generator/lib/src/code_builder.dart b/generator/lib/src/code_builder.dart index 607c688a7..b36366651 100644 --- a/generator/lib/src/code_builder.dart +++ b/generator/lib/src/code_builder.dart @@ -169,6 +169,13 @@ class CodeBuilder extends Builder { entityInModel.removeProperty(p); }); + final relationProps = + entityInModel.properties.where((p) => p.type.isRelation).toList(); + + if (relationProps.isNotEmpty) { + modelInfo.lastRelationId = relationProps.last.id; + } + return entityInModel.id; } } diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index 97833f945..781b7b28e 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -127,9 +127,6 @@ class EntityResolver extends Builder { (fieldTypeDart.isDartCoreList && relatableEntityNames.contains(fieldTypeDart.listSubType))) { fieldType = OBXPropertyType.Relation; - // TODO remove - // log.warning(relatableEntityNames.join(' ') + - // '? ${fieldTypeDart.toString()} ${fieldTypeDart.isDartCoreList ? fieldTypeDart.listSubType : ""}'); } else { log.warning( " skipping property '${f.name}' in entity '${element.name}', as it has the unsupported type '${fieldTypeDart.toString()}'"); diff --git a/lib/objectbox.dart b/lib/objectbox.dart index e3b693bcf..b66ae27f7 100644 --- a/lib/objectbox.dart +++ b/lib/objectbox.dart @@ -12,3 +12,4 @@ export 'src/model.dart'; export 'src/modelinfo/index.dart'; export 'src/query/query.dart'; export 'src/store.dart'; +export 'src/util.dart'; diff --git a/lib/src/bindings/bindings.dart b/lib/src/bindings/bindings.dart index d1b8f3954..5a8fe72e1 100644 --- a/lib/src/bindings/bindings.dart +++ b/lib/src/bindings/bindings.dart @@ -408,8 +408,10 @@ class _ObjectBoxBindings { // box relations obx_box_rel_put = _fn('obx_box_rel_put').asFunction(); - obx_box_rel_remove = _fn('').asFunction(); - obx_box_rel_get_ids = _fn('').asFunction(); + obx_box_rel_remove = + _fn('obx_box_rel_remove').asFunction(); + obx_box_rel_get_ids = + _fn('obx_box_rel_get_ids').asFunction(); obx_box_rel_get_backlink_ids = _fn( 'obx_box_rel_get_backlink_ids') .asFunction(); diff --git a/lib/src/model.dart b/lib/src/model.dart index e6ababf2b..79a92510f 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -6,6 +6,7 @@ import 'bindings/bindings.dart'; import 'bindings/helpers.dart'; import 'common.dart'; import 'modelinfo/index.dart'; +import 'util.dart'; class Model { Pointer _cModel; @@ -21,6 +22,9 @@ class Model { // set last entity id bindings.obx_model_last_entity_id( _cModel, model.lastEntityId.id, model.lastEntityId.uid); + + bindings.obx_model_last_relation_id( + _cModel, model.lastRelationId.id, model.lastRelationId.uid); } catch (e) { bindings.obx_model_free(_cModel); _cModel = null; @@ -59,6 +63,11 @@ class Model { try { _check(bindings.obx_model_property( _cModel, name, prop.type, prop.id.id, prop.id.uid)); + + if (prop.type.isRelation) { + _check(bindings.obx_model_property_relation( + _cModel, name, prop.id.id, prop.id.uid)); + } } finally { free(name); } diff --git a/lib/src/modelinfo/iduid.dart b/lib/src/modelinfo/iduid.dart index 94a2a3ce6..822ef0eba 100644 --- a/lib/src/modelinfo/iduid.dart +++ b/lib/src/modelinfo/iduid.dart @@ -49,6 +49,10 @@ class IdUid { int get uid => _uid; + bool get isEmpty { + return _id == 0 && _uid == 0; + } + @override String toString() => '$_id:$_uid'; } diff --git a/lib/src/modelinfo/modelinfo.dart b/lib/src/modelinfo/modelinfo.dart index 3b42ea895..daa106249 100644 --- a/lib/src/modelinfo/modelinfo.dart +++ b/lib/src/modelinfo/modelinfo.dart @@ -199,7 +199,15 @@ class ModelInfo { } entities = entities.where((p) => p != foundEntity).toList(); retiredEntityUids.add(entity.id.uid); - entity.properties.forEach((prop) => retiredPropertyUids.add(prop.id.uid)); + + entity.properties.forEach((prop) { + final propType = prop.type; + if (propType.isRelation) { + retiredRelationUids.add(prop.id.uid); + } else { + retiredPropertyUids.add(prop.id.uid); + } + }); } int generateUid() { diff --git a/lib/src/query/query.dart b/lib/src/query/query.dart index cb2dc6f8b..76e6a8510 100644 --- a/lib/src/query/query.dart +++ b/lib/src/query/query.dart @@ -225,6 +225,11 @@ class QueryBooleanProperty extends QueryProperty { // Condition operator ==(bool p) => equals(p); // see issue #43 } +class QueryRelationProperty extends QueryProperty { + QueryRelationProperty({int entityId, int propertyId, int obxType}) + : super(entityId, propertyId, obxType); +} + enum ConditionOp { isNull, notNull, diff --git a/lib/src/util.dart b/lib/src/util.dart index 24fd40d7f..c7bc53cbf 100644 --- a/lib/src/util.dart +++ b/lib/src/util.dart @@ -1,2 +1,8 @@ +import 'package:objectbox/src/bindings/constants.dart'; + bool listContains(List list, T item) => list.indexWhere((x) => x == item) != -1; + +extension PropertyType on int { + bool get isRelation => this == OBXPropertyType.Relation; +} From 08fc8c494ee91e12f94f1bf772031d96ed1d8e85 Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Thu, 22 Oct 2020 16:41:32 +0200 Subject: [PATCH 04/13] removed extension overkill --- generator/lib/src/entity_resolver.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index 781b7b28e..a87adfcf1 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -9,11 +9,9 @@ import 'package:objectbox/src/bindings/constants.dart'; import 'package:objectbox/src/modelinfo/index.dart'; import 'package:source_gen/source_gen.dart'; -extension RelationCheck on DartType { - String get listSubType { - final name = this.toString(); - return name.substring('List<'.length, name.length - 1); - } +String listSubType(DartType t) { + final name = t.toString(); + return name.substring('List<'.length, name.length - 1); } /// EntityResolver finds all classes with an @Entity annotation and generates '.objectbox.info' files in build cache. @@ -125,7 +123,7 @@ class EntityResolver extends Builder { fieldType = OBXPropertyType.Double; } else if (relatableEntityNames.contains(fieldTypeDart.toString()) || (fieldTypeDart.isDartCoreList && - relatableEntityNames.contains(fieldTypeDart.listSubType))) { + relatableEntityNames.contains(listSubType(fieldTypeDart)))) { fieldType = OBXPropertyType.Relation; } else { log.warning( From 7336c01bf5a7c9c280ec0a590a3886a32ae9dc31 Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Thu, 22 Oct 2020 16:42:17 +0200 Subject: [PATCH 05/13] moving generator sdk support from 2.5 to 2.6 like the main library --- generator/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generator/pubspec.yaml b/generator/pubspec.yaml index ee1b2949f..14c0e8308 100644 --- a/generator/pubspec.yaml +++ b/generator/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://objectbox.io description: ObjectBox binding code generator - finds annotated entities and adds them to the ObjectBox DB model. environment: - sdk: ">=2.5.0 <3.0.0" + sdk: ">=2.6.0 <3.0.0" dependencies: objectbox: 0.8.0 From ab9ea8b87eb9bab9425fe33cd2e5843830b6783e Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Thu, 22 Oct 2020 17:32:17 +0200 Subject: [PATCH 06/13] * Activated lastRelationId test in integration-test * Use of retiredPropertyUids --- generator/integration-tests/common.dart | 15 ++++++++++----- lib/src/modelinfo/modelentity.dart | 7 ++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/generator/integration-tests/common.dart b/generator/integration-tests/common.dart index 7585f6590..848d5db31 100644 --- a/generator/integration-tests/common.dart +++ b/generator/integration-tests/common.dart @@ -59,13 +59,18 @@ commonModelTests(ModelDefinition defs, ModelInfo jsonModel) { jsonModel.retiredEntityUids); }); - // TODO when indexes are available +// TODO when indexes are available +// TODO see one-liner in the next lastRelationId test // test('lastIndexId', () { // testLastId(defs.model.lastIndexId, defs.model.entities.map((el) => ...), jsonModel.retiredIndexUids); // }); - // TODO when relations are available -// test('lastRelationId', () { -// testLastId(defs.model.lastRelationId, defs.model.entities.map((el) => ...), jsonModel.retiredRelationUids); -// }); + test('lastRelationId', () { + testLastId( + defs.model.lastRelationId, + defs.model.entities + .map((e) => e.properties.where((p) => p.isRelation)) + .fold([], (a, b) => a + b).map((e) => e.id), + jsonModel.retiredRelationUids); + }); } diff --git a/lib/src/modelinfo/modelentity.dart b/lib/src/modelinfo/modelentity.dart index c7240cd44..a885a2c49 100644 --- a/lib/src/modelinfo/modelentity.dart +++ b/lib/src/modelinfo/modelentity.dart @@ -138,8 +138,13 @@ class ModelEntity { throw Exception( "cannot remove property '${prop.name}' with id ${prop.id.toString()}: not found"); } + properties = properties.where((p) => p != foundProp).toList(); - model.retiredPropertyUids.add(prop.id.uid); + if (prop.type.isRelation) { + model.retiredRelationUids.add(prop.id.uid); + } else { + model.retiredPropertyUids.add(prop.id.uid); + } } bool containsUid(int searched) { From fb64e27c0882e306ea4c45ae1d02139c9bbde241 Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Fri, 23 Oct 2020 06:47:52 +0200 Subject: [PATCH 07/13] simplified even more --- generator/lib/src/entity_resolver.dart | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index a87adfcf1..16c8b6a03 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -9,11 +9,6 @@ import 'package:objectbox/src/bindings/constants.dart'; import 'package:objectbox/src/modelinfo/index.dart'; import 'package:source_gen/source_gen.dart'; -String listSubType(DartType t) { - final name = t.toString(); - return name.substring('List<'.length, name.length - 1); -} - /// EntityResolver finds all classes with an @Entity annotation and generates '.objectbox.info' files in build cache. /// It's using some tools from source_gen but defining its custom builder because source_gen expects only dart code. class EntityResolver extends Builder { @@ -105,7 +100,8 @@ class EntityResolver extends Builder { } if (fieldType == null) { - var fieldTypeDart = f.type; + final fieldTypeDart = f.type; + final fieldTypeDartString = f.type.toString(); if (fieldTypeDart.isDartCoreInt) { // dart: 8 bytes @@ -121,9 +117,8 @@ class EntityResolver extends Builder { // dart: 8 bytes // ob: 8 bytes fieldType = OBXPropertyType.Double; - } else if (relatableEntityNames.contains(fieldTypeDart.toString()) || - (fieldTypeDart.isDartCoreList && - relatableEntityNames.contains(listSubType(fieldTypeDart)))) { + } else if (relatableEntityNames + .any((e) => fieldTypeDartString.contains(e))) { fieldType = OBXPropertyType.Relation; } else { log.warning( From 556064da34b616cc4dc43927dab83cfbc2ebc156 Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Fri, 23 Oct 2020 15:09:55 +0200 Subject: [PATCH 08/13] moved the last property id, that was getting overwritten --- generator/lib/src/code_builder.dart | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/generator/lib/src/code_builder.dart b/generator/lib/src/code_builder.dart index b36366651..a93771e3b 100644 --- a/generator/lib/src/code_builder.dart +++ b/generator/lib/src/code_builder.dart @@ -82,6 +82,7 @@ class CodeBuilder extends Builder { // merge existing model and annotated model that was just read, then write new final model to file merge(model, entities); + assignLastPropertyIds(model); model.validate(); // write model info @@ -93,6 +94,18 @@ class CodeBuilder extends Builder { return model; } + void assignLastPropertyIds(ModelInfo model) { + model.entities.forEach((e) { + e.properties.forEach((p) { + if (p.type.isRelation) { + model.lastRelationId = p.id; + // }else if (p.type.isIndexer) { + // model.lastIndexId = p.id; + } + }); + }); + } + void updateCode( ModelInfo model, List infoFiles, BuildStep buildStep) async { // transform '/lib/path/entity.objectbox.info' to 'path/entity.dart' @@ -169,13 +182,6 @@ class CodeBuilder extends Builder { entityInModel.removeProperty(p); }); - final relationProps = - entityInModel.properties.where((p) => p.type.isRelation).toList(); - - if (relationProps.isNotEmpty) { - modelInfo.lastRelationId = relationProps.last.id; - } - return entityInModel.id; } } From 00e1aa8ea8da54b1d5c439f857f178d4f4d9d015 Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Fri, 23 Oct 2020 16:49:21 +0200 Subject: [PATCH 09/13] Improved detecting one-to-many or many-to-many relations --- generator/lib/src/entity_resolver.dart | 27 +++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index 16c8b6a03..d33f2d326 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -32,11 +32,12 @@ class EntityResolver extends Builder { // generate for all entities final entities = >[]; final annotatedWithEntity = libReader.annotatedWith(_entityChecker); + final entityNames = annotatedWithEntity.map((a) => a.element.name).toSet(); + final entityNamesAsList = + annotatedWithEntity.map((a) => 'List<${a.element.name}>').toSet(); for (var annotatedEl in annotatedWithEntity) { - entities.add(generateForAnnotatedElement( - annotatedEl.element, - annotatedEl.annotation, - annotatedWithEntity.map((a) => a.element.name).toSet()) + entities.add(generateForAnnotatedElement(annotatedEl.element, + annotatedEl.annotation, entityNames, entityNamesAsList) .toMap()); } @@ -47,8 +48,11 @@ class EntityResolver extends Builder { buildStep.inputId.changeExtension(suffix), json); } - ModelEntity generateForAnnotatedElement(Element elementBare, - ConstantReader annotation, Set relatableEntityNames) { + ModelEntity generateForAnnotatedElement( + Element elementBare, + ConstantReader annotation, + Set relatableEntityNames, + Set relatableEntityNamesAsList) { if (elementBare is! ClassElement) { throw InvalidGenerationSourceError( "in target ${elementBare.name}: annotated element isn't a class"); @@ -101,7 +105,7 @@ class EntityResolver extends Builder { if (fieldType == null) { final fieldTypeDart = f.type; - final fieldTypeDartString = f.type.toString(); + final dartTypeString = f.type.toString().replaceAll('*', ''); if (fieldTypeDart.isDartCoreInt) { // dart: 8 bytes @@ -117,8 +121,8 @@ class EntityResolver extends Builder { // dart: 8 bytes // ob: 8 bytes fieldType = OBXPropertyType.Double; - } else if (relatableEntityNames - .any((e) => fieldTypeDartString.contains(e))) { + } else if (areRelated( + dartTypeString, relatableEntityNames, relatableEntityNamesAsList)) { fieldType = OBXPropertyType.Relation; } else { log.warning( @@ -145,4 +149,9 @@ class EntityResolver extends Builder { return readEntity; } + + bool areRelated(String typeString, Set typeCollection, + Set listTypeCollection) => + typeCollection.contains(typeString) || + listTypeCollection.contains(typeString); } From 06939ad308ef57a9a468d63a307e303d9c28446b Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Fri, 23 Oct 2020 18:34:10 +0200 Subject: [PATCH 10/13] add setup for passing relational dart type --- generator/lib/src/code_builder.dart | 1 + generator/lib/src/code_chunks.dart | 6 +++++- generator/lib/src/entity_resolver.dart | 11 ++++++++--- lib/integration_test.dart | 2 +- lib/src/modelinfo/modelentity.dart | 2 +- lib/src/modelinfo/modelproperty.dart | 7 +++++-- lib/src/query/query.dart | 2 +- 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/generator/lib/src/code_builder.dart b/generator/lib/src/code_builder.dart index a93771e3b..54da77285 100644 --- a/generator/lib/src/code_builder.dart +++ b/generator/lib/src/code_builder.dart @@ -153,6 +153,7 @@ class CodeBuilder extends Builder { propInModel.name = prop.name; propInModel.type = prop.type; propInModel.flags = prop.flags; + propInModel.relationDartType = prop.relationDartType; } } diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index 3eb8c2535..03d52cccf 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -83,8 +83,12 @@ class CodeChunks { "Unsupported property type (${prop.type}): ${entity.name}.${name}"); } + final relationTypeGenericParam = prop.type == OBXPropertyType.Relation + ? '<${prop.relationDartType}>' + : ''; + ret.add(""" - static final ${name} = Query${fieldType}Property(entityId:${entity.id.id}, propertyId:${prop.id.id}, obxType:${prop.type}); + static final ${name} = Query${fieldType}Property$relationTypeGenericParam(entityId:${entity.id.id}, propertyId:${prop.id.id}, obxType:${prop.type}); """); } return ret.join(); diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index d33f2d326..5e5f62448 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -78,6 +78,7 @@ class EntityResolver extends Builder { int fieldType, flags = 0; int propUid; + final dartTypeString = f.type.toString().replaceAll('*', ''); if (_idChecker.hasAnnotationOfExact(f)) { if (hasIdProperty) { @@ -105,7 +106,6 @@ class EntityResolver extends Builder { if (fieldType == null) { final fieldTypeDart = f.type; - final dartTypeString = f.type.toString().replaceAll('*', ''); if (fieldTypeDart.isDartCoreInt) { // dart: 8 bytes @@ -132,8 +132,13 @@ class EntityResolver extends Builder { } // create property (do not use readEntity.createProperty in order to avoid generating new ids) - final prop = - ModelProperty(IdUid.empty(), f.name, fieldType, flags, readEntity); + final prop = ModelProperty( + IdUid.empty(), + f.name, + fieldType == OBXPropertyType.Relation ? dartTypeString : null, + fieldType, + flags, + readEntity); if (propUid != null) prop.id.uid = propUid; readEntity.properties.add(prop); diff --git a/lib/integration_test.dart b/lib/integration_test.dart index 5442ffdbc..2552353f4 100644 --- a/lib/integration_test.dart +++ b/lib/integration_test.dart @@ -20,7 +20,7 @@ class IntegrationTest { // create a model with a single entity and a single property final modelInfo = ModelInfo.createDefault(); final property = ModelProperty( - IdUid(1, int64_max - 1), 'id', OBXPropertyType.Long, 0, null); + IdUid(1, int64_max - 1), 'id', null, OBXPropertyType.Long, 0, null); final entity = ModelEntity(IdUid(1, int64_max), null, 'entity', [], modelInfo); property.entity = entity; diff --git a/lib/src/modelinfo/modelentity.dart b/lib/src/modelinfo/modelentity.dart index a885a2c49..706f60fc0 100644 --- a/lib/src/modelinfo/modelentity.dart +++ b/lib/src/modelinfo/modelentity.dart @@ -118,7 +118,7 @@ class ModelEntity { } final uniqueUid = uid == 0 ? model.generateUid() : uid; - var property = ModelProperty(IdUid(id, uniqueUid), name, 0, 0, this); + var property = ModelProperty(IdUid(id, uniqueUid), name, null, 0, 0, this); properties.add(property); lastPropertyId = property.id; return property; diff --git a/lib/src/modelinfo/modelproperty.dart b/lib/src/modelinfo/modelproperty.dart index 717014750..eabaf21a9 100644 --- a/lib/src/modelinfo/modelproperty.dart +++ b/lib/src/modelinfo/modelproperty.dart @@ -4,11 +4,12 @@ import 'iduid.dart'; /// ModelProperty describes a single property of an entity, i.e. its id, name, type and flags. class ModelProperty { IdUid id; - String name; + String name, relationDartType; int type, flags; ModelEntity entity; - ModelProperty(this.id, this.name, this.type, this.flags, this.entity) { + ModelProperty(this.id, this.name, this.relationDartType, this.type, + this.flags, this.entity) { validate(); } @@ -17,6 +18,7 @@ class ModelProperty { id = IdUid.fromString(data['id']); name = data['name']; type = data['type']; + relationDartType = data['relationDartType']; flags = data.containsKey('flags') ? data['flags'] : 0; if (check) validate(); } @@ -35,6 +37,7 @@ class ModelProperty { ret['id'] = id.toString(); ret['name'] = name; ret['type'] = type; + if (relationDartType != null) ret['relationDartType'] = relationDartType; if (flags != 0) ret['flags'] = flags; return ret; } diff --git a/lib/src/query/query.dart b/lib/src/query/query.dart index 76e6a8510..6e1287134 100644 --- a/lib/src/query/query.dart +++ b/lib/src/query/query.dart @@ -225,7 +225,7 @@ class QueryBooleanProperty extends QueryProperty { // Condition operator ==(bool p) => equals(p); // see issue #43 } -class QueryRelationProperty extends QueryProperty { +class QueryRelationProperty extends QueryProperty { QueryRelationProperty({int entityId, int propertyId, int obxType}) : super(entityId, propertyId, obxType); } From 58cb56a31db8053fed16417db67edc64570e5a8b Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Sat, 24 Oct 2020 08:58:12 +0200 Subject: [PATCH 11/13] one to one: add relIndexId on the property many to many: add retiredRelationUids on removeEntity many to many: lastRelationId one to one: lastIndexId obx_store_debug_flags --- generator/lib/src/code_builder.dart | 71 ++++++++++++++++++++++++-- generator/lib/src/code_chunks.dart | 5 +- generator/lib/src/entity_resolver.dart | 25 +++++---- lib/integration_test.dart | 2 +- lib/src/bindings/bindings.dart | 16 +++++- lib/src/bindings/constants.dart | 8 +++ lib/src/bindings/signatures.dart | 8 ++- lib/src/model.dart | 38 ++++++++++---- lib/src/modelinfo/modelentity.dart | 2 +- lib/src/modelinfo/modelinfo.dart | 13 +++-- lib/src/modelinfo/modelproperty.dart | 55 ++++++++++++++++++-- lib/src/store.dart | 9 +++- test/entity.dart | 11 +--- 13 files changed, 211 insertions(+), 52 deletions(-) diff --git a/generator/lib/src/code_builder.dart b/generator/lib/src/code_builder.dart index 54da77285..8e8611bc5 100644 --- a/generator/lib/src/code_builder.dart +++ b/generator/lib/src/code_builder.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; import 'dart:convert'; import 'package:build/build.dart'; import 'package:glob/glob.dart'; @@ -97,10 +98,12 @@ class CodeBuilder extends Builder { void assignLastPropertyIds(ModelInfo model) { model.entities.forEach((e) { e.properties.forEach((p) { - if (p.type.isRelation) { - model.lastRelationId = p.id; - // }else if (p.type.isIndexer) { - // model.lastIndexId = p.id; + // if (p.isIndexer) { + // model.lastIndexId = p.id; }else + if (p.isOneToOne) { + model.lastIndexId = p.relIndexId; + } else if (p.isManyToMany) { + model.lastRelationId = p.relationId; } }); }); @@ -144,6 +147,17 @@ class CodeBuilder extends Builder { entities.forEach((entity) => mergeEntity(model, entity)); } + int generateUid(int seed, Set prohibited) { + final rng = Random(seed + DateTime.now().millisecondsSinceEpoch); + for (int i = 0; i < 1000; i++) { + var uid = rng.nextInt(1 << 32); + uid |= rng.nextInt(1 << 32) << 32; + uid &= ~(1 << 63); + if (!prohibited.contains(uid)) return uid; + } + return 0; + } + void mergeProperty(ModelEntity entity, ModelProperty prop) { final propInModel = entity.findSameProperty(prop); if (propInModel == null) { @@ -153,7 +167,10 @@ class CodeBuilder extends Builder { propInModel.name = prop.name; propInModel.type = prop.type; propInModel.flags = prop.flags; - propInModel.relationDartType = prop.relationDartType; + if (prop.type.isRelation) { + propInModel.targetEntityName = prop.targetEntityName; + propInModel.relIndexId = prop.relIndexId; + } } } @@ -174,6 +191,50 @@ class CodeBuilder extends Builder { // here, the entity was found already and entityInModel and readEntity might differ, i.e. conflicts need to be resolved, so merge all properties first entity.properties.forEach((p) => mergeProperty(entityInModel, p)); + // one to one relation: get the highest relIndexId.id + // TODO Ask: Do we want to repair contiguous ids, if one prop is removed in the middle? + var relIndexIdCounter = entityInModel.properties + .where((p) => p.isOneToOne && p.relIndexId != null) + .map((p) => p.relIndexId.id) + .fold(1, (a, b) => a < b ? b : a); + + // Prohibit reuse of indexUids + final avoidSameUidSet = Set()..addAll(modelInfo.retiredIndexUids); + + // one to one relation + entityInModel.properties.where((p) => p.isOneToOne).forEach((p) { + if (p.relIndexId == null) { + p.relIndexId = IdUid.empty(); + } + if (p.relIndexId.isEmpty) { + p.relIndexId.id = relIndexIdCounter++; + p.relIndexId.uid = generateUid(entityInModel.id.uid, avoidSameUidSet); + avoidSameUidSet.add(p.relIndexId.uid); + } + }); + + // many to many relation + var relationIdCounter = entityInModel.properties + .where((p) => p.isManyToMany && p.relationId != null) + .map((p) => p.relationId.id) + .fold(1, (a, b) => a < b ? b : a); + + final targetEntityMap = {}; + final mapEntityNameToId = + modelInfo.entities.forEach((e) => targetEntityMap[e.name] = e.id); + entityInModel.properties.where((p) => p.isManyToMany).forEach((p) { + if (p.relationId == null) { + p.relationId = IdUid.empty(); + } + if (p.relationId.isEmpty) { + p.relationId.id = relationIdCounter++; + p.relationId.uid = generateUid(entityInModel.id.uid, avoidSameUidSet); + avoidSameUidSet.add(p.relationId.uid); + } + + p.targetEntityId = targetEntityMap[p.targetEntityName]; + }); + // then remove all properties not present anymore in readEntity entityInModel.properties .where((p) => entity.findSameProperty(p) == null) diff --git a/generator/lib/src/code_chunks.dart b/generator/lib/src/code_chunks.dart index 03d52cccf..293921cf2 100644 --- a/generator/lib/src/code_chunks.dart +++ b/generator/lib/src/code_chunks.dart @@ -83,9 +83,8 @@ class CodeChunks { "Unsupported property type (${prop.type}): ${entity.name}.${name}"); } - final relationTypeGenericParam = prop.type == OBXPropertyType.Relation - ? '<${prop.relationDartType}>' - : ''; + final relationTypeGenericParam = + prop.type == OBXPropertyType.Relation ? '<${prop.targetEntityName}>' : ''; ret.add(""" static final ${name} = Query${fieldType}Property$relationTypeGenericParam(entityId:${entity.id.id}, propertyId:${prop.id.id}, obxType:${prop.type}); diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index 5e5f62448..50bf489fe 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -132,13 +132,20 @@ class EntityResolver extends Builder { } // create property (do not use readEntity.createProperty in order to avoid generating new ids) + final isRelation = fieldType == OBXPropertyType.Relation; + + // setup relations + final isOneToOne = + isRelation && areRelated(dartTypeString, relatableEntityNames); + final isManyToMany = + isRelation && areRelated(dartTypeString, relatableEntityNamesAsList); final prop = ModelProperty( - IdUid.empty(), - f.name, - fieldType == OBXPropertyType.Relation ? dartTypeString : null, - fieldType, - flags, - readEntity); + IdUid.empty(), f.name, fieldType, flags, readEntity, + targetEntityName: isRelation ? dartTypeString : null, + relIndexId: isOneToOne ? IdUid.empty() : null, + relationId: isManyToMany ? IdUid.empty() : null, + targetEntityId: isManyToMany ? IdUid.empty() : null); + if (propUid != null) prop.id.uid = propUid; readEntity.properties.add(prop); @@ -155,8 +162,6 @@ class EntityResolver extends Builder { return readEntity; } - bool areRelated(String typeString, Set typeCollection, - Set listTypeCollection) => - typeCollection.contains(typeString) || - listTypeCollection.contains(typeString); + bool areRelated(String typeString, Set s1, [Set s2]) => + s1.contains(typeString) || (s2 != null && s2.contains(typeString)); } diff --git a/lib/integration_test.dart b/lib/integration_test.dart index 2552353f4..5442ffdbc 100644 --- a/lib/integration_test.dart +++ b/lib/integration_test.dart @@ -20,7 +20,7 @@ class IntegrationTest { // create a model with a single entity and a single property final modelInfo = ModelInfo.createDefault(); final property = ModelProperty( - IdUid(1, int64_max - 1), 'id', null, OBXPropertyType.Long, 0, null); + IdUid(1, int64_max - 1), 'id', OBXPropertyType.Long, 0, null); final entity = ModelEntity(IdUid(1, int64_max), null, 'entity', [], modelInfo); property.entity = entity; diff --git a/lib/src/bindings/bindings.dart b/lib/src/bindings/bindings.dart index 5a8fe72e1..1dacf6bab 100644 --- a/lib/src/bindings/bindings.dart +++ b/lib/src/bindings/bindings.dart @@ -46,10 +46,14 @@ class _ObjectBoxBindings { int Function(Pointer model, Pointer name, int type, int property_id, int property_uid) obx_model_property; int Function(Pointer model, int flags) obx_model_property_flags; + int Function(Pointer model, int index_id, int index_uid) + obx_model_property_index_id; int Function(Pointer model, int property_id, int property_uid) obx_model_entity_last_property_id; - int Function(Pointer model, int entity_id, int entity_uid) + void Function(Pointer model, int entity_id, int entity_uid) obx_model_last_entity_id; + void Function(Pointer model, int entity_id, int index_uid) + obx_model_last_index_id; int Function(Pointer model, Pointer target_entity, int entity_id, int entity_uid) obx_model_property_relation; int Function(Pointer model, int relation_id, int relation_uid, @@ -67,6 +71,7 @@ class _ObjectBoxBindings { void Function(Pointer opt) obx_opt_free; Pointer Function(Pointer opt) obx_store_open; int Function(Pointer store) obx_store_close; + int Function(Pointer store, int flags) obx_store_debug_flags; // transactions Pointer Function(Pointer store) obx_txn_write; @@ -324,6 +329,9 @@ class _ObjectBoxBindings { _fn('obx_model_entity').asFunction(); obx_model_property = _fn('obx_model_property').asFunction(); + obx_model_property_index_id = + _fn('obx_model_property_index_id') + .asFunction(); obx_model_property_flags = _fn('obx_model_property_flags') .asFunction(); @@ -334,6 +342,9 @@ class _ObjectBoxBindings { obx_model_last_entity_id = _fn('obx_model_last_entity_id') .asFunction(); + obx_model_last_index_id = + _fn('obx_model_last_index_id') + .asFunction(); obx_model_relation = _fn('obx_model_relation').asFunction(); obx_model_property_relation = @@ -359,6 +370,9 @@ class _ObjectBoxBindings { _fn('obx_store_open').asFunction(); obx_store_close = _fn('obx_store_close').asFunction(); + obx_store_debug_flags = + _fn('obx_store_debug_flags') + .asFunction(); // transactions obx_txn_write = _fn('obx_txn_write').asFunction(); diff --git a/lib/src/bindings/constants.dart b/lib/src/bindings/constants.dart index 3698d76dc..769f49406 100644 --- a/lib/src/bindings/constants.dart +++ b/lib/src/bindings/constants.dart @@ -83,3 +83,11 @@ class OBXError { /// A requested schema object (e.g. entity or property) was not found in the schema static const int OBX_ERROR_SCHEMA_OBJECT_NOT_FOUND = 10503; } + +class OBXDebugFlags { + static const int LOG_TRANSACTIONS_READ = 1; + static const int LOG_TRANSACTIONS_WRITE = 2; + static const int LOG_QUERIES = 4; + static const int LOG_QUERY_PARAMETERS = 8; + static const int LOG_ASYNC_QUEUE = 16; +} diff --git a/lib/src/bindings/signatures.dart b/lib/src/bindings/signatures.dart index 99302dcbe..3b5e01e2a 100644 --- a/lib/src/bindings/signatures.dart +++ b/lib/src/bindings/signatures.dart @@ -35,12 +35,16 @@ typedef obx_model_entity_native_t = Int32 Function(Pointer model, Pointer name, Uint32 entity_id, Uint64 entity_uid); typedef obx_model_property_native_t = Int32 Function(Pointer model, Pointer name, Uint32 type, Uint32 property_id, Uint64 property_uid); +typedef obx_model_property_index_id_native_t = Int32 Function( + Pointer model, Uint32 index_id, Uint64 index_uid); typedef obx_model_property_flags_native_t = Int32 Function( Pointer model, Uint32 flags); typedef obx_model_entity_last_property_id_native_t = Int32 Function( Pointer model, Uint32 property_id, Uint64 property_uid); -typedef obx_model_last_entity_id_native_t = Int32 Function( +typedef obx_model_last_entity_id_native_t = Void Function( Pointer model, Uint32 entity_id, Uint64 entity_uid); +typedef obx_model_last_index_id_native_t = Void Function( + Pointer model, Uint32 entity_id, Uint64 index_uid); typedef obx_model_property_relation_native_t = Int32 Function( Pointer model, Pointer target_entity, @@ -69,6 +73,8 @@ typedef obx_opt_model_native_t = Int32 Function( Pointer opt, Pointer model); typedef obx_store_open_native_t = Pointer Function(Pointer opt); typedef obx_store_close_native_t = Int32 Function(Pointer store); +typedef obx_store_debug_flags_native_t = Int32 Function( + Pointer store, Int32 flags); // transactions typedef obx_txn_write_native_t = Pointer Function(Pointer store); diff --git a/lib/src/model.dart b/lib/src/model.dart index 79a92510f..0a4c98c60 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -23,8 +23,15 @@ class Model { bindings.obx_model_last_entity_id( _cModel, model.lastEntityId.id, model.lastEntityId.uid); - bindings.obx_model_last_relation_id( - _cModel, model.lastRelationId.id, model.lastRelationId.uid); + if (model.lastRelationId != null) { + bindings.obx_model_last_relation_id( + _cModel, model.lastRelationId.id, model.lastRelationId.uid); + } + + if (model.lastIndexId != null) { + bindings.obx_model_last_index_id( + _cModel, model.lastIndexId.id, model.lastIndexId.uid); + } } catch (e) { bindings.obx_model_free(_cModel); _cModel = null; @@ -59,21 +66,32 @@ class Model { } void addProperty(ModelProperty prop) { - var name = Utf8.toUtf8(prop.name); + final name = Utf8.toUtf8(prop.name); try { _check(bindings.obx_model_property( _cModel, name, prop.type, prop.id.id, prop.id.uid)); - if (prop.type.isRelation) { - _check(bindings.obx_model_property_relation( - _cModel, name, prop.id.id, prop.id.uid)); + if (prop.flags != 0) { + _check(bindings.obx_model_property_flags(_cModel, prop.flags)); + } + + if (prop.isOneToOne) { + final targetEntityName = Utf8.toUtf8(prop.targetEntityName); + + _check(bindings.obx_model_property_relation(_cModel, targetEntityName, + prop.relIndexId.id, prop.relIndexId.uid)); + + free(targetEntityName); + } else if (prop.isManyToMany) { + _check(bindings.obx_model_relation( + _cModel, + prop.relationId.id, + prop.relationId.uid, + prop.targetEntityId.id, + prop.targetEntityId.uid)); } } finally { free(name); } - - if (prop.flags != 0) { - _check(bindings.obx_model_property_flags(_cModel, prop.flags)); - } } } diff --git a/lib/src/modelinfo/modelentity.dart b/lib/src/modelinfo/modelentity.dart index 706f60fc0..a885a2c49 100644 --- a/lib/src/modelinfo/modelentity.dart +++ b/lib/src/modelinfo/modelentity.dart @@ -118,7 +118,7 @@ class ModelEntity { } final uniqueUid = uid == 0 ? model.generateUid() : uid; - var property = ModelProperty(IdUid(id, uniqueUid), name, null, 0, 0, this); + var property = ModelProperty(IdUid(id, uniqueUid), name, 0, 0, this); properties.add(property); lastPropertyId = property.id; return property; diff --git a/lib/src/modelinfo/modelinfo.dart b/lib/src/modelinfo/modelinfo.dart index daa106249..2a5bc0a99 100644 --- a/lib/src/modelinfo/modelinfo.dart +++ b/lib/src/modelinfo/modelinfo.dart @@ -201,12 +201,15 @@ class ModelInfo { retiredEntityUids.add(entity.id.uid); entity.properties.forEach((prop) { - final propType = prop.type; - if (propType.isRelation) { - retiredRelationUids.add(prop.id.uid); - } else { - retiredPropertyUids.add(prop.id.uid); + if (prop.isRelation) { + if (prop.targetEntityName.contains('<')) { + retiredRelationUids.add(prop.relationId.uid); + } else { + retiredIndexUids.add(prop.relIndexId.uid); + } } + // if (prop.isIndexer) { retiredIndexUids.add(prop.indexId.uid); } + retiredPropertyUids.add(prop.id.uid); }); } diff --git a/lib/src/modelinfo/modelproperty.dart b/lib/src/modelinfo/modelproperty.dart index eabaf21a9..cd2b7ec58 100644 --- a/lib/src/modelinfo/modelproperty.dart +++ b/lib/src/modelinfo/modelproperty.dart @@ -1,15 +1,22 @@ +import 'package:objectbox/src/bindings/constants.dart'; + import 'modelentity.dart'; import 'iduid.dart'; /// ModelProperty describes a single property of an entity, i.e. its id, name, type and flags. class ModelProperty { IdUid id; - String name, relationDartType; + String name; int type, flags; ModelEntity entity; + String targetEntityName; + IdUid relIndexId, relationId, targetEntityId; - ModelProperty(this.id, this.name, this.relationDartType, this.type, - this.flags, this.entity) { + ModelProperty(this.id, this.name, this.type, this.flags, this.entity, + {this.targetEntityName, + this.relIndexId, + this.relationId, + this.targetEntityId}) { validate(); } @@ -18,8 +25,23 @@ class ModelProperty { id = IdUid.fromString(data['id']); name = data['name']; type = data['type']; - relationDartType = data['relationDartType']; flags = data.containsKey('flags') ? data['flags'] : 0; + + // relations + targetEntityName = + data.containsKey('targetEntityName') ? data['targetEntityName'] : null; + relIndexId = data.containsKey('relIndexId') + ? IdUid.fromString(data['relIndexId']) + : null; + + relationId = data.containsKey('relationId') + ? IdUid.fromString(data['relationId']) + : null; + + targetEntityId = data.containsKey('targetEntityId') + ? IdUid.fromString(data['targetEntityId']) + : null; + if (check) validate(); } @@ -37,7 +59,18 @@ class ModelProperty { ret['id'] = id.toString(); ret['name'] = name; ret['type'] = type; - if (relationDartType != null) ret['relationDartType'] = relationDartType; + if (targetEntityName != null) { + ret['targetEntityName'] = targetEntityName; + } + if (relIndexId != null) { + ret['relIndexId'] = relIndexId.toString(); + } + if (targetEntityId != null) { + ret['targetEntityId'] = targetEntityId.toString(); + } + if (relationId != null) { + ret['relationId'] = relationId.toString(); + } if (flags != 0) ret['flags'] = flags; return ret; } @@ -45,4 +78,16 @@ class ModelProperty { bool containsUid(int searched) { return id.uid == searched; } + + bool get isRelation => OBXPropertyType.Relation == type; + bool get isOneToOne => isRelation && !targetEntityName.contains('<'); + bool get isManyToMany => isRelation && targetEntityName.contains('<'); + // bool get isIndexer => [ + // OBXPropertyFlag.INDEXED, + // OBXPropertyFlag.UNIQUE, + // OBXPropertyFlag.INDEX_PARTIAL_SKIP_NULL, + // OBXPropertyFlag.INDEX_PARTIAL_SKIP_ZERO, + // OBXPropertyFlag.INDEX_HASH, + // OBXPropertyFlag.INDEX_HASH64 + // ].any((i) => (i & flags) == i); } diff --git a/lib/src/store.dart b/lib/src/store.dart index 95ef13bcf..deb710bc2 100644 --- a/lib/src/store.dart +++ b/lib/src/store.dart @@ -34,7 +34,11 @@ class Store { /// /// See our examples for more details. Store(this.defs, - {String directory, int maxDBSizeInKB, int fileMode, int maxReaders}) { + {String directory, + int maxDBSizeInKB, + int fileMode, + int maxReaders, + int debugFlags = 0}) { var model = Model(defs.model); var opt = bindings.obx_opt(); @@ -64,6 +68,9 @@ class Store { rethrow; } _cStore = bindings.obx_store_open(opt); + if (debugFlags != 0) { + checkObx(bindings.obx_store_debug_flags(_cStore, debugFlags)); + } try { checkObxPtr(_cStore, 'failed to create store'); diff --git a/test/entity.dart b/test/entity.dart index 3e3146122..e79a17ea2 100644 --- a/test/entity.dart +++ b/test/entity.dart @@ -63,13 +63,6 @@ class TestEntity { omit = -1; disregard = 1; } - - RelatedEntityA relA; - RelatedEntityB relB; - List listA; - List listB; - - TestEntity.relate({this.relA, this.relB, this.listA, this.listB}); } @Entity() @@ -91,7 +84,7 @@ class RelatedEntityB { String tString; double tDouble; - RelatedEntityA relA; + List listTestEntity; - RelatedEntityB({this.id, this.tString, this.tDouble, this.relA}); + RelatedEntityB({this.id, this.tString, this.tDouble, this.listTestEntity}); } From cfa5c0edb7520848b9f6a807af5c500b6abb0df9 Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Mon, 26 Oct 2020 15:28:06 +0100 Subject: [PATCH 12/13] renamed bad names --- generator/lib/src/code_builder.dart | 12 ++++++------ generator/lib/src/entity_resolver.dart | 10 +++++----- lib/src/model.dart | 5 ++--- lib/src/modelinfo/modelproperty.dart | 4 ++-- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/generator/lib/src/code_builder.dart b/generator/lib/src/code_builder.dart index 8e8611bc5..9359aa235 100644 --- a/generator/lib/src/code_builder.dart +++ b/generator/lib/src/code_builder.dart @@ -100,9 +100,9 @@ class CodeBuilder extends Builder { e.properties.forEach((p) { // if (p.isIndexer) { // model.lastIndexId = p.id; }else - if (p.isOneToOne) { + if (p.isToOne) { model.lastIndexId = p.relIndexId; - } else if (p.isManyToMany) { + } else if (p.isToMany) { model.lastRelationId = p.relationId; } }); @@ -194,7 +194,7 @@ class CodeBuilder extends Builder { // one to one relation: get the highest relIndexId.id // TODO Ask: Do we want to repair contiguous ids, if one prop is removed in the middle? var relIndexIdCounter = entityInModel.properties - .where((p) => p.isOneToOne && p.relIndexId != null) + .where((p) => p.isToOne && p.relIndexId != null) .map((p) => p.relIndexId.id) .fold(1, (a, b) => a < b ? b : a); @@ -202,7 +202,7 @@ class CodeBuilder extends Builder { final avoidSameUidSet = Set()..addAll(modelInfo.retiredIndexUids); // one to one relation - entityInModel.properties.where((p) => p.isOneToOne).forEach((p) { + entityInModel.properties.where((p) => p.isToOne).forEach((p) { if (p.relIndexId == null) { p.relIndexId = IdUid.empty(); } @@ -215,14 +215,14 @@ class CodeBuilder extends Builder { // many to many relation var relationIdCounter = entityInModel.properties - .where((p) => p.isManyToMany && p.relationId != null) + .where((p) => p.isToMany && p.relationId != null) .map((p) => p.relationId.id) .fold(1, (a, b) => a < b ? b : a); final targetEntityMap = {}; final mapEntityNameToId = modelInfo.entities.forEach((e) => targetEntityMap[e.name] = e.id); - entityInModel.properties.where((p) => p.isManyToMany).forEach((p) { + entityInModel.properties.where((p) => p.isToMany).forEach((p) { if (p.relationId == null) { p.relationId = IdUid.empty(); } diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index 50bf489fe..fc0d5358a 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -135,16 +135,16 @@ class EntityResolver extends Builder { final isRelation = fieldType == OBXPropertyType.Relation; // setup relations - final isOneToOne = + final isToOne = isRelation && areRelated(dartTypeString, relatableEntityNames); - final isManyToMany = + final isToMany = isRelation && areRelated(dartTypeString, relatableEntityNamesAsList); final prop = ModelProperty( IdUid.empty(), f.name, fieldType, flags, readEntity, targetEntityName: isRelation ? dartTypeString : null, - relIndexId: isOneToOne ? IdUid.empty() : null, - relationId: isManyToMany ? IdUid.empty() : null, - targetEntityId: isManyToMany ? IdUid.empty() : null); + relIndexId: isToOne ? IdUid.empty() : null, + relationId: isToMany ? IdUid.empty() : null, + targetEntityId: isToMany ? IdUid.empty() : null); if (propUid != null) prop.id.uid = propUid; readEntity.properties.add(prop); diff --git a/lib/src/model.dart b/lib/src/model.dart index 0a4c98c60..3fc595345 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -6,7 +6,6 @@ import 'bindings/bindings.dart'; import 'bindings/helpers.dart'; import 'common.dart'; import 'modelinfo/index.dart'; -import 'util.dart'; class Model { Pointer _cModel; @@ -75,14 +74,14 @@ class Model { _check(bindings.obx_model_property_flags(_cModel, prop.flags)); } - if (prop.isOneToOne) { + if (prop.isToOne) { final targetEntityName = Utf8.toUtf8(prop.targetEntityName); _check(bindings.obx_model_property_relation(_cModel, targetEntityName, prop.relIndexId.id, prop.relIndexId.uid)); free(targetEntityName); - } else if (prop.isManyToMany) { + } else if (prop.isToMany) { _check(bindings.obx_model_relation( _cModel, prop.relationId.id, diff --git a/lib/src/modelinfo/modelproperty.dart b/lib/src/modelinfo/modelproperty.dart index cd2b7ec58..764cd88c4 100644 --- a/lib/src/modelinfo/modelproperty.dart +++ b/lib/src/modelinfo/modelproperty.dart @@ -80,8 +80,8 @@ class ModelProperty { } bool get isRelation => OBXPropertyType.Relation == type; - bool get isOneToOne => isRelation && !targetEntityName.contains('<'); - bool get isManyToMany => isRelation && targetEntityName.contains('<'); + bool get isToOne => isRelation && !targetEntityName.contains('<'); + bool get isToMany => isRelation && targetEntityName.contains('<'); // bool get isIndexer => [ // OBXPropertyFlag.INDEXED, // OBXPropertyFlag.UNIQUE, From 7be7a63662bce3077d399d42d274b94bded206a3 Mon Sep 17 00:00:00 2001 From: Jasm Sison Date: Tue, 27 Oct 2020 00:44:52 +0100 Subject: [PATCH 13/13] hasRelation --- lib/src/modelinfo/modelentity.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/src/modelinfo/modelentity.dart b/lib/src/modelinfo/modelentity.dart index a885a2c49..ccc73204b 100644 --- a/lib/src/modelinfo/modelentity.dart +++ b/lib/src/modelinfo/modelentity.dart @@ -128,6 +128,9 @@ class ModelEntity { final modelProp = createProperty(prop.name, prop.id.uid); modelProp.type = prop.type; modelProp.flags = prop.flags; + + _hasRelations |= modelProp.isRelation; + return modelProp; } @@ -155,4 +158,7 @@ class ModelEntity { } return false; } + + bool _hasRelations = false; + bool get hasRelations => _hasRelations; }