Skip to content

Commit 021cf3a

Browse files
committed
Merge branch '10-flex-types' into dev
2 parents aabf170 + 445a6e6 commit 021cf3a

File tree

6 files changed

+125
-9
lines changed

6 files changed

+125
-9
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: 21 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._ob_type == OBXPropertyType_Flex:
100+
return None
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,11 @@ 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+
flex_builder = flatbuffers.flexbuffers.Builder()
132+
flex_builder.Add(val)
133+
buffer = flex_builder.Finish()
134+
offsets[prop._id] = builder.CreateByteVector(bytes(buffer))
125135
else:
126136
assert False, "programming error - invalid type OB & FB type combination"
127137

@@ -186,6 +196,13 @@ def unmarshal(self, data: bytes):
186196
val = table.GetVectorAsNumpy(flatbuffers.number_types.Float32Flags, o)
187197
elif prop._ob_type == OBXPropertyType_DoubleVector:
188198
val = table.GetVectorAsNumpy(flatbuffers.number_types.Float64Flags, o)
199+
elif prop._ob_type == OBXPropertyType_Flex:
200+
# access the FB byte vector information
201+
start = table.Vector(o)
202+
size = table.VectorLen(o)
203+
# slice the vector as bytes
204+
buf = table.Bytes[start:start+size]
205+
val = flatbuffers.flexbuffers.Loads(buf)
189206
else:
190207
val = table.Get(prop._fb_type, o + table.Pos)
191208
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: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import shutil
44
import pytest
5-
from tests.model import TestEntity, TestEntityDatetime
5+
from tests.model import TestEntity, TestEntityDatetime, TestEntityFlex
66
import numpy as np
77

88
test_dir = 'testdata'
@@ -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
@@ -35,14 +35,25 @@ def load_empty_test_objectbox(name: str = "") -> objectbox.ObjectBox:
3535
def load_empty_test_datetime(name: str = "") -> objectbox.ObjectBox:
3636
model = objectbox.Model()
3737
from objectbox.model import IdUid
38-
model.entity(TestEntityDatetime, last_property_id=IdUid(3, 2003))
38+
model.entity(TestEntityDatetime, last_property_id=IdUid(4, 2004))
3939
model.last_entity_id = IdUid(2, 2)
4040

4141
db_name = test_dir if len(name) == 0 else test_dir + "/" + name
4242

4343
return objectbox.Builder().model(model).directory(db_name).build()
4444

4545

46+
def load_empty_test_flex(name: str = "") -> objectbox.ObjectBox:
47+
model = objectbox.Model()
48+
from objectbox.model import IdUid
49+
model.entity(TestEntityFlex, last_property_id=IdUid(3, 3003))
50+
model.last_entity_id = IdUid(3, 3)
51+
52+
db_name = test_dir if len(name) == 0 else test_dir + "/" + name
53+
54+
return objectbox.Builder().model(model).directory(db_name).build()
55+
56+
4657
def assert_equal_prop(actual, expected, default):
4758
assert actual == expected or (isinstance(
4859
expected, objectbox.model.Property) and actual == default)
@@ -75,4 +86,5 @@ def assert_equal(actual: TestEntity, expected: TestEntity):
7586
assert_equal_prop_approx(actual.floats_list, expected.floats_list, [])
7687
assert_equal_prop_approx(actual.doubles_list, expected.doubles_list, [])
7788
assert_equal_prop_approx(actual.date, expected.date, 0)
78-
assert_equal_prop(actual.date_nano, expected.date_nano, 0)
89+
assert_equal_prop(actual.date_nano, expected.date_nano, 0)
90+
assert_equal_prop(actual.flex, expected.flex, None)

tests/model.py

Lines changed: 11 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, Dict, Any
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 = ""):
@@ -38,3 +40,12 @@ class TestEntityDatetime:
3840

3941
def __init__(self, string: str = ""):
4042
self.str = string
43+
44+
@Entity(id=3, uid=3)
45+
class TestEntityFlex:
46+
id = Id(id=1, uid=3001)
47+
flex_dict = Property(Dict[str, Any], type=PropertyType.flex, id=2, uid=3002)
48+
flex_int = Property(int, type=PropertyType.flex, id=3, uid=3003)
49+
50+
def __init__(self, string: str = ""):
51+
self.str = string

tests/test_box.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import pytest
22
import objectbox
3-
from tests.model import TestEntity, TestEntityDatetime
3+
from tests.model import TestEntity, TestEntityDatetime, TestEntityFlex
44
from tests.common import (
55
autocleanup,
66
load_empty_test_objectbox,
77
load_empty_test_datetime,
8+
load_empty_test_flex,
89
assert_equal,
910
)
1011
import numpy as np
@@ -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,74 @@ def test_datetime():
174176
box.get(1)
175177

176178
ob.close()
179+
180+
181+
def test_flex():
182+
183+
def test_put_get(object: TestEntity, box: objectbox.Box, property):
184+
object.flex = property
185+
id = box.put(object)
186+
assert id == object.id
187+
read = box.get(object.id)
188+
assert read.flex == object.flex
189+
190+
ob = load_empty_test_objectbox()
191+
box = objectbox.Box(ob, TestEntity)
192+
object = TestEntity()
193+
194+
# Put an empty object
195+
id = box.put(object)
196+
assert id == object.id
197+
198+
# Put a None type object
199+
test_put_get(object, box, None)
200+
201+
# Update to int
202+
test_put_get(object, box, 1)
203+
204+
# Update to float
205+
test_put_get(object, box, 1.2)
206+
207+
# Update to string
208+
test_put_get(object, box, "foo")
209+
210+
# Update to int list
211+
test_put_get(object, box, [1, 2, 3])
212+
213+
# Update to float list
214+
test_put_get(object, box, [1.1, 2.2, 3.3])
215+
216+
# Update to dict
217+
test_put_get(object, box, {"a": 1, "b": 2})
218+
219+
# Update to bool
220+
test_put_get(object, box, True)
221+
222+
# Update to dict inside dict
223+
test_put_get(object, box, {"a": 1, "b": {"c": 2}})
224+
225+
# Update to list inside dict
226+
test_put_get(object, box, {"a": 1, "b": [1, 2, 3]})
227+
228+
ob.close()
229+
230+
231+
def test_flex_dict():
232+
ob = load_empty_test_flex()
233+
box = objectbox.Box(ob, TestEntityFlex)
234+
object = TestEntityFlex()
235+
236+
# Put an empty object
237+
id = box.put(object)
238+
assert id == object.id
239+
read = box.get(object.id)
240+
assert read.flex_dict == None
241+
assert read.flex_int == None
242+
243+
object.flex_dict = {"a": 1, "b": 2}
244+
object.flex_int = 25
245+
id = box.put(object)
246+
assert id == object.id
247+
read = box.get(object.id)
248+
assert read.flex_dict == object.flex_dict
249+
assert read.flex_int == object.flex_int

0 commit comments

Comments
 (0)