zeromicro / go-zero

A cloud-native Go microservices framework with cli tool for productivity.
https://go-zero.dev
MIT License
29.15k stars 3.94k forks source link

With MongoDB, how to convert between types of `decimal128` and `decimal.Decimal`? #3734

Open shanweie opened 11 months ago

shanweie commented 11 months ago

registry := bson.NewRegistry() registry.RegisterTypeEncoder(reflect.TypeOf(decimal.Decimal{}), &mongo.MongoDecimal{})

conn := mon.MustNewModel(url, db, collection, opts)

怎么设置opts,使用registry自动互转

POABOB commented 11 months ago

Mongo Options 的原始碼在這裡:https://github.com/zeromicro/go-zero/blob/master/core/stores/mon/options.go

import (
    "reflect"

    "go.mongodb.org/mongo-driver/bson"
    // 引入 go mongo-driver
    mopt "go.mongodb.org/mongo-driver/mongo/options"
    "github.com/zeromicro/go-zero/core/stores/mon"
)

registry := bson.NewRegistry()
registry.RegisterTypeEncoder(reflect.TypeOf(decimal.Decimal{}), &mongo.MongoDecimal{})
// 使用 SetRegistry 註冊
opts := mon.Option(mopt.SetRegistry(registry))   // opts 的 type 會是 Option,這是 go-zero 自己訂的 type
conn := mon.MustNewModel(url, db, collection, opts)

資料來源:

  1. https://blog.csdn.net/u013053623/article/details/129023275
  2. https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo/options#ClientOptions.SetRegistry
  3. https://jira.mongodb.org/browse/GODRIVER-2886
Issues-translate-bot commented 11 months ago

Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿


The source code of Mongo Options is here: https://github.com/zeromicro/go-zero/blob/master/core/stores/mon/options.go

import (
    "reflect"

    "go.mongodb.org/mongo-driver/bson"
    //Introduce go mongo-driver
    mopt "go.mongodb.org/mongo-driver/mongo/options"
    "github.com/zeromicro/go-zero/core/stores/mon"
)

registry := bson.NewRegistry()
registry.RegisterTypeEncoder(reflect.TypeOf(decimal.Decimal{}), &mongo.MongoDecimal{})
//Register using SetRegistry
opts := mon.Option(mopt.SetRegistry(registry)) // The type of opts will be Option, which is the type set by go-zero itself
conn := mon.MustNewModel(url, db, collection, opts)

source:

  1. https://blog.csdn.net/u013053623/article/details/129023275
  2. https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo/options#ClientOptions.SetRegistry
  3. https://jira.mongodb.org/browse/GODRIVER-2886
shanweie commented 11 months ago

mongo-driver v1.12.1,里面没有这个mopt.SetRegistry(registry)这个方法

Issues-translate-bot commented 11 months ago

Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿


mongo-driver v1.12.1, there is no mopt.SetRegistry(registry) method in it

POABOB commented 11 months ago

@kevwan Hey! I have a problem with transfering mongo decimal128 to decimal.Decimal. There doesn't seem to be a function that can set up the mongo configuration.

Could I make a pull request to add the setRegistry function for the mongo client on https://github.com/zeromicro/go-zero/blob/master/core/stores/mon/options.go?

core/stores/mon/options.go

// WithRegistry the mon client can transfer mongo types to custom types.
func WithRegistry(registry *bsoncodec.Registry) Option {
    return func(opts *options) {
        opts.SetRegistry(registry)
    }
}

To use this function, follow the example below:

registry := bson.NewRegistry()
registry.RegisterTypeEncoder(reflect.TypeOf(decimal.Decimal{}), MongoDecimal{})
// this code
opts := mon.WithRegistry(registry)
conn := mon.MustNewModel(url, db, collection, opts)
shanweie commented 11 months ago

试过了还是不行,错误为: cannot decode 128-bit decimal into a decimal.Decimal

registry := bson.NewRegistry() registry.RegisterTypeEncoder(reflect.TypeOf(decimal.Decimal{}), &mongo.MongoDecimal{}) conn := mon.MustNewModel(url, db, collection, mongo.WithRegistry(registry))

func WithRegistry(registry bsoncodec.Registry) mon.Option { return func(opts mopt.ClientOptions) { opts.SetRegistry(registry) } }

shanweie commented 11 months ago

mongo.mongoDecimal的代码为: ` package mongo

import ( "fmt" "github.com/shopspring/decimal" "github.com/zeromicro/go-zero/core/stores/mon" "go.mongodb.org/mongo-driver/bson/bsoncodec" "go.mongodb.org/mongo-driver/bson/bsonrw" "go.mongodb.org/mongo-driver/bson/primitive" mopt "go.mongodb.org/mongo-driver/mongo/options" "reflect" )

// MongoDecimal是一个允许十进制编码的ValueCodec,Decimal128到decimal.Decimal type MongoDecimal struct{}

var _ bsoncodec.ValueCodec = &MongoDecimal{}

func WithRegistry(registry bsoncodec.Registry) mon.Option { return func(opts mopt.ClientOptions) { opts.SetRegistry(registry) } }

func (dc *MongoDecimal) EncodeValue(ect bsoncodec.EncodeContext, w bsonrw.ValueWriter, value reflect.Value) error { // 使用反射转换,值改为 decimal.Decimal. dec, ok := value.Interface().(decimal.Decimal) if !ok { return fmt.Errorf("value %v to encode is not of type decimal.Decimal", value) }

// Convert decimal.Decimal to primitive.Decimal128.
primDec, err := primitive.ParseDecimal128(dec.String())
if err != nil {
    return fmt.Errorf("error converting decimal.Decimal %v to primitive.Decimal128: %v", dec, err)
}
return w.WriteDecimal128(primDec)

}

func (dc *MongoDecimal) DecodeValue(ect bsoncodec.DecodeContext, r bsonrw.ValueReader, value reflect.Value) error { primDec, err := r.ReadDecimal128() if err != nil { return fmt.Errorf("error reading primitive.Decimal128 from ValueReader: %v", err) }

// 将primitive.Decimal128转变为Golang的decimal.Decimal.
dec, err := decimal.NewFromString(primDec.String())
if err != nil {
    return fmt.Errorf("error converting primitive.Decimal128 %v to decimal.Decimal: %v", primDec, err)
}

// 设置值为 decimal.Decimal类型数据
value.Set(reflect.ValueOf(dec))
return nil

}

`

POABOB commented 11 months ago

@shanweie I found a problem that you only use registry.RegisterTypeEncoder, but there is no registry.RegisterTypeEncoder.

截圖 2023-11-22 下午9 15 36

You have to use BOTH so that you can transfering the Decimal to MongoDecimal.

截圖 2023-11-22 下午9 15 09