ostafen / clover

A lightweight document-oriented NoSQL database written in pure Golang.
MIT License
633 stars 54 forks source link

doc.Unmarshal does not play well with `json` tag #142

Closed semolex closed 7 months ago

semolex commented 7 months ago

Hello Clover DB team,

I've encountered an issue while integrating Clover DB with the Wails framework. It seems that when using struct tags for JSON unmarshaling alongside Clover's own tags, the unmarshaled struct ends up with default values for certain fields, rather than the values stored in the database.

Environment:

Steps to Reproduce

  1. Create a struct with both json and clover tags.
  2. Store a document in Clover DB with non-default values for all fields.
  3. Unmarshal the document into the struct using Clover DB's unmarshaling logic.

    func (f *WorkersCollection) GetAllWorkers() ([]Worker, error) {
    workerModel := &Worker{}
    docs, err := f.manager.getAll(workerMode) // just a call to db.FindAll(query.NewQuery(collectionName))
    
    if err != nil {
        return nil, err
    }
    var workers []Worker
    
    for _, doc := range docs {
        log.Infof("", doc)
        var worker Worker
        err := doc.Unmarshal(&worker)
        if err != nil {
            return nil, NewCustomError(err.Error(), failedToUnmarshal)
        }
        log.Printf("Unmarshalled worker: %+v\n", worker)
        workers = append(workers, worker)
    }
    
    return workers, nil
    }

    Expected Behavior

    The struct fields should reflect the values stored in the database.

Actual Behavior

Some fields in the struct are set to their default Go values, despite having different values stored in the database.

Example

Is there is some undocumented way to use both tags or some designed way to specify those fields? I could definitely use are maps but in a certain way I am using type validations, possible method calls etc. Thank you for looking into this issue. I am looking forward to your response or guidance on how to address this problem.

univac490 commented 7 months ago

I made a local copy of clover/internal/encoding.go and updated createRenameMap as follows:

func createRenameMap(rv reflect.Value) map[string]string {
    renameMap := make(map[string]string)
    for i := 0; i < rv.NumField(); i++ {
        fieldType := rv.Type().Field(i)

        tagStr, found := fieldType.Tag.Lookup("clover")
        if found {
            name, _ := processStructTag(tagStr)
            jsonTagStr, found := fieldType.Tag.Lookup("json")
            if found {
                jsonName, _ := processStructTag(jsonTagStr)
                if jsonName != name {
                    renameMap[name] = jsonName
                }
            } else {
                renameMap[name] = fieldType.Name
            }
        }
    }
    return renameMap
}
univac490 commented 7 months ago

actually, the json tag should be always be checked, not just when the clover tag is found.

ostafen commented 7 months ago

@semolex: thank you for reporting the issue.

@univac490: could you submit a PR?

ostafen commented 7 months ago

@semolex: please check if still have the issue. @univac490: thank you very much for fix :=)

semolex commented 7 months ago

@ostafen Thanks. Confirmed. Waiting for a next release