Skip to content

WiP: @Unique and @Index (Any help is welcome) #120

New issue

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

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

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
14 changes: 9 additions & 5 deletions generator/lib/src/code_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,6 @@ 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) {
Expand All @@ -137,9 +135,15 @@ class CodeBuilder extends Builder {
log.info('Found new property ${entity.name}.${prop.name}');
entity.addProperty(prop);
} else {
propInModel.name = prop.name;
propInModel.type = prop.type;
propInModel.flags = prop.flags;
if (propInModel.name != prop.name) {
log.warning('The name of the property(${prop.name}) changed.');
}
if (propInModel.flags != prop.flags) {
log.warning('The flags of the property(${prop.name}) changed.');
}
if (propInModel.type != prop.type) {
log.warning('The type of the property(${prop.name}) changed.');
}
}
}

Expand Down
32 changes: 30 additions & 2 deletions generator/lib/src/entity_resolver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:source_gen/source_gen.dart';
import 'package:objectbox/objectbox.dart' as obx;
import 'package:objectbox/src/bindings/constants.dart';
import 'package:objectbox/src/modelinfo/index.dart';
import 'package:objectbox/src/util.dart';

/// 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.
Expand All @@ -20,6 +21,8 @@ class EntityResolver extends Builder {
final _propertyChecker = const TypeChecker.fromRuntime(obx.Property);
final _idChecker = const TypeChecker.fromRuntime(obx.Id);
final _transientChecker = const TypeChecker.fromRuntime(obx.Transient);
final _uniqueChecker = const TypeChecker.fromRuntime(obx.Unique);
final _indexChecker = const TypeChecker.fromRuntime(obx.Index);

@override
FutureOr<void> build(BuildStep buildStep) async {
Expand Down Expand Up @@ -68,7 +71,7 @@ class EntityResolver extends Builder {
}

int fieldType, flags = 0;
int propUid;
int propUid, indexUid;

if (_idChecker.hasAnnotationOfExact(f)) {
if (hasIdProperty) {
Expand All @@ -87,6 +90,18 @@ class EntityResolver extends Builder {

final _idAnnotation = _idChecker.firstAnnotationOfExact(f);
propUid = _idAnnotation.getField('uid').toIntValue();
} else if (_uniqueChecker.hasAnnotationOfExact(f)) {
final _uniqueAnnotation = _uniqueChecker.firstAnnotationOfExact(f);
propUid = _uniqueAnnotation.getField('uid').toIntValue();
indexUid = _uniqueAnnotation.getField('indexUid').toIntValue();
fieldType = _uniqueAnnotation.getField('type').toIntValue();
flags = OBXPropertyFlag.UNIQUE;
} else if (_indexChecker.hasAnnotationOfExact(f)) {
final _indexAnnotation = _indexChecker.firstAnnotationOfExact(f);
propUid = _indexAnnotation.getField('uid').toIntValue();
indexUid = _indexAnnotation.getField('indexUid').toIntValue();
fieldType = _indexAnnotation.getField('type').toIntValue();
flags = OBXPropertyFlag.INDEXED;
} else if (_propertyChecker.hasAnnotationOfExact(f)) {
final _propertyAnnotation = _propertyChecker.firstAnnotationOfExact(f);
propUid = _propertyAnnotation.getField('uid').toIntValue();
Expand Down Expand Up @@ -121,11 +136,24 @@ 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);

// TODO test on @Property(...) that uses the proper flags for index
final isIndexer = flags.isIndexer;

if (isIndexer) {
if (fieldType == OBXPropertyType.Float || fieldType == OBXPropertyType.Double || fieldType == OBXPropertyType.Byte) {
throw InvalidGenerationSourceError(
'property ${prop.name} with type ${prop.type} cannot be used as an index or made unique');
}

prop.indexId = indexUid == null ? IdUid.empty() : IdUid(0, indexUid);
}

if (propUid != null) prop.id.uid = propUid;
readEntity.properties.add(prop);

log.info(
' property ${prop.name}(${prop.id}) type:${prop.type} flags:${prop.flags}');
' ${isIndexer ? "index " : ""}property ${prop.name}(${prop.id}) type:${prop.type} flags:${prop.flags}');
}

// some checks on the entity's integrity
Expand Down
10 changes: 10 additions & 0 deletions lib/src/annotations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,13 @@ class Id {
class Transient {
const Transient();
}

class Unique {
final int uid, indexUid, type;
const Unique({this.type, this.uid, this.indexUid});
}

class Index {
final int uid, indexUid, type;
const Index({this.type, this.uid, this.indexUid});
}
12 changes: 11 additions & 1 deletion lib/src/bindings/bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,15 @@ class _ObjectBoxBindings {
int entity_uid) obx_model_entity;
int Function(Pointer<Void> model, Pointer<Utf8> name, int type,
int property_id, int property_uid) obx_model_property;
int Function(Pointer<Void> model, int index_id, int index_uid)
obx_model_property_index_id;
int Function(Pointer<Void> model, int flags) obx_model_property_flags;
int Function(Pointer<Void> model, int property_id, int property_uid)
obx_model_entity_last_property_id;
int Function(Pointer<Void> model, int entity_id, int entity_uid)
void Function(Pointer<Void> model, int entity_id, int entity_uid)
obx_model_last_entity_id;
void Function(Pointer<Void> model, int entity_id, int index_uid)
obx_model_last_index_id;

// object store management
Pointer<Void> Function() obx_opt;
Expand Down Expand Up @@ -282,6 +286,9 @@ class _ObjectBoxBindings {
_fn<obx_model_entity_native_t>('obx_model_entity').asFunction();
obx_model_property =
_fn<obx_model_property_native_t>('obx_model_property').asFunction();
obx_model_property_index_id =
_fn<obx_model_property_index_id_native_t>('obx_model_property_index_id')
.asFunction();
obx_model_property_flags =
_fn<obx_model_property_flags_native_t>('obx_model_property_flags')
.asFunction();
Expand All @@ -292,6 +299,9 @@ class _ObjectBoxBindings {
obx_model_last_entity_id =
_fn<obx_model_last_entity_id_native_t>('obx_model_last_entity_id')
.asFunction();
obx_model_last_index_id =
_fn<obx_model_last_index_id_native_t>('obx_model_last_index_id')
.asFunction();

// object store management
obx_opt = _fn<obx_opt_native_t>('obx_opt').asFunction();
Expand Down
6 changes: 5 additions & 1 deletion lib/src/bindings/signatures.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,16 @@ typedef obx_model_entity_native_t = Int32 Function(Pointer<Void> model,
Pointer<Utf8> name, Uint32 entity_id, Uint64 entity_uid);
typedef obx_model_property_native_t = Int32 Function(Pointer<Void> model,
Pointer<Utf8> name, Uint32 type, Uint32 property_id, Uint64 property_uid);
typedef obx_model_property_index_id_native_t = Int32 Function(
Pointer<Void> model, Uint32 index_id, Uint64 index_uid);
typedef obx_model_property_flags_native_t = Int32 Function(
Pointer<Void> model, Uint32 flags);
typedef obx_model_entity_last_property_id_native_t = Int32 Function(
Pointer<Void> 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<Void> model, Uint32 entity_id, Uint64 entity_uid);
typedef obx_model_last_index_id_native_t = Void Function(
Pointer<Void> model, Uint32 entity_id, Uint64 index_uid);

// object store management
typedef obx_opt_native_t = Pointer<Void> Function();
Expand Down
11 changes: 11 additions & 0 deletions lib/src/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,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);
_cModel = null;
Expand Down Expand Up @@ -59,6 +65,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);
}
Expand Down
42 changes: 28 additions & 14 deletions lib/src/modelinfo/modelentity.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import '../util.dart';
import 'iduid.dart';
import 'modelinfo.dart';
import 'modelproperty.dart';
import 'package:objectbox/src/bindings/constants.dart';
import '../bindings/constants.dart';
import '../util.dart';

/// ModelEntity describes an entity of a model and consists of instances of `ModelProperty` as well as an other entity
/// information: id, name and last property id.
Expand Down Expand Up @@ -110,25 +110,38 @@ class ModelEntity {
return ret ??= findPropertyByName(other.name);
}

ModelProperty createProperty(String name, [int uid = 0]) {
var id = 1;
if (properties.isNotEmpty) id = lastPropertyId.id + 1;
ModelProperty createProperty(
String name, int uid, int type, int flags, int indexUid) {
final id = properties.isNotEmpty ? lastPropertyId.id + 1 : 1;
if (uid != 0 && model.containsUid(uid)) {
throw Exception('uid already exists: $uid');
}
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, 0, this);

if (flags.isIndexer) {
if (indexUid != 0 && model.containsUid(indexUid)) {
throw Exception('index uid already exists: $uid');
}
property.indexId = IdUid(_model.lastIndexId.id + 1,
indexUid == 0 ? model.generateUid() : indexUid);

_model.lastIndexId = property.indexId;
}

property.type = type;
property.flags = flags;

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 createProperty(
prop.name, prop.id.uid, prop.type, prop.flags, prop.indexId.uid);
}

void removeProperty(ModelProperty prop) {
Expand All @@ -140,14 +153,15 @@ class ModelEntity {
}
properties = properties.where((p) => p != foundProp).toList();
model.retiredPropertyUids.add(prop.id.uid);

if (prop.flags.isIndexer) {
model.retiredIndexUids.add(prop.indexId.uid);
}
}

bool containsUid(int searched) {
if (id.uid == searched) return true;
if (lastPropertyId != null && lastPropertyId.uid == searched) return true;
if (properties.indexWhere((p) => p.containsUid(searched)) != -1) {
return true;
}
return false;
return properties.indexWhere((p) => p.containsUid(searched)) != -1;
}
}
9 changes: 8 additions & 1 deletion lib/src/modelinfo/modelproperty.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import 'modelentity.dart';
import 'iduid.dart';
import '../util.dart';

/// ModelProperty describes a single property of an entity, i.e. its id, name, type and flags.
class ModelProperty {
IdUid id;
IdUid id, indexId;
String name;
int type, flags;
ModelEntity entity;
Expand All @@ -18,6 +19,9 @@ class ModelProperty {
name = data['name'];
type = data['type'];
flags = data.containsKey('flags') ? data['flags'] : 0;
if (flags.isIndexer) {
indexId = IdUid.fromString(data['indexId']);
}
if (check) validate();
}

Expand All @@ -36,6 +40,9 @@ class ModelProperty {
ret['name'] = name;
ret['type'] = type;
if (flags != 0) ret['flags'] = flags;
if (flags.isIndexer) {
ret['indexId'] = indexId.toString();
}
return ret;
}

Expand Down
8 changes: 8 additions & 0 deletions lib/src/util.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
import 'package:objectbox/src/bindings/constants.dart';

bool listContains<T>(List<T> list, T item) =>
list.indexWhere((x) => x == item) != -1;

extension Indexer on int {
bool get isIndexer =>
this & OBXPropertyFlag.INDEXED == 8 ||
this & OBXPropertyFlag.UNIQUE == 32;
}
9 changes: 9 additions & 0 deletions test/box_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ 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, uChar: 1);
final again = TestEntity.unique(uString: 'a', uLong: 1, uInt: 1,
uShort: 1, uChar: 1);

expect(box.putMany([u1, again]), throwsException);
});

test('.getAll retrieves all items', () {
final int id1 = box.put(TestEntity(tString: 'One'));
final int id2 = box.put(TestEntity(tString: 'Two'));
Expand Down
Loading