MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Jak zignorować wartości null podczas rozmieszczania dokumentu MongoDB?

Problem polega na tym, że obecne kodeki bson nie obsługują kodowania / dekodowania string do / z null .

Jednym ze sposobów poradzenia sobie z tym jest stworzenie własnego dekodera dla string typ, w którym obsługujemy null wartości:po prostu używamy pustego ciągu (i, co ważniejsze, nie zgłaszamy błędu).

Dekodery niestandardowe są opisane przez typ bsoncodec.ValueDecoder . Można je zarejestrować w bsoncodec.Registry , używając bsoncodec.RegistryBuilder na przykład.

Rejestry można ustawiać / stosować na wielu poziomach, nawet do całego mongo.Client lub do mongo.Database lub po prostu do mongo.Collection , przy ich nabywaniu, w ramach posiadanych opcji, m.in. options.ClientOptions.SetRegistry() .

Najpierw zobaczmy, jak możemy to zrobić dla string , a następnie zobaczymy, jak ulepszyć / uogólnić rozwiązanie dla dowolnego typu.

1. Obsługa null struny

Po pierwsze, stwórzmy niestandardowy dekoder ciągów znaków, który może zmienić null do (n pustego) ciągu:

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, a teraz zobaczmy, jak wykorzystać ten niestandardowy dekoder ciągów do mongo.Client :

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

Od teraz używaj tego client , za każdym razem, gdy dekodujesz wyniki na string wartości, ten zarejestrowany nullawareStrDecoder dekoder zostanie wywołany do obsługi konwersji, która akceptuje bson null wartości i ustawia pusty ciąg Go "" .

Ale możemy zrobić lepiej... Czytaj dalej...

2. Obsługa null wartości dowolnego typu:"type-neutral" dekoder typu null-aware

Jednym ze sposobów byłoby stworzenie oddzielnego, niestandardowego dekodera i zarejestrowanie go dla każdego typu, który chcemy obsłużyć. To wydaje się być dużo pracy.

Zamiast tego możemy (i powinniśmy) stworzyć pojedynczy, "neutralny pod względem typu" dekoder, który obsługuje tylko null s, a jeśli wartość BSON nie jest null , powinien wywołać domyślny dekoder do obsługi wartości innych niż null wartość.

To zaskakująco proste:

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
}

Musimy tylko dowiedzieć się, czego użyć dla nullawareDecoder.defDecoder . W tym celu możemy użyć domyślnego rejestru:bson.DefaultRegistry , możemy wyszukać domyślny dekoder dla poszczególnych typów. Fajnie.

Więc teraz rejestrujemy wartość naszego nullawareDecoder dla wszystkich typów, które chcemy obsłużyć null s dla. To nie jest takie trudne. Po prostu wymieniamy typy (lub wartości tych typów), dla których chcemy to zrobić, i możemy zająć się nimi za pomocą prostej pętli:

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)

W powyższym przykładzie zarejestrowałem dekodery null-aware dla string , int i int32 , ale możesz rozszerzyć tę listę według własnych upodobań, po prostu dodaj wartości żądanych typów do customValues kawałek powyżej.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Sortowanie bez rozróżniania wielkości liter w MongoDB

  2. Jak utworzyć plik konfiguracyjny dla MongoDB

  3. Mongoose z mongodb jak zwrócić właśnie zapisany obiekt?

  4. Jak spłaszczyć poddokument do poziomu głównego w MongoDB?

  5. Zapytanie o datę z ISODate w mongodb nie działa