MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Как да игнорирате нулеви стойности, докато демарширате документ на MongoDB?

Проблемът е, че текущите bson кодеци не поддържат кодиране / декодиране на string в / от null .

Един от начините да се справите с това е да създадете персонализиран декодер за string тип, в който обработваме null стойности:ние просто използваме празния низ (и по-важното не съобщаваме за грешка).

Персонализираните декодери се описват от типа bsoncodec.ValueDecoder . Те могат да бъдат регистрирани в bsoncodec.Registry , като използвате bsoncodec.RegistryBuilder например.

Регистрите могат да се задават/прилагат на множество нива, дори към цял mongo.Client , или към mongo.Database или просто към mongo.Collection , при придобиването им, като част от техните опции, напр. options.ClientOptions.SetRegistry() .

Първо нека видим как можем да направим това за string , а след това ще видим как да подобрим/обобщим решението за всеки тип.

1. Обработка на null струни

Първо, нека създадем персонализиран декодер на низове, който може да превърне null в (n празен) низ:

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
}

Добре, а сега нека да видим как да използваме този персонализиран низ декодер за mongo.Client :

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

Отсега нататък, използвайки този client , когато декодирате резултатите в string стойности, този регистриран nullawareStrDecoder декодерът ще бъде извикан за обработка на преобразуването, което приема bson null стойности и задава празния низ "" .

Но можем да направим по-добре... Прочетете...

2. Обработка на null стойности от всякакъв тип:"type-neutral" декодер с нулева информация

Един от начините би бил да създадем отделен персонализиран декодер и да го регистрираме за всеки тип, с който искаме да работим. Това изглежда е много работа.

Това, което можем (и трябва) да направим вместо това, е да създадем един-единствен, "неутрален по тип" персонализиран декодер, който обработва само null s и ако стойността на BSON не е null , трябва да извика декодера по подразбиране, за да обработва не-null стойност.

Това е изненадващо просто:

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
}

Просто трябва да разберем какво да използваме за nullawareDecoder.defDecoder . За това можем да използваме системния регистър по подразбиране:bson.DefaultRegistry , можем да търсим декодера по подразбиране за отделни типове. Готино.

Така че това, което правим сега, е да регистрираме стойност на нашия nullawareDecoder за всички типове искаме да обработваме null s за. Не е толкова трудно. Ние просто изброяваме типовете (или стойностите на тези типове), за които искаме това, и можем да се погрижим за всичко с обикновен цикъл:

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)

В примера по-горе регистрирах нулеви декодери за string , int и int32 , но можете да разширите този списък по ваш вкус, просто добавете стойности на желаните типове към customValues парче по-горе.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Как да изберете поддокументи с MongoDB

  2. Как да използвам агрегирането на MongoDB за пагинация?

  3. Първи стъпки с PHP и MongoDB

  4. Docker mongo изображение „Връзката е отказана“ от друг контейнер

  5. mongodb премества документи от една колекция в друга колекция