sql >> Databasteknik >  >> NoSQL >> MongoDB

Hur ignorerar man nollor samtidigt som man tar upp ett MongoDB-dokument?

Problemet är att de nuvarande bson-codecna inte stöder kodning / avkodning av string till / från null .

Ett sätt att hantera detta är att skapa en anpassad avkodare för string typ som vi hanterar null värden:vi använder bara den tomma strängen (och ännu viktigare rapporterar inte fel).

Anpassade avkodare beskrivs av typen bsoncodec.ValueDecoder . De kan registreras i ett bsoncodec.Registry , med en bsoncodec.RegistryBuilder till exempel.

Register kan ställas in/tillämpas på flera nivåer, även på en hel mongo.Client , eller till en mongo.Database eller bara till en mongo.Collection , när de förvärvar dem, som en del av deras optioner, t.ex. options.ClientOptions.SetRegistry() .

Låt oss först se hur vi kan göra detta för string , och härnäst ska vi se hur vi kan förbättra / generalisera lösningen till vilken typ som helst.

1. Hanterar null strängar

Först och främst, låt oss skapa en anpassad strängavkodare som kan göra en null i en (n tom) sträng:

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, och nu ska vi se hur man använder den här anpassade strängavkodaren till en mongo.Client :

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

Från och med nu använder du denna client , när du avkodar resultat till string värden, denna registrerade nullawareStrDecoder dekoder kommer att anropas för att hantera konverteringen, som accepterar bson null värden och ställer in Go tom-strängen "" .

Men vi kan göra bättre... Läs vidare...

2. Hanterar null värden av vilken typ som helst:"typneutral" nollmedveten dekoder

Ett sätt skulle vara att skapa en separat, anpassad dekoder och registrera den för varje typ vi vill hantera. Det verkar vara mycket jobb.

Vad vi kan (och borde) göra istället är att skapa en enda "typneutral" anpassad avkodare som bara hanterar null s, och om BSON-värdet inte är null , bör anropa standardavkodaren för att hantera icke-null värde.

Detta är förvånansvärt enkelt:

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
}

Vi måste bara ta reda på vad vi ska använda för nullawareDecoder.defDecoder . För detta kan vi använda standardregistret:bson.DefaultRegistry , kan vi slå upp standardavkodaren för enskilda typer. Coolt.

Så vad vi gör nu är att registrera ett värde på vår nullawareDecoder för alla typer vi vill hantera null s för. Det är inte så svårt. Vi listar bara de typer (eller värdena för de typerna) vi vill ha detta för, och vi kan ta hand om allt med en enkel loop:

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)

I exemplet ovan registrerade jag noll-medvetna avkodare för string , int och int32 , men du kan utöka den här listan efter eget tycke, lägg bara till värden av önskad typ till customValues skiva ovan.



  1. Hur kör jag ett MongoDB js-skript med Java MongoDriver

  2. Redis Key upphör med Jedis

  3. SQL LPAD()

  4. Hur lagrar Trello data i MongoDB? (Insamling per tavla?)