ent
ent copied to clipboard
Support scan for json fields
Scan to ent object fails if one of your fields is a field of type json.
This happens because the struct type required for json type does not need to implement database/sql.Scanner interface (which is very convenient). Also the json type of Strings does not implement this interface as well.
- [x] I have searched the issues of this repository and believe that this is not a duplicate.
Summary 💡
If we wish to use example 2 that is shown in https://entgo.io/docs/feature-flags#custom-sql-modifiers:
var p1 []struct {
ent.Pet
NameLength int `sql:"length"`
}
client.Pet.Query().
Order(ent.Asc(pet.FieldID)).
Modify(func(s *sql.Selector) {
s.AppendSelect("LENGTH(name)")
}).
ScanX(ctx, &p1)
And Pet has the following definition:
func (Pet) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.Strings("nicknames"),
}
}
Then we'd get the following error:
sql/scan: failed scanning rows: sql: Scan error on column index 2, name \"nicknames\": unsupported Scan, storing driver.Value type []uint8 into type *[]string",
Currently my way to solve it is not to rely on ent.Pet and also implement wrapper type around []string that implements Scanner interface.
Example:
// JSONStrings is a sql.Scanner implementation that represents an array of strings as a `jsonb` DB column.
type JSONStrings []string
// Scan implements the sql.Scanner interface.
func (js *JSONStrings) Scan(value interface{}) error {
if value == nil {
*js = []string{}
return nil
}
if v, ok := value.([]uint8); ok {
var res []string
if len(v) > 0 {
if err := json.Unmarshal(v, &res); err != nil {
return fmt.Errorf("types: failed to unmarshal json strings: %v", err)
}
}
*js = res
return nil
}
return fmt.Errorf("value of json strings is incorrect. type=%T, value=%v", value, value)
}
var p1 []struct {
Name string `sql:"name"`
Nicknames JSONStrings `sql:"nicknames"`
NameLength int `sql:"length"`
}
client.Pet.Query().
Order(ent.Asc(pet.FieldID)).
Modify(func(s *sql.Selector) {
s.AppendSelect("LENGTH(name)")
}).
ScanX(ctx, &p1)
This is obviously very troubling for several reasons:
- It requires to implement the same scanner interface for each json type we use
- You can't use the ent objects created and have to duplicate field definitions in other structs
I'm not sure if it's possible but if Scan could parse json fields same it does for the regular APIs it would be great and save lots of code and inconvenience
Motivation 🔦
We use custom sql modifier a lot in order to select special expressions and join with other tables efficiently. However since many of our tables have some sort of json fields, it forces us to create lot of duplicate code which makes it hard to develop and maintain
It don't support jsonb filed Scan() now, has any method to resolve this question?
@a8m Could you help us?
Hey! Issue was resolved with https://github.com/ent/ent/pull/3022.
Thanks for reporting it, @naormatania 🙏