Skip to content

Commit 930ae01

Browse files
Shubhamgreenrobot-team
authored andcommitted
external types: add @ExternalType annotation #139
1 parent 38d5f87 commit 930ae01

File tree

11 files changed

+320
-3
lines changed

11 files changed

+320
-3
lines changed

generator/lib/src/code_builder.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ class CodeBuilder extends Builder {
213213
propInModel.dartFieldType = prop.dartFieldType;
214214
propInModel.relationTarget = prop.relationTarget;
215215
propInModel.hnswParams = prop.hnswParams;
216+
propInModel.externalPropertyType = prop.externalPropertyType;
217+
propInModel.externalPropertyName = prop.externalPropertyName;
216218

217219
if (!prop.hasIndexFlag()) {
218220
propInModel.removeIndex();

generator/lib/src/code_chunks.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ class CodeChunks {
154154
additionalArgs +=
155155
", hnswParams: ${property.hnswParams!.toCodeString(obxInt)}";
156156
}
157+
if (property.externalPropertyType != null) {
158+
additionalArgs +=
159+
", externalPropertyType: ${property.externalPropertyType!}";
160+
}
161+
if (property.externalPropertyName != null) {
162+
additionalArgs +=
163+
", externalPropertyName: '${property.externalPropertyName!}'";
164+
}
157165
return '''
158166
$obxInt.ModelProperty(
159167
id: ${createIdUid(property.id)},

generator/lib/src/entity_resolver.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class EntityResolver extends Builder {
2828
final _indexChecker = const TypeChecker.fromRuntime(Index);
2929
final _backlinkChecker = const TypeChecker.fromRuntime(Backlink);
3030
final _hnswChecker = const TypeChecker.fromRuntime(HnswIndex);
31+
final _externalTypeChecker = const TypeChecker.fromRuntime(ExternalProperty);
3132

3233
@override
3334
FutureOr<void> build(BuildStep buildStep) async {
@@ -226,6 +227,10 @@ class EntityResolver extends Builder {
226227
_readHnswIndexParams(annotation, prop);
227228
});
228229

230+
_externalTypeChecker.runIfMatches(annotated, (annotation) {
231+
_readExternalTypeParams(annotation, prop);
232+
});
233+
229234
// for code generation
230235
prop.dartFieldType =
231236
f.type.element!.name! + (isNullable(f.type) ? '?' : '');
@@ -555,6 +560,19 @@ class EntityResolver extends Builder {
555560
annotation.getField('vectorCacheHintSizeKB')!.toIntValue());
556561
property.hnswParams = ModelHnswParams.fromAnnotation(hnswRestored);
557562
}
563+
564+
void _readExternalTypeParams(DartObject annotation, ModelProperty property) {
565+
final typeIndex =
566+
_enumValueIndex(annotation.getField('type')!, "ExternalProperty.type");
567+
final type = typeIndex != null ? ExternalType.values[typeIndex] : null;
568+
if (type == null) {
569+
throw InvalidGenerationSourceError(
570+
"'type' attribute not specified in @ExternalProperty annotation");
571+
}
572+
final name = annotation.getField('name')!.toStringValue();
573+
property.externalPropertyType = externalTypeToOBXExternalType(type);
574+
property.externalPropertyName = name;
575+
}
558576
}
559577

560578
extension _TypeCheckerExtensions on TypeChecker {

generator/test/code_builder_test.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,38 @@ void main() {
378378
expect(idProperty.flags & OBXPropertyFlags.ID_SELF_ASSIGNABLE != 0, true);
379379
});
380380
});
381+
382+
test('ExternalType annotation', () async {
383+
final source = r'''
384+
library example;
385+
import 'package:objectbox/objectbox.dart';
386+
387+
@Entity()
388+
class Example {
389+
@Id()
390+
int id = 0;
391+
392+
@Property(type: PropertyType.byteVector)
393+
@ExternalProperty(type: ExternalType.bson, name: 'my-mongo-bson')
394+
List<int>? mongoBson;
395+
396+
@ExternalProperty(type: ExternalType.javaScript, name: 'my-mongo-js')
397+
String? mongoJS;
398+
}
399+
''';
400+
401+
final testEnv = GeneratorTestEnv();
402+
await testEnv.run(source);
403+
404+
final property1 = testEnv.model.entities[0].properties
405+
.firstWhere((element) => element.name == "mongoBson");
406+
expect(property1.externalPropertyType, OBXExternalPropertyType.Bson);
407+
expect(property1.externalPropertyName, "my-mongo-bson");
408+
final property2 = testEnv.model.entities[0].properties
409+
.firstWhere((element) => element.name == "mongoJS");
410+
expect(property2.externalPropertyType, OBXExternalPropertyType.JavaScript);
411+
expect(property2.externalPropertyName, "my-mongo-js");
412+
});
381413
}
382414

383415
Future<PackageConfig> _unsupported() {

objectbox/lib/src/annotations.dart

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,3 +487,109 @@ class HnswIndex {
487487
this.reparationBacklinkProbability,
488488
this.vectorCacheHintSizeKB});
489489
}
490+
491+
/// A property type of an external system (e.g. another database) that has no
492+
/// default mapping to an ObjectBox type.
493+
///
494+
/// Use with [ExternalProperty].
495+
enum ExternalType {
496+
/// Representing type: ByteVector
497+
///
498+
/// Encoding: 1:1 binary representation, little endian (16 bytes)
499+
int128,
500+
501+
/// Representing type: ByteVector
502+
///
503+
/// Encoding: 1:1 binary representation (16 bytes)
504+
uuid,
505+
506+
/// IEEE 754 decimal128 type, e.g. supported by MongoDB.
507+
///
508+
/// Representing type: ByteVector
509+
///
510+
/// Encoding: 1:1 binary representation (16 bytes)
511+
decimal128,
512+
513+
/// A key/value map; e.g. corresponds to a JSON object or a MongoDB document
514+
/// (although not keeping the key order).
515+
///
516+
/// Unlike the Flex type, this must contain a map value (e.g. not a vector or
517+
/// a scalar).
518+
///
519+
/// Representing type: Flex
520+
///
521+
/// Encoding: Flex
522+
flexMap,
523+
524+
/// A vector (aka list or array) of flexible elements; e.g. corresponds to a
525+
/// JSON array or a MongoDB array.
526+
///
527+
/// Unlike the Flex type, this must contain a vector value (e.g. not a map or
528+
/// a scalar).
529+
///
530+
/// Representing type: Flex
531+
///
532+
/// Encoding: Flex
533+
flexVector,
534+
535+
/// Placeholder (not yet used) for a JSON document.
536+
///
537+
/// Representing type: String
538+
json,
539+
540+
/// Placeholder (not yet used) for a BSON document.
541+
///
542+
/// Representing type: ByteVector
543+
bson,
544+
545+
/// JavaScript source code.
546+
///
547+
/// Representing type: String
548+
javaScript,
549+
550+
/// A vector (array) of Int128 values.
551+
int128Vector,
552+
553+
/// A vector (array) of Int128 values
554+
uuidVector,
555+
556+
/// The 12-byte ObjectId type in MongoDB.
557+
///
558+
/// Representing type: ByteVector
559+
///
560+
/// Encoding: 1:1 binary representation (12 bytes)
561+
mongoId,
562+
563+
/// A vector (array) of MongoId values.
564+
mongoIdVector,
565+
566+
/// Representing type: Long
567+
///
568+
/// Encoding: Two unsigned 32-bit integers merged into a 64-bit integer.
569+
mongoTimestamp,
570+
571+
/// Representing type: ByteVector
572+
///
573+
/// Encoding: 3 zero bytes (reserved, functions as padding), fourth byte is
574+
/// the sub-type, followed by the binary data.
575+
mongoBinary,
576+
577+
/// Representing type: string vector with 2 elements (index 0: pattern,
578+
/// index 1: options)
579+
///
580+
/// Encoding: 1:1 string representation
581+
mongoRegex
582+
}
583+
584+
/// Annotates a property to be stored in an external system.
585+
class ExternalProperty {
586+
/// The name assigned to the property in the external system.
587+
final String? name;
588+
589+
/// The type of the property in the external system.
590+
/// See [ExternalType] for possible values.
591+
final ExternalType type;
592+
593+
/// Create an ExternalProperty annotation.
594+
const ExternalProperty({required this.type, this.name});
595+
}

objectbox/lib/src/modelinfo/enums.dart

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,112 @@ abstract class OBXPropertyType {
239239
/// < Variable sized vector of Date values (high precision 64-bit timestamp).
240240
static const int DateNanoVector = 32;
241241
}
242+
243+
int externalTypeToOBXExternalType(ExternalType type) {
244+
switch (type) {
245+
case ExternalType.int128:
246+
return OBXExternalPropertyType.Int128;
247+
case ExternalType.uuid:
248+
return OBXExternalPropertyType.Uuid;
249+
case ExternalType.decimal128:
250+
return OBXExternalPropertyType.Decimal128;
251+
case ExternalType.flexMap:
252+
return OBXExternalPropertyType.FlexMap;
253+
case ExternalType.flexVector:
254+
return OBXExternalPropertyType.FlexVector;
255+
case ExternalType.json:
256+
return OBXExternalPropertyType.Json;
257+
case ExternalType.bson:
258+
return OBXExternalPropertyType.Bson;
259+
case ExternalType.javaScript:
260+
return OBXExternalPropertyType.JavaScript;
261+
case ExternalType.int128Vector:
262+
return OBXExternalPropertyType.Int128Vector;
263+
case ExternalType.uuidVector:
264+
return OBXExternalPropertyType.UuidVector;
265+
case ExternalType.mongoId:
266+
return OBXExternalPropertyType.MongoId;
267+
case ExternalType.mongoIdVector:
268+
return OBXExternalPropertyType.MongoIdVector;
269+
case ExternalType.mongoTimestamp:
270+
return OBXExternalPropertyType.MongoTimestamp;
271+
case ExternalType.mongoBinary:
272+
return OBXExternalPropertyType.MongoBinary;
273+
case ExternalType.mongoRegex:
274+
return OBXExternalPropertyType.MongoRegex;
275+
default:
276+
throw ArgumentError.value(type, 'type', 'Invalid ExternalType');
277+
}
278+
}
279+
280+
/// A property type of an external system (e.g. another database) that has no default mapping to an ObjectBox type.
281+
/// External property types numeric values start at 100 to avoid overlaps with ObjectBox's PropertyType.
282+
/// (And if we ever support one of these as a primary type, we could share the numeric value?)
283+
abstract class OBXExternalPropertyType {
284+
/// Not a real type: represents uninitialized state and can be used for forward compatibility.
285+
static const int Unknown = 0;
286+
287+
/// Representing type: ByteVector
288+
/// Encoding: 1:1 binary representation, little endian (16 bytes)
289+
static const int Int128 = 100;
290+
291+
/// Representing type: ByteVector
292+
/// Encoding: 1:1 binary representation (16 bytes)
293+
static const int Uuid = 102;
294+
295+
/// IEEE 754 decimal128 type, e.g. supported by MongoDB
296+
/// Representing type: ByteVector
297+
/// Encoding: 1:1 binary representation (16 bytes)
298+
static const int Decimal128 = 103;
299+
300+
/// A key/value map; e.g. corresponds to a JSON object or a MongoDB document (although not keeping the key order).
301+
/// Unlike the Flex type, this must contain a map value (e.g. not a vector or a scalar).
302+
/// Representing type: Flex
303+
/// Encoding: Flex
304+
static const int FlexMap = 107;
305+
306+
/// A vector (aka list or array) of flexible elements; e.g. corresponds to a JSON array or a MongoDB array.
307+
/// Unlike the Flex type, this must contain a vector value (e.g. not a map or a scalar).
308+
/// Representing type: Flex
309+
/// Encoding: Flex
310+
static const int FlexVector = 108;
311+
312+
/// Placeholder (not yet used) for a JSON document.
313+
/// Representing type: String
314+
static const int Json = 109;
315+
316+
/// Placeholder (not yet used) for a BSON document.
317+
/// Representing type: ByteVector
318+
static const int Bson = 110;
319+
320+
/// JavaScript source code
321+
/// Representing type: String
322+
static const int JavaScript = 111;
323+
324+
/// A vector (array) of Int128 values
325+
static const int Int128Vector = 116;
326+
327+
/// A vector (array) of Int128 values
328+
static const int UuidVector = 118;
329+
330+
/// The 12-byte ObjectId type in MongoDB
331+
/// Representing type: ByteVector
332+
/// Encoding: 1:1 binary representation (12 bytes)
333+
static const int MongoId = 123;
334+
335+
/// A vector (array) of MongoId values
336+
static const int MongoIdVector = 124;
337+
338+
/// Representing type: Long
339+
/// Encoding: Two unsigned 32-bit integers merged into a 64-bit integer.
340+
static const int MongoTimestamp = 125;
341+
342+
/// Representing type: ByteVector
343+
/// Encoding: 3 zero bytes (reserved, functions as padding), fourth byte is the sub-type,
344+
/// followed by the binary data.
345+
static const int MongoBinary = 126;
346+
347+
/// Representing type: string vector with 2 elements (index 0: pattern, index 1: options)
348+
/// Encoding: 1:1 string representation
349+
static const int MongoRegex = 127;
350+
}

objectbox/lib/src/modelinfo/modelproperty.dart

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class ModelProperty {
1616
ModelEntity? entity;
1717
String? relationTarget;
1818
ModelHnswParams? hnswParams;
19+
int? externalPropertyType;
20+
String? externalPropertyName;
1921

2022
/// Type used in the source dart code - used by the code generator.
2123
/// Starts with [_fieldReadOnlyPrefix] if the field (currently IDs only) is
@@ -114,7 +116,9 @@ class ModelProperty {
114116
required int flags,
115117
IdUid? indexId,
116118
this.relationTarget,
117-
this.hnswParams})
119+
this.hnswParams,
120+
this.externalPropertyName,
121+
this.externalPropertyType})
118122
: _name = name,
119123
_type = type,
120124
_flags = flags,
@@ -127,7 +131,9 @@ class ModelProperty {
127131
_dartFieldType = data['dartFieldType'] as String?,
128132
uidRequest = data['uidRequest'] as bool? ?? false,
129133
hnswParams = ModelHnswParams.fromMap(
130-
data['hnswParams'] as Map<String, dynamic>?) {
134+
data['hnswParams'] as Map<String, dynamic>?),
135+
externalPropertyType = data['externalType'] as int?,
136+
externalPropertyName = data['externalName'] as String? {
131137
name = data['name'] as String?;
132138
type = data['type'] as int?;
133139
flags = data['flags'] as int? ?? 0;
@@ -151,6 +157,12 @@ class ModelProperty {
151157
if (hnswParams != null) {
152158
ret['hnswParams'] = hnswParams!.toMap();
153159
}
160+
if (externalPropertyType != null) {
161+
ret['externalType'] = externalPropertyType;
162+
}
163+
if (externalPropertyName != null) {
164+
ret['externalName'] = externalPropertyName;
165+
}
154166
}
155167
return ret;
156168
}

objectbox/lib/src/native/model.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,16 @@ class Model {
115115
_cModel, hnswParams.vectorCacheHintSizeKB!));
116116
}
117117
}
118+
119+
final externalPropertyType = prop.externalPropertyType;
120+
if (externalPropertyType != null) {
121+
_check(C.model_property_external_type(_cModel, externalPropertyType));
122+
}
123+
final externalPropertyName = prop.externalPropertyName;
124+
if (externalPropertyName != null) {
125+
_check(C.model_property_external_name(
126+
_cModel, externalPropertyName.toNativeUtf8().cast()));
127+
}
118128
}
119129

120130
void addRelation(ModelRelation rel) {

0 commit comments

Comments
 (0)