diff --git a/example/flutter/objectbox_demo/test_driver/app.dart b/example/flutter/objectbox_demo/test_driver/app.dart index f4181aa4b..ace076df3 100644 --- a/example/flutter/objectbox_demo/test_driver/app.dart +++ b/example/flutter/objectbox_demo/test_driver/app.dart @@ -8,4 +8,4 @@ void main() { // Call the `main()` function of the app, or call `runApp` with // any widget you are interested in testing. app.main(); -} \ No newline at end of file +} diff --git a/generator/integration-tests/common.dart b/generator/integration-tests/common.dart index 8509e1bab..99940d4c7 100644 --- a/generator/integration-tests/common.dart +++ b/generator/integration-tests/common.dart @@ -73,3 +73,10 @@ commonModelTests(ModelDefinition defs, ModelInfo jsonModel) { ModelEntity entity(ModelInfo model, String name) { return model.entities.firstWhere((ModelEntity e) => e.name == name); } + +ModelProperty property(ModelInfo model, String path) { + final components = path.split('.'); + return entity(model, components[0]) + .properties + .firstWhere((ModelProperty p) => p.name == components[1]); +} diff --git a/generator/integration-tests/indexes/.gitignore b/generator/integration-tests/indexes/.gitignore new file mode 100644 index 000000000..d5ba0726f --- /dev/null +++ b/generator/integration-tests/indexes/.gitignore @@ -0,0 +1,2 @@ +# start with an empty project, without a objectbox-model.json +objectbox-model.json \ No newline at end of file diff --git a/generator/integration-tests/indexes/1.dart b/generator/integration-tests/indexes/1.dart new file mode 100644 index 000000000..fe84b5a31 --- /dev/null +++ b/generator/integration-tests/indexes/1.dart @@ -0,0 +1,47 @@ +import 'dart:io'; +import 'package:objectbox/objectbox.dart'; + +import 'lib/lib.dart'; +import 'lib/objectbox.g.dart'; +import 'package:test/test.dart'; +import '../test_env.dart'; +import '../common.dart'; +import 'package:objectbox/src/bindings/bindings.dart'; + +void main() { + TestEnv env; + final jsonModel = readModelJson('lib'); + final defs = getObjectBoxModel(); + + setUp(() { + env = TestEnv(defs); + }); + + tearDown(() { + env.close(); + }); + + commonModelTests(defs, jsonModel); + + test('project must be generated properly', () { + expect(TestEnv.dir.existsSync(), true); + expect(File('lib/objectbox.g.dart').existsSync(), true); + expect(File('lib/objectbox-model.json').existsSync(), true); + }); + + test('property flags', () { + expect(property(jsonModel, 'A.id').flags, equals(OBXPropertyFlags.ID)); + expect(property(jsonModel, 'A.indexed').flags, + equals(OBXPropertyFlags.INDEXED)); + expect(property(jsonModel, 'A.unique').flags, + equals(OBXPropertyFlags.INDEX_HASH | OBXPropertyFlags.UNIQUE)); + expect(property(jsonModel, 'A.uniqueValue').flags, + equals(OBXPropertyFlags.INDEXED | OBXPropertyFlags.UNIQUE)); + expect(property(jsonModel, 'A.uniqueHash').flags, + equals(OBXPropertyFlags.INDEX_HASH | OBXPropertyFlags.UNIQUE)); + expect(property(jsonModel, 'A.uniqueHash64').flags, + equals(OBXPropertyFlags.INDEX_HASH64 | OBXPropertyFlags.UNIQUE)); + expect(property(jsonModel, 'A.uid').flags, + equals(OBXPropertyFlags.INDEXED | OBXPropertyFlags.UNIQUE)); + }); +} diff --git a/generator/integration-tests/indexes/lib/lib.dart b/generator/integration-tests/indexes/lib/lib.dart new file mode 100644 index 000000000..1a78591ba --- /dev/null +++ b/generator/integration-tests/indexes/lib/lib.dart @@ -0,0 +1,31 @@ +import 'package:objectbox/objectbox.dart'; +import 'objectbox.g.dart'; + +@Entity() +class A { + @Id() + int id; + + @Index() + int indexed; + + @Unique() + String unique; + + @Unique() + @Index(type: IndexType.value) + String uniqueValue; + + @Unique() + @Index(type: IndexType.hash) + String uniqueHash; + + @Unique() + @Index(type: IndexType.hash64) + String uniqueHash64; + + @Unique() + int uid; + + A(); +} diff --git a/generator/integration-tests/indexes/pubspec.yaml b/generator/integration-tests/indexes/pubspec.yaml new file mode 120000 index 000000000..8d669e1bf --- /dev/null +++ b/generator/integration-tests/indexes/pubspec.yaml @@ -0,0 +1 @@ +../shared-pubspec.yaml \ No newline at end of file diff --git a/generator/lib/src/code_builder.dart b/generator/lib/src/code_builder.dart index 19b78ab78..3e0add71b 100644 --- a/generator/lib/src/code_builder.dart +++ b/generator/lib/src/code_builder.dart @@ -127,19 +127,24 @@ class CodeBuilder extends Builder { 'Entity ${entity.name}(${entity.id.toString()}) not found in the code, removing from the model'); model.removeEntity(entity); }); - - entities.forEach((entity) => mergeEntity(model, entity)); } void mergeProperty(ModelEntity entity, ModelProperty prop) { - final propInModel = entity.findSameProperty(prop); + var propInModel = entity.findSameProperty(prop); + if (propInModel == null) { log.info('Found new property ${entity.name}.${prop.name}'); - entity.addProperty(prop); + propInModel = entity.createProperty(prop.name, prop.id.uid); + } + + propInModel.name = prop.name; + propInModel.type = prop.type; + propInModel.flags = prop.flags; + + if (!prop.hasIndexFlag()) { + propInModel.removeIndex(); } else { - propInModel.name = prop.name; - propInModel.type = prop.type; - propInModel.flags = prop.flags; + propInModel.indexId ??= entity.model.createIndexId(); } } @@ -151,26 +156,26 @@ class CodeBuilder extends Builder { if (entityInModel == null) { log.info('Found new entity ${entity.name}'); // in case the entity is created (i.e. when its given UID or name that does not yet exist), we are done, as nothing needs to be merged - entityInModel = modelInfo.addEntity(entity); - } else { - entityInModel.name = entity.name; - entityInModel.flags = entity.flags; - - // 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)); - - // then remove all properties not present anymore in readEntity - final missingProps = entityInModel.properties - .where((p) => entity.findSameProperty(p) == null) - .toList(growable: false); - - missingProps.forEach((p) { - log.warning( - 'Property ${entity.name}.${p.name}(${p.id.toString()}) not found in the code, removing from the model'); - entityInModel.removeProperty(p); - }); + entityInModel = modelInfo.createEntity(entity.name, entity.id.uid); } + entityInModel.name = entity.name; + entityInModel.flags = entity.flags; + + // 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)); + + // then remove all properties not present anymore in readEntity + final missingProps = entityInModel.properties + .where((p) => entity.findSameProperty(p) == null) + .toList(growable: false); + + missingProps.forEach((p) { + log.warning( + 'Property ${entity.name}.${p.name}(${p.id.toString()}) not found in the code, removing from the model'); + entityInModel.removeProperty(p); + }); + return entityInModel.id; } } diff --git a/generator/lib/src/entity_resolver.dart b/generator/lib/src/entity_resolver.dart index 1de9a1935..5fa809595 100644 --- a/generator/lib/src/entity_resolver.dart +++ b/generator/lib/src/entity_resolver.dart @@ -2,6 +2,7 @@ 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/bindings.dart'; @@ -22,6 +23,8 @@ class EntityResolver extends Builder { final _idChecker = const TypeChecker.fromRuntime(obx.Id); final _transientChecker = const TypeChecker.fromRuntime(obx.Transient); final _syncChecker = const TypeChecker.fromRuntime(obx.Sync); + final _uniqueChecker = const TypeChecker.fromRuntime(obx.Unique); + final _indexChecker = const TypeChecker.fromRuntime(obx.Index); @override FutureOr build(BuildStep buildStep) async { @@ -140,13 +143,18 @@ 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, entity); + final prop = ModelProperty(IdUid.empty(), f.name, fieldType, + flags: flags, entity: entity); + + // Index and unique annotation. + final indexTypeStr = + processAnnotationIndexUnique(f, fieldType, elementBare, prop); + if (propUid != null) prop.id.uid = propUid; entity.properties.add(prop); log.info( - ' property ${prop.name}(${prop.id}) type:${prop.type} flags:${prop.flags}'); + ' property ${prop.name}(${prop.id}) type:${prop.type} flags:${prop.flags} ${prop.hasIndexFlag() ? "index:${indexTypeStr}" : ""}'); } // some checks on the entity's integrity @@ -157,4 +165,84 @@ class EntityResolver extends Builder { return entity; } + + String processAnnotationIndexUnique(FieldElement f, int fieldType, + Element elementBare, obx.ModelProperty prop) { + obx.IndexType indexType; + + final indexAnnotation = _indexChecker.firstAnnotationOfExact(f); + final hasUniqueAnnotation = _uniqueChecker.hasAnnotationOfExact(f); + if (indexAnnotation == null && !hasUniqueAnnotation) return null; + + // Throw if property type does not support any index. + if (fieldType == OBXPropertyType.Float || + fieldType == OBXPropertyType.Double || + fieldType == OBXPropertyType.ByteVector) { + throw InvalidGenerationSourceError( + "in target ${elementBare.name}: @Index/@Unique is not supported for type '${f.type.toString()}' of field '${f.name}'"); + } + + if (prop.hasFlag(OBXPropertyFlags.ID)) { + throw InvalidGenerationSourceError( + 'in target ${elementBare.name}: @Index/@Unique is not supported for ID field ${f.name}. IDs are unique by definition and automatically indexed'); + } + + // If available use index type from annotation. + if (indexAnnotation != null && !indexAnnotation.isNull) { + // find out @Index(type:) value - its an enum IndexType + final indexTypeField = indexAnnotation.getField('type'); + if (!indexTypeField.isNull) { + final indexTypeEnumValues = (indexTypeField.type as InterfaceType) + .element + .fields + .where((f) => f.isEnumConstant) + .toList(); + + // Find the index of the matching enum constant. + for (var i = 0; i < indexTypeEnumValues.length; i++) { + if (indexTypeEnumValues[i].computeConstantValue() == indexTypeField) { + indexType = obx.IndexType.values[i]; + break; + } + } + } + } + + // Fall back to index type based on property type. + final supportsHashIndex = fieldType == OBXPropertyType.String; + if (indexType == null) { + if (supportsHashIndex) { + indexType = obx.IndexType.hash; + } else { + indexType = obx.IndexType.value; + } + } + + // Throw if HASH or HASH64 is not supported by property type. + if (!supportsHashIndex && + (indexType == obx.IndexType.hash || + indexType == obx.IndexType.hash64)) { + throw InvalidGenerationSourceError( + "in target ${elementBare.name}: a hash index is not supported for type '${f.type.toString()}' of field '${f.name}'"); + } + + if (hasUniqueAnnotation) { + prop.flags |= OBXPropertyFlags.UNIQUE; + } + + switch (indexType) { + case obx.IndexType.value: + prop.flags |= OBXPropertyFlags.INDEXED; + return 'value'; + case obx.IndexType.hash: + prop.flags |= OBXPropertyFlags.INDEX_HASH; + return 'hash'; + case obx.IndexType.hash64: + prop.flags |= OBXPropertyFlags.INDEX_HASH64; + return 'hash64'; + default: + throw InvalidGenerationSourceError( + 'in target ${elementBare.name}: invalid index type: $indexType'); + } + } } diff --git a/generator/test.sh b/generator/test.sh index 2be995c13..0e6a002a1 100755 --- a/generator/test.sh +++ b/generator/test.sh @@ -15,7 +15,7 @@ function runTestFile() { # build before each step, except for "0.dart" if [ "${1}" != "0" ]; then echo "Running build_runner before ${file}" - dart pub run build_runner build + dart pub run build_runner build --verbose fi echo "Running ${file}" dart test "${file}" diff --git a/lib/integration_test.dart b/lib/integration_test.dart index 2c435ac75..af38d0287 100644 --- a/lib/integration_test.dart +++ b/lib/integration_test.dart @@ -18,8 +18,8 @@ class IntegrationTest { static void model() { // create a model with a single entity and a single property final modelInfo = ModelInfo(); - final property = ModelProperty( - IdUid(1, int64_max - 1), 'id', OBXPropertyType.Long, 0, null); + final property = + ModelProperty(IdUid(1, int64_max - 1), 'id', OBXPropertyType.Long); final entity = ModelEntity(IdUid(1, int64_max), 'entity', modelInfo); property.entity = entity; entity.properties.add(property); diff --git a/lib/src/annotations.dart b/lib/src/annotations.dart index 76aa4799c..92c716a24 100644 --- a/lib/src/annotations.dart +++ b/lib/src/annotations.dart @@ -33,3 +33,34 @@ class Transient { // class Sync { // const Sync(); // } + +/// Specifies that the property should be indexed. +/// +/// It is highly recommended to index properties that are used in a Query to +/// improve query performance. To fine tune indexing of a property you can +/// override the default index type. +/// +/// Note: indexes are currently not supported for ByteVector, Float or Double +/// properties. +class Index { + final IndexType /*?*/ type; + const Index({this.type}); +} + +enum IndexType { + value, + hash, + hash64, +} + +/// Enforces that the value of a property is unique among all Objects in a Box +/// before an Object can be put. +/// +/// Trying to put an Object with offending values will result in an exception. +/// +/// Unique properties are based on an [Index], so the same restrictions apply. +/// It is supported to explicitly add the [Index] annotation to configure the +/// index type. +class Unique { + const Unique(); +} diff --git a/lib/src/model.dart b/lib/src/model.dart index 48f546152..3fae9e97b 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -19,6 +19,12 @@ class Model { // set last entity id bindings.obx_model_last_entity_id( _cModel, model.lastEntityId.id, model.lastEntityId.uid); + + // set last index id + if (model.lastIndexId != null) { + bindings.obx_model_last_index_id( + _cModel, model.lastIndexId.id, model.lastIndexId.uid); + } } catch (e) { bindings.obx_model_free(_cModel); rethrow; @@ -67,6 +73,11 @@ class Model { try { _check(bindings.obx_model_property( _cModel, name, prop.type, prop.id.id, prop.id.uid)); + + if (prop.indexId != null) { + _check(bindings.obx_model_property_index_id( + _cModel, prop.indexId.id, prop.indexId.uid)); + } } finally { free(name); } diff --git a/lib/src/modelinfo/modelentity.dart b/lib/src/modelinfo/modelentity.dart index 19c9f2507..d98b6040a 100644 --- a/lib/src/modelinfo/modelentity.dart +++ b/lib/src/modelinfo/modelentity.dart @@ -142,17 +142,11 @@ class ModelEntity { } final uniqueUid = uid == 0 ? model.generateUid() : uid; - var property = ModelProperty(IdUid(id, uniqueUid), name, 0, 0, this); + final property = ModelProperty(IdUid(id, uniqueUid), name, 0, entity: this); properties.add(property); lastPropertyId = property.id; - return property; - } - ModelProperty addProperty(ModelProperty prop) { - final modelProp = createProperty(prop.name, prop.id.uid); - modelProp.type = prop.type; - modelProp.flags = prop.flags; - return modelProp; + return property; } void removeProperty(ModelProperty prop) { @@ -163,6 +157,10 @@ class ModelEntity { } _properties.remove(foundProp); model.retiredPropertyUids.add(prop.id.uid); + + if (prop.indexId != null) { + model.retiredIndexUids.add(prop.indexId.uid); + } } bool containsUid(int searched) { diff --git a/lib/src/modelinfo/modelinfo.dart b/lib/src/modelinfo/modelinfo.dart index 657c596e0..d198d859d 100644 --- a/lib/src/modelinfo/modelinfo.dart +++ b/lib/src/modelinfo/modelinfo.dart @@ -152,12 +152,6 @@ class ModelInfo { return ret; } - ModelEntity addEntity(ModelEntity other) { - final modelEntity = createEntity(other.name, other.id.uid); - other.properties.forEach((p) => modelEntity.addProperty(p)); - return modelEntity; - } - ModelEntity createEntity(String name, [int uid = 0]) { var id = 1; if (entities.isNotEmpty) id = lastEntityId.id + 1; @@ -208,4 +202,10 @@ class ModelInfo { if (listContains(retiredRelationUids, uid)) return true; return false; } + + IdUid createIndexId() { + var id = lastIndexId.isEmpty ? 1 : lastIndexId.id + 1; + lastIndexId = IdUid(id, generateUid()); + return lastIndexId; + } } diff --git a/lib/src/modelinfo/modelproperty.dart b/lib/src/modelinfo/modelproperty.dart index b73c9b319..32cf5312c 100644 --- a/lib/src/modelinfo/modelproperty.dart +++ b/lib/src/modelinfo/modelproperty.dart @@ -1,3 +1,5 @@ +import 'package:objectbox/src/bindings/bindings.dart'; + import 'modelentity.dart'; import 'iduid.dart'; @@ -6,6 +8,7 @@ class ModelProperty { IdUid id; /*late*/ String _name; /*late*/ int _type, _flags; + IdUid /*?*/ _indexId; ModelEntity /*?*/ entity; String get name => _name; @@ -35,16 +38,30 @@ class ModelProperty { _flags = value /*!*/; } - ModelProperty(this.id, String /*?*/ name, int /*?*/ type, int /*?*/ flags, - this.entity) { + IdUid /*?*/ get indexId => _indexId; + + set indexId(IdUid /*?*/ value) { + if (value != null) { + if (value.id == 0 || value.uid == 0) { + throw Exception('indexId must contain valid ID & UID'); + } + } + _indexId = value /*!*/; + } + + ModelProperty(this.id, String /*?*/ name, int /*?*/ type, + {int flags = 0, String /*?*/ indexId, this.entity}) { this.name = name; this.type = type; this.flags = flags; + this.indexId = indexId == null ? null : IdUid.fromString(indexId); } ModelProperty.fromMap(Map data, ModelEntity /*?*/ entity) : this(IdUid.fromString(data['id']), data['name'], data['type'], - data['flags'] ?? 0, entity); + flags: data['flags'] ?? 0, + indexId: data['indexId'], + entity: entity); Map toMap() { final ret = {}; @@ -52,6 +69,7 @@ class ModelProperty { ret['name'] = name; ret['type'] = type; if (flags != 0) ret['flags'] = flags; + if (indexId != null) ret['indexId'] = indexId /*!*/ .toString(); return ret; } @@ -62,4 +80,17 @@ class ModelProperty { bool hasFlag(int flag) { return (flags & flag) == flag; } + + bool hasIndexFlag() { + return hasFlag(OBXPropertyFlags.INDEXED) || + hasFlag(OBXPropertyFlags.INDEX_HASH) || + hasFlag(OBXPropertyFlags.INDEX_HASH64); + } + + void removeIndex() { + if (_indexId != null) { + entity.model.retiredIndexUids.add(_indexId.uid); + indexId = null; + } + } } diff --git a/test/box_test.dart b/test/box_test.dart index ac877cfff..c3f10bdf5 100644 --- a/test/box_test.dart +++ b/test/box_test.dart @@ -58,6 +58,18 @@ void main() { expect(item.tString, equals('Two')); }); + test('.put() cannot add duplicate values on a unique field', () { + final u1 = TestEntity.unique( + uString: 'a', uLong: 1, uInt: 1, uShort: 1, uByte: 1, uChar: 1); + final again = TestEntity.unique( + uString: 'a', uLong: 1, uInt: 1, uShort: 1, uByte: 1, uChar: 1); + + expect( + () => box.putMany([u1, again]), + throwsA(predicate((ObjectBoxException e) => + e.toString().contains('same property value already exists')))); + }); + test('.getAll retrieves all items', () { final int id1 = box.put(TestEntity(tString: 'One')); final int id2 = box.put(TestEntity(tString: 'Two')); diff --git a/test/entity.dart b/test/entity.dart index b3ce00fda..2086191b4 100644 --- a/test/entity.dart +++ b/test/entity.dart @@ -1,4 +1,5 @@ import 'package:objectbox/objectbox.dart'; +import 'package:objectbox/src/bindings/bindings.dart'; /// A dummy annotation to verify the code is generated properly even with annotations unknown to ObjectBox generator. class TestingUnknownAnnotation { @@ -28,23 +29,23 @@ class TestEntity { // explicitly declared types, see OB-C, objectbox.h // OBXPropertyType.Byte | 1 byte - @Property(type: 2) + @Property(type: OBXPropertyType.Byte) int /*?*/ tByte; // OBXPropertyType.Short | 2 bytes - @Property(type: 3) + @Property(type: OBXPropertyType.Short) int /*?*/ tShort; // OBXPropertyType.Char | 1 byte - @Property(type: 4) + @Property(type: OBXPropertyType.Char) int /*?*/ tChar; // OBXPropertyType.Int | ob: 4 bytes, dart: 8 bytes - @Property(type: 5) + @Property(type: OBXPropertyType.Int) int /*?*/ tInt; // OBXPropertyType.Float | 4 bytes - @Property(type: 7) + @Property(type: OBXPropertyType.Float) double /*?*/ tFloat; TestEntity( @@ -64,4 +65,68 @@ class TestEntity { omit = -1; disregard = 1; } + + @Property(type: OBXPropertyType.Byte) + @Unique() + int uByte; + + @Property(type: OBXPropertyType.Short) + @Unique() + int uShort; + + @Property(type: OBXPropertyType.Char) + @Unique() + int uChar; + + @Property(type: OBXPropertyType.Int) + @Unique() + int uInt; + + // implicitly determined types + @Unique() + String uString; + + @Unique() + int uLong; + + TestEntity.unique({ + this.uString, + this.uLong, + this.uInt, + this.uShort, + this.uByte, + this.uChar, + }); + + @Property(type: OBXPropertyType.Byte) + @Index() + int iByte; + + @Property(type: OBXPropertyType.Short) + @Index() + int iShort; + + @Property(type: OBXPropertyType.Char) + @Index() + int iChar; + + @Property(type: OBXPropertyType.Int) + @Index() + int iInt; + + // implicitly determined types + @Index() + String iString; + + @Index() + int iLong; + + TestEntity.index({ + this.iString, + this.iLong, + this.iInt, + this.iShort, + this.iByte, + this.iChar, + }); } diff --git a/test/objectbox-model.json b/test/objectbox-model.json index c081e636a..2ad506fce 100644 --- a/test/objectbox-model.json +++ b/test/objectbox-model.json @@ -5,7 +5,7 @@ "entities": [ { "id": "1:4630700155272683157", - "lastPropertyId": "14:2723176855509462268", + "lastPropertyId": "26:3059114515142777989", "name": "TestEntity", "flags": 2, "properties": [ @@ -59,6 +59,90 @@ "id": "14:2723176855509462268", "name": "tFloat", "type": 7 + }, + { + "id": "15:5960097736918862689", + "name": "uByte", + "type": 2, + "flags": 40, + "indexId": "1:759265819613755228" + }, + { + "id": "16:3014870337091941693", + "name": "uShort", + "type": 3, + "flags": 40, + "indexId": "2:3731424852781354288" + }, + { + "id": "17:3004362051550241897", + "name": "uChar", + "type": 4, + "flags": 40, + "indexId": "3:1180301620695216647" + }, + { + "id": "18:1083874867412561588", + "name": "uInt", + "type": 5, + "flags": 40, + "indexId": "4:9159391309201422019" + }, + { + "id": "19:1849261948480372113", + "name": "uString", + "type": 9, + "flags": 2080, + "indexId": "5:7090314366957370493" + }, + { + "id": "20:1700905145011245158", + "name": "uLong", + "type": 6, + "flags": 40, + "indexId": "6:9146355272521890356" + }, + { + "id": "21:1256214340403371310", + "name": "iByte", + "type": 2, + "flags": 8, + "indexId": "7:2597915862881161952" + }, + { + "id": "22:5768208836201983752", + "name": "iShort", + "type": 3, + "flags": 8, + "indexId": "8:5900854653402683567" + }, + { + "id": "23:2550226115516189529", + "name": "iChar", + "type": 4, + "flags": 8, + "indexId": "9:6832522351412488843" + }, + { + "id": "24:8654682464442924778", + "name": "iInt", + "type": 5, + "flags": 8, + "indexId": "10:7301436684067319059" + }, + { + "id": "25:6667369331692110338", + "name": "iString", + "type": 9, + "flags": 2048, + "indexId": "11:4395163045094957195" + }, + { + "id": "26:3059114515142777989", + "name": "iLong", + "type": 6, + "flags": 8, + "indexId": "12:5335419474465474748" } ] }, @@ -77,7 +161,7 @@ } ], "lastEntityId": "3:3569200127393812728", - "lastIndexId": "0:0", + "lastIndexId": "12:5335419474465474748", "lastRelationId": "0:0", "lastSequenceId": "0:0", "modelVersion": 5, diff --git a/test/query_test.dart b/test/query_test.dart index 843029999..e4c92bead 100644 --- a/test/query_test.dart +++ b/test/query_test.dart @@ -295,7 +295,7 @@ void main() { final remaining = box.getAll(); expect(remaining.length, 2); - expect(remaining.map((e) => e.id), equals([1,4])); + expect(remaining.map((e) => e.id), equals([1, 4])); }); test('.count items after grouping with and/or', () {