问题
I have created a custom Set
Data type in go, which i am using to define one to many relationships. For example in my Schema, i have the following struct definition
type Doctor struct {
firstName string
lastName string
capabilities commons.Set
}
Here capabilities
is a set of strings which have the following values chat, audio, video
, with this setup i am trying to persist the above struct into MySQL
using the GORM
library, but when i do this i get the following error
panic: invalid sql type Set (interface) for mysql
goroutine 6 [running]:
catalog/vendor/github.com/jinzhu/gorm.(*mysql).DataTypeOf(0xc00027e8a0, 0xc00024d680, 0x8, 0x8)
/home/kumard/go/src/catalog/vendor/github.com/jinzhu/gorm/dialect_mysql.go:123 +0xce9
catalog/vendor/github.com/jinzhu/gorm.(*Scope).createTable(0xc000169400, 0xc14e60)
I know that i have to implement certain methods in order to achieve this, but i am not able to figure out which method/callback to implement here.
ThreadUnsafeSet Definition:
type threadUnsafeSet map[interface{}]struct{}
type OrderedPair struct {
First interface{}
Second interface{}
}
func newThreadUnsafeSet() threadUnsafeSet {
return make(threadUnsafeSet)
}
func (pair *OrderedPair) Equal(other OrderedPair) bool {
return pair.First == other.First && pair.Second == other.Second
}
func (set *threadUnsafeSet) Add(i interface{}) bool {
_, found := (*set)[i]
if found {
return false
}
(*set)[i] = struct{}{}
return true
}
func (set *threadUnsafeSet) Contains(i ...interface{}) bool {
for _, val := range i {
if _, ok := (*set)[val]; !ok {
return false
}
}
return true
}
func (set *threadUnsafeSet) Cardinality() int {
return len(*set)
}
func (set *threadUnsafeSet) Equal(other Set) bool {
_ = other.(*threadUnsafeSet)
if set.Cardinality() != other.Cardinality() {
return false
}
for elem := range *set {
if !other.Contains(elem){
return false
}
}
return true
}
func (set *threadUnsafeSet) IsSubSet(other Set) bool {
_ = other.(*threadUnsafeSet)
if set.Cardinality() > other.Cardinality() {
return false
}
for elem := range *set {
if !other.Contains(elem) {
return false
}
}
return true
}
func (set *threadUnsafeSet) IsProperSubSet(other Set) bool {
return set.IsSubSet(other) && !set.Equal(other)
}
func (set *threadUnsafeSet) IsSuperSet(other Set) bool {
return other.IsSubSet(set)
}
func (set *threadUnsafeSet) IsProperSuperSet(other Set) bool {
return set.IsSuperSet(other) && !set.Equal(other)
}
func (set *threadUnsafeSet) Union(other Set) Set {
o := other.(*threadUnsafeSet)
result := newThreadUnsafeSet()
for elem := range *set {
result.Add(elem)
}
for elem := range *o {
result.Add(elem)
}
return &result
}
func (set *threadUnsafeSet) Intersect(other Set) Set {
o := other.(*threadUnsafeSet)
intersection := newThreadUnsafeSet()
if set.Cardinality() < other.Cardinality() {
for elem := range *set {
if other.Contains(elem) {
intersection.Add(elem)
}
}
} else {
for elem := range *o {
if set.Contains(elem) {
intersection.Add(elem)
}
}
}
return &intersection
}
func (set *threadUnsafeSet) Difference(other Set) Set {
_ = other.(*threadUnsafeSet)
difference := newThreadUnsafeSet()
for elem := range *set {
if !other.Contains(elem) {
difference.Add(elem)
}
}
return &difference
}
func (set *threadUnsafeSet) SymmetricDifference(other Set) Set {
_ = other.(*threadUnsafeSet)
aDiff := set.Difference(other)
bDiff := other.Difference(set)
return aDiff.Difference(bDiff)
}
func (set *threadUnsafeSet) Clear(){
*set = newThreadUnsafeSet()
}
func (set *threadUnsafeSet) Remove(i interface{}) {
delete(*set, i)
}
func (set *threadUnsafeSet) Each(cb func(interface{}) bool) {
for elem := range *set {
if cb(elem) {
break
}
}
}
func (set *threadUnsafeSet) Iter() <-chan interface{} {
ch := make(chan interface{})
go func() {
for elem := range *set {
ch <- elem
}
close(ch)
}()
return ch
}
func (set *threadUnsafeSet) Iterator() *commons.Iterator {
iterator, ch, stopCh := commons.NewIterator()
go func (){
L:
for elem := range *set {
select {
case <-stopCh: {
break L
}
case ch <- elem:
}
close(ch)
}
}()
return iterator
}
func (set *threadUnsafeSet) Clone() Set {
clonedSet := newThreadUnsafeSet()
for elem := range *set {
clonedSet.Add(elem)
}
return &clonedSet
}
func (set *threadUnsafeSet) String() string {
items := make([]string, 0, len(*set))
for elem := range *set {
items = append(items, fmt.Sprintf("%v", elem))
}
return fmt.Sprintf("Set{%s}", strings.Join(items, ","))
}
func (pair OrderedPair) String() string {
return fmt.Sprintf("(%v, %v)", pair.First, pair.Second)
}
func (set *threadUnsafeSet) Pop() interface{} {
for item := range *set {
delete (*set, item)
return item
}
return nil
}
func (set *threadUnsafeSet) PowerSet() Set {
powSet := NewThreadUnsafeSet()
nullSet := newThreadUnsafeSet()
powSet.Add(&nullSet)
for _, v := range i {
switch t := v.(type) {
case []interface{}, map[string]interface{}:
continue
default:
set.Add(t)
}
}
return nil
}
func (set *threadUnsafeSet) FindAny() fi.Optional {
for elem := range *set {
return fi.MakeNullable(elem)
}
return fi.MakeNullable(nil)
}
func (set *threadUnsafeSet) FindFirst(predicate func(interface{}) bool) fi.Optional {
for elem := range *set {
if predicate(elem) {
return fi.MakeNullable(elem)
}
}
return fi.MakeNullable(nil)
}
func (set *threadUnsafeSet) RemoveAll(elementsToRemove ... interface{}) {
for _, elem := range elementsToRemove {
if set.Contains(elem) {
set.Remove(elem)
}
}
}
for es := range *set {
u := newThreadUnsafeSet()
j := powSet.Iter()
for err := range j {
p := newThreadUnsafeSet()
if reflect.TypeOf(err).Name() == "" {
k := err.(*threadUnsafeSet)
for ek := range *(k){
p.Add(ek)
}
}else {
p.Add(err)
}
p.Add(es)
u.Add(&p)
}
powSet = powSet.Union(&u)
}
return powSet
}
func (set *threadUnsafeSet) CartesianProduct(other Set) Set {
o := other.(*threadUnsafeSet)
cartProduct := NewThreadUnsafeSet()
for i := range *set {
for j := range *o {
elem := OrderedPair{First: i, Second: j}
cartProduct.Add(elem)
}
}
return cartProduct
}
func (set *threadUnsafeSet) ToSlice() []interface{}{
keys := make([]interface{}, 0, set.Cardinality())
for elem := range *set {
keys = append(keys, elem)
}
return keys
}
func (set *threadUnsafeSet) MarshalJSON() ([]byte, error) {
items := make([]string, 0, set.Cardinality())
for elem := range *set {
b, err := json.Marshal(elem)
if err != nil {
return nil, err
}
items = append(items, string(b))
}
return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil
}
func (set *threadUnsafeSet) UnMarshalJSON(b []byte) error {
var i []interface{}
d := json.NewDecoder(bytes.NewReader(b))
d.UseNumber()
err := d.Decode(&i)
if err != nil {
return err
}
Set Interface definition
type Set interface {
Add(i interface{}) bool
Cardinality() int
Clear()
Clone() Set
Contains(i ...interface{}) bool
Difference(other Set) Set
Equal(other Set) bool
Intersect(other Set) Set
IsProperSubSet(other Set) bool
IsSubSet(other Set) bool
IsSuperSet(other Set) bool
Each(func(interface{}) bool)
Iter() <-chan interface{}
Iterator() *commons.Iterator
Remove(i interface{})
String() string
SymmetricDifference(other Set) Set
Union(other Set) Set
Pop() interface{}
PowerSet() Set
CartesianProduct(other Set) Set
ToSlice() []interface{}
FindAny() fi.Optional
FindFirst(predicate func(interface{}) bool) fi.Optional
RemoveAll(...interface{})
}
// NewSet creates and returns a reference to an empty set. Operations
// on the resulting set are thread-safe.
func NewSet(s ...interface{}) Set {
set := newThreadSafeSet()
for _, item := range s {
set.Add(item)
}
return &set
}
// NewSetWith creates and returns a new set with the given elements.
// Operations on the resulting set are thread-safe.
func NewSetWith(elts ...interface{}) Set {
return NewSetFromSlice(elts)
}
// NewSetFromSlice creates and returns a reference to a set from an
// existing slice. Operations on the resulting set are thread-safe.
func NewSetFromSlice(s []interface{}) Set {
a := NewSet(s...)
return a
}
// NewThreadUnsafeSet creates and returns a reference to an empty set.
// Operations on the resulting set are not thread-safe.
func NewThreadUnsafeSet() Set {
set := newThreadUnsafeSet()
return &set
}
// NewThreadUnsafeSetFromSlice creates and returns a reference to a
// set from an existing slice. Operations on the resulting set are
// not thread-safe.
func NewThreadUnsafeSetFromSlice(s []interface{}) Set {
a := NewThreadUnsafeSet()
for _, item := range s {
a.Add(item)
}
return a
}
回答1:
You need to implement Scanner & Driver Valuer interface for the custom type then database driver could know how to store the data in the database and how to get the data from the database.
func (data *CustomType) Value() (driver.Value, error) {
...
}
func (data *CustomType) Scan(value interface{}) error {
...
}
Example:
Suppose UserAccess is map[interface{}]struct{}
type.
type UserAccess map[interface{}]struct{}
func (data *UserAccess) Value() (driver.Value, error) {
return data.ConvertJSONToString(), nil
}
func (data *UserAccess) Scan(value interface{}) error {
*data = data.ConvertStringToJson(valueString)
}
Here ConvertStringToJson
and ConvertJSONToString
used to convert custom datatype value into database compatible type like json-string.
来源:https://stackoverflow.com/questions/61073209/persisting-custom-set-data-type-using-gorm-golang