MongoDB
 sql >> Teknologi Basis Data >  >> NoSQL >> MongoDB

Bagaimana cara mengabaikan nol saat membongkar dokumen MongoDB?

Masalahnya adalah codec bson saat ini tidak mendukung encoding / decoding string ke / dari null .

Salah satu cara untuk mengatasinya adalah dengan membuat dekoder khusus untuk string ketik di mana kami menangani null nilai:kami hanya menggunakan string kosong (dan yang lebih penting tidak melaporkan kesalahan).

Dekoder khusus dijelaskan dengan jenis bsoncodec.ValueDecoder . Mereka dapat didaftarkan di bsoncodec.Registry , menggunakan bsoncodec.RegistryBuilder misalnya.

Registri dapat disetel / diterapkan pada beberapa level, bahkan ke seluruh mongo.Client , atau ke mongo.Database atau hanya ke mongo.Collection , saat memperolehnya, sebagai bagian dari opsinya, mis. options.ClientOptions.SetRegistry() .

Pertama mari kita lihat bagaimana kita bisa melakukan ini untuk string , dan selanjutnya kita akan melihat cara meningkatkan / menggeneralisasi solusi ke semua jenis.

1. Menangani null string

Pertama-tama, mari buat dekoder string khusus yang dapat mengubah null menjadi a(n kosong) string:

import (
    "go.mongodb.org/mongo-driver/bson/bsoncodec"
    "go.mongodb.org/mongo-driver/bson/bsonrw"
    "go.mongodb.org/mongo-driver/bson/bsontype"
)

type nullawareStrDecoder struct{}

func (nullawareStrDecoder) DecodeValue(dctx bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
    if !val.CanSet() || val.Kind() != reflect.String {
        return errors.New("bad type or not settable")
    }
    var str string
    var err error
    switch vr.Type() {
    case bsontype.String:
        if str, err = vr.ReadString(); err != nil {
            return err
        }
    case bsontype.Null: // THIS IS THE MISSING PIECE TO HANDLE NULL!
        if err = vr.ReadNull(); err != nil {
            return err
        }
    default:
        return fmt.Errorf("cannot decode %v into a string type", vr.Type())
    }

    val.SetString(str)
    return nil
}

Oke, dan sekarang mari kita lihat cara menggunakan dekoder string khusus ini ke mongo.Client :

clientOpts := options.Client().
    ApplyURI("mongodb://localhost:27017/").
    SetRegistry(
        bson.NewRegistryBuilder().
            RegisterDecoder(reflect.TypeOf(""), nullawareStrDecoder{}).
            Build(),
    )
client, err := mongo.Connect(ctx, clientOpts)

Mulai sekarang, gunakan client ini , setiap kali Anda mendekode hasil menjadi string nilai, nullawareStrDecoder ini terdaftar decoder akan dipanggil untuk menangani konversi, yang menerima bson null nilai dan setel string kosong Go "" .

Tapi kita bisa melakukan yang lebih baik... Baca terus...

2. Menangani null nilai jenis apa pun:dekoder null-aware "type-neutral"

Salah satu caranya adalah dengan membuat dekoder khusus yang terpisah dan mendaftarkannya untuk setiap jenis yang ingin kita tangani. Sepertinya itu banyak pekerjaan.

Apa yang mungkin (dan harus) kami lakukan adalah membuat satu dekoder khusus "type-neutral" yang hanya menangani null s, dan jika nilai BSON bukan null , harus memanggil dekoder default untuk menangani non-null nilai.

Ini sangat sederhana:

type nullawareDecoder struct {
    defDecoder bsoncodec.ValueDecoder
    zeroValue  reflect.Value
}

func (d *nullawareDecoder) DecodeValue(dctx bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
    if vr.Type() != bsontype.Null {
        return d.defDecoder.DecodeValue(dctx, vr, val)
    }

    if !val.CanSet() {
        return errors.New("value not settable")
    }
    if err := vr.ReadNull(); err != nil {
        return err
    }
    // Set the zero value of val's type:
    val.Set(d.zeroValue)
    return nil
}

Kita hanya perlu mencari tahu apa yang akan digunakan untuk nullawareDecoder.defDecoder . Untuk ini kami dapat menggunakan registri default:bson.DefaultRegistry , kami mungkin mencari dekoder default untuk masing-masing tipe. Keren.

Jadi yang kita lakukan sekarang adalah mendaftarkan nilai nullawareDecoder our untuk semua tipe yang ingin kita tangani null untuk. Tidak sesulit itu. Kami hanya mencantumkan tipe (atau nilai dari tipe tersebut) yang kami inginkan, dan kami dapat menangani semuanya dengan loop sederhana:

customValues := []interface{}{
    "",       // string
    int(0),   // int
    int32(0), // int32
}

rb := bson.NewRegistryBuilder()
for _, v := range customValues {
    t := reflect.TypeOf(v)
    defDecoder, err := bson.DefaultRegistry.LookupDecoder(t)
    if err != nil {
        panic(err)
    }
    rb.RegisterDecoder(t, &nullawareDecoder{defDecoder, reflect.Zero(t)})
}

clientOpts := options.Client().
    ApplyURI("mongodb://localhost:27017/").
    SetRegistry(rb.Build())
client, err := mongo.Connect(ctx, clientOpts)

Pada contoh di atas saya mendaftarkan dekoder yang sadar-nol untuk string , int dan int32 , tetapi Anda dapat memperluas daftar ini sesuai keinginan Anda, cukup tambahkan nilai dari jenis yang diinginkan ke customValues irisan di atas.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoDB:Menghitung berapa banyak dari setiap nilai berbeda yang ada?

  2. Pengoptimalan kueri MongoDB

  3. Buat Indeks Multikey di MongoDB

  4. cara menggunakan fungsi agregat di meteor

  5. Mengapa Mongoose memiliki skema dan model?