sql >> Database >  >> NoSQL >> MongoDB

Hoe nulls te negeren terwijl u een MongoDB-document ongedaan maakt?

Het probleem is dat de huidige bson-codecs geen ondersteuning bieden voor het coderen / decoderen van string naar / van null .

Een manier om dit aan te pakken is door een aangepaste decoder te maken voor string type waarin we null verwerken waarden:we gebruiken gewoon de lege string (en nog belangrijker, rapporteren geen fouten).

Aangepaste decoders worden beschreven door het type bsoncodec.ValueDecoder . Ze kunnen worden geregistreerd bij een bsoncodec.Registry , met behulp van een bsoncodec.RegistryBuilder bijvoorbeeld.

Registries kunnen op meerdere niveaus worden ingesteld / toegepast, zelfs op een hele mongo.Client , of naar een mongo.Database of gewoon naar een mongo.Collection , bij het verwerven ervan, als onderdeel van hun opties, b.v. options.ClientOptions.SetRegistry() .

Laten we eerst eens kijken hoe we dit kunnen doen voor string , en vervolgens zullen we zien hoe we de oplossing kunnen verbeteren / generaliseren naar elk type.

1. Omgaan met null snaren

Laten we allereerst een aangepaste stringdecoder maken die een null . kan veranderen in een (lege) tekenreeks:

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
}

OK, en laten we nu eens kijken hoe we deze aangepaste stringdecoder kunnen gebruiken voor een mongo.Client :

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

Gebruik vanaf nu deze client , telkens wanneer u resultaten decodeert in string waarden, deze geregistreerde nullawareStrDecoder decoder wordt aangeroepen om de conversie af te handelen, die bson null . accepteert waarden en stelt de Ga lege tekenreeks in "" .

Maar we kunnen beter... Lees verder...

2. Omgaan met null waarden van elk type:"type-neutrale" null-aware decoder

Een manier zou zijn om een ​​aparte, aangepaste decoder te maken en deze te registreren voor elk type dat we willen gebruiken. Dat lijkt veel werk te zijn.

Wat we in plaats daarvan kunnen (en moeten) doen, is een enkele, "type-neutrale" aangepaste decoder maken die alleen null verwerkt. s, en als de BSON-waarde niet null . is , zou de standaarddecoder moeten aanroepen om de niet-null . te verwerken waarde.

Dit is verrassend eenvoudig:

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
}

We moeten alleen uitzoeken wat we moeten gebruiken voor nullawareDecoder.defDecoder . Hiervoor kunnen we het standaardregister gebruiken:bson.DefaultRegistry , kunnen we de standaarddecoder voor afzonderlijke typen opzoeken. Cool.

Dus wat we nu doen is een waarde registreren van onze nullawareDecoder voor alle typen die we willen behandelen null voor. Het is niet zo moeilijk. We vermelden gewoon de typen (of waarden van die typen) waarvoor we dit willen, en we kunnen voor alles zorgen met een eenvoudige lus:

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)

In het bovenstaande voorbeeld heb ik null-aware decoders geregistreerd voor string , int en int32 , maar u kunt deze lijst naar wens uitbreiden, voeg gewoon waarden van de gewenste typen toe aan de customValues plak hierboven.



  1. AfschaffingWaarschuwing:het luisteren naar gebeurtenissen in de Db-klasse is beëindigd en wordt verwijderd in de volgende hoofdversie

  2. YCSB-laden starten met cluster ingeschakelde optie voor REDIS

  3. Alleen de waarde teruggeven in MongoDB?

  4. HBase Performance-testen met YCSB