Custom types
Sometimes, you need to store a type that can't be handled out of the box. ObjectBox let's you define a custom converter (a pair of functions) that takes care of encoding & decoding properties.
The following built-in types, their aliases and named types based on them are recognized as ObjectBox and stored as an appropriate internal type:
1
int, int8, int16, int32, int64
2
uint, uint8, uint16, uint32, uint64
3
bool
4
string, []string
5
byte, []byte
6
rune
7
float32, float64
Copied!

Defining a converter

To add support for a custom type, you can map properties to one of the built-in types using a converter annotation.
For example, you could define a color in your entity using a custom Color struct and map it to an int32. Or you can map the time.Time to an int64, though losing some precision - less than a millisecond, i. e. a thousandth of a second):
1
type Task struct {
2
Id uint64
3
Text string
4
DateCreated time.Time `objectbox:"date type:int64 converter:timeInt64"`
5
}
Copied!
In the entity definition above, we instruct ObjectBox to store the DateCreated field as a int64 while converting it to/from time.Time when using in the program. ObjectBox will generate a binding code that will call the following two functions (both start with the prefix timeInt64 specified above):
1
// from DB value to runtime value
2
func timeInt64ToEntityProperty(dbValue int64) (time.Time, error)
3
4
// from runtime value to DB value
5
func timeInt64ToDatabaseValue(goValue time.Time) (int64, error)
Copied!
Just to complete the example, those functions could be implemented like this:
1
// converts Unix timestamp in milliseconds (ObjectBox date field format) to time.Time
2
func timeInt64ToEntityProperty(dbValue int64) (goValue time.Time, err error) {
3
err = goValue.UnmarshalText([]byte(dbValue))
4
if err != nil {
5
err = fmt.Errorf("error unmarshalling time %v: %v", dbValue, err)
6
}
7
return goValue, err
8
}
9
10
// converts time.Time to Unix timestamp in milliseconds
11
// i. e. internal format expected by ObjectBox on a date field
12
func timeInt64ToDatabaseValue(goValue time.Time) (int64, error) {
13
var ms = int64(goValue.Nanosecond()) / 1000000
14
return goValue.Unix()*1000 + ms, nil
15
}
Copied!
Actually this converter for time.Time is already part of the objectbox package and used automatically when you mark a time.Time property with `objectbox:"date"`.

Queries on custom types

When you use a converter, the actual value stored in the database is the result of the ...ToDatabaseValue() call, e.g. int64 in the previous example. Therefore, when you want to compare the stored data in a query condition, make sure you use the converted value as well:
1
// Create
2
id, _ := box.Put(&model.Task{
3
Text: "Buy milk",
4
DateCreated: time.Now().UTC()
5
})
6
7
// Query
8
minTime, _ := time.Parse(time.RFC3339, "2018-11-28T12:16:42.145+07:00")
9
minTimeInt64, _ := objectbox.TimeInt64ConvertToDatabaseValue(minTime)
10
tasks, _ := box.Query(
11
model.Task_.DateCreated.GreaterThan(minTimeInt64)
12
).Find()
Copied!

Things to look out for

You must not interact with the database (such as using Box or ObjectBox) inside the converter. The converter methods are called within a transaction, so for example getting or putting entities to a box will fail.
Your converter implementation must be thread safe as it can be called from multiple go routines in parallel. Try to avoid using global variables.
Query is unaware of custom types. You have to use the primitive DB type for queries.
Last modified 1yr ago