Skip to content

Commit f6bb4a8

Browse files
committed
Add support for flex types #10
1 parent 77fb331 commit f6bb4a8

File tree

6 files changed

+69
-6
lines changed

6 files changed

+69
-6
lines changed

objectbox/c.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ def c_voidp_as_bytes(voidp, size):
395395
OBXPropertyType_Date = 10
396396
OBXPropertyType_Relation = 11
397397
OBXPropertyType_DateNano = 12
398+
OBXPropertyType_Flex = 13
398399
OBXPropertyType_ByteVector = 23
399400
OBXPropertyType_IntVector = 26
400401
OBXPropertyType_LongVector = 27

objectbox/model/entity.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515

1616
import flatbuffers
17+
import flatbuffers.flexbuffers
18+
from typing import Generic
1719
import numpy as np
1820
from math import floor
1921
from datetime import datetime
@@ -74,6 +76,7 @@ def fill_properties(self):
7476
OBXPropertyType_LongVector,
7577
OBXPropertyType_FloatVector,
7678
OBXPropertyType_DoubleVector,
79+
OBXPropertyType_Flex,
7780
], "programming error - invalid type OB & FB type combination"
7881
self.offset_properties.append(prop)
7982

@@ -90,11 +93,13 @@ def get_value(self, object, prop: Property):
9093
if prop._py_type == np.ndarray:
9194
if (val == np.array(prop)).all():
9295
return np.array([])
93-
elif prop._py_type == datetime:
94-
if val == prop:
95-
return datetime.fromtimestamp(0)
9696
elif val == prop:
97-
return prop._py_type() # default (empty) value for the given type
97+
if prop._py_type == datetime:
98+
return datetime.fromtimestamp(0)
99+
if prop._py_type == Generic:
100+
return {}
101+
else:
102+
return prop._py_type() # default (empty) value for the given type
98103
return val
99104

100105
def get_object_id(self, object) -> int:
@@ -122,6 +127,9 @@ def marshal(self, object, id: int) -> bytearray:
122127
offsets[prop._id] = builder.CreateNumpyVector(np.array(val, dtype=np.float32))
123128
elif prop._ob_type == OBXPropertyType_DoubleVector:
124129
offsets[prop._id] = builder.CreateNumpyVector(np.array(val, dtype=np.float64))
130+
elif prop._ob_type == OBXPropertyType_Flex:
131+
buffer = flatbuffers.flexbuffers.Dumps(val)
132+
offsets[prop._id] = builder.CreateByteVector(bytes(buffer))
125133
else:
126134
assert False, "programming error - invalid type OB & FB type combination"
127135

@@ -186,6 +194,13 @@ def unmarshal(self, data: bytes):
186194
val = table.GetVectorAsNumpy(flatbuffers.number_types.Float32Flags, o)
187195
elif prop._ob_type == OBXPropertyType_DoubleVector:
188196
val = table.GetVectorAsNumpy(flatbuffers.number_types.Float64Flags, o)
197+
elif prop._ob_type == OBXPropertyType_Flex:
198+
# access the FB byte vector information
199+
start = table.Vector(o)
200+
size = table.VectorLen(o)
201+
# slice the vector as bytes
202+
buf = table.Bytes[start:start+size]
203+
val = flatbuffers.flexbuffers.Loads(buf)
189204
else:
190205
val = table.Get(prop._fb_type, o + table.Pos)
191206
if prop._py_type == list:

objectbox/model/properties.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class PropertyType(IntEnum):
3131
string = OBXPropertyType_String
3232
date = OBXPropertyType_Date
3333
dateNano = OBXPropertyType_DateNano
34+
flex = OBXPropertyType_Flex
3435
# relation = OBXPropertyType_Relation
3536
byteVector = OBXPropertyType_ByteVector
3637
intVector = OBXPropertyType_IntVector
@@ -52,6 +53,7 @@ class PropertyType(IntEnum):
5253
PropertyType.string: flatbuffers.number_types.UOffsetTFlags,
5354
PropertyType.date: flatbuffers.number_types.Int64Flags,
5455
PropertyType.dateNano: flatbuffers.number_types.Int64Flags,
56+
PropertyType.flex: flatbuffers.number_types.UOffsetTFlags,
5557
# PropertyType.relation: flatbuffers.number_types.Int64Flags,
5658
PropertyType.byteVector: flatbuffers.number_types.UOffsetTFlags,
5759
PropertyType.intVector: flatbuffers.number_types.UOffsetTFlags,

tests/common.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def autocleanup():
2424
def load_empty_test_objectbox(name: str = "") -> objectbox.ObjectBox:
2525
model = objectbox.Model()
2626
from objectbox.model import IdUid
27-
model.entity(TestEntity, last_property_id=IdUid(20, 1020))
27+
model.entity(TestEntity, last_property_id=IdUid(21, 1021))
2828
model.last_entity_id = IdUid(2, 2)
2929

3030
db_name = test_dir if len(name) == 0 else test_dir + "/" + name
@@ -75,4 +75,13 @@ def assert_equal(actual: TestEntity, expected: TestEntity):
7575
assert_equal_prop_approx(actual.floats_list, expected.floats_list, [])
7676
assert_equal_prop_approx(actual.doubles_list, expected.doubles_list, [])
7777
assert_equal_prop_approx(actual.date, expected.date, 0)
78-
assert_equal_prop(actual.date_nano, expected.date_nano, 0)
78+
assert_equal_prop(actual.date_nano, expected.date_nano, 0)
79+
assert_equal_prop(actual.flex, expected.flex, {})
80+
81+
82+
def put_flex(object, box, property):
83+
object.flex = property
84+
id = box.put(object)
85+
assert id == object.id
86+
read = box.get(object.id)
87+
assert read.flex == object.flex

tests/model.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from objectbox.model import *
22
import numpy as np
33
from datetime import datetime
4+
from typing import Generic
45

56

67
@Entity(id=1, uid=1)
@@ -25,6 +26,7 @@ class TestEntity:
2526
doubles_list = Property(list, type=PropertyType.doubleVector, id=18, uid=1018)
2627
date = Property(int, type=PropertyType.date, id=19, uid=1019)
2728
date_nano = Property(int, type=PropertyType.dateNano, id=20, uid=1020)
29+
flex = Property(Generic, type=PropertyType.flex, id=21, uid=1021)
2830
transient = "" # not "Property" so it's not stored
2931

3032
def __init__(self, string: str = ""):

tests/test_box.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
load_empty_test_objectbox,
77
load_empty_test_datetime,
88
assert_equal,
9+
put_flex,
910
)
1011
import numpy as np
1112
from datetime import datetime
@@ -48,6 +49,7 @@ def test_box_basics():
4849
object.doubles_list = [99.1999, 88.2888, 77.3777, 66.4666, 55.6597555]
4950
object.date = time.time() * 1000 # milliseconds since UNIX epoch
5051
object.date_nano = time.time_ns() # nanoseconds since UNIX epoch
52+
object.flex = dict(a=1, b=2, c=3)
5153
object.transient = "abcd"
5254

5355
id = box.put(object)
@@ -174,3 +176,35 @@ def test_datetime():
174176
box.get(1)
175177

176178
ob.close()
179+
180+
181+
def test_flex():
182+
ob = load_empty_test_objectbox()
183+
box = objectbox.Box(ob, TestEntity)
184+
object = TestEntity()
185+
186+
# Put a None type first
187+
put_flex(object, box, None)
188+
189+
# Update to int
190+
put_flex(object, box, 1)
191+
192+
# Update to float
193+
put_flex(object, box, 1.2)
194+
195+
# Update to string
196+
put_flex(object, box, "foo")
197+
198+
# Update to int list
199+
put_flex(object, box, [1, 2, 3])
200+
201+
# Update to float list
202+
put_flex(object, box, [1.1, 2.2, 3.3])
203+
204+
# Update to dict
205+
put_flex(object, box, {"a": 1, "b": 2})
206+
207+
# Update to bool
208+
put_flex(object, box, True)
209+
210+
ob.close()

0 commit comments

Comments
 (0)