google-cloud-go icon indicating copy to clipboard operation
google-cloud-go copied to clipboard

datastore: WithIgnoreFieldMismatch option works for just first record in embedded / nested struct array case.

Open smrfs opened this issue 1 year ago • 1 comments

Current Behavior When using WithIgnoreFieldMismatch option, I cannot retrieve after second records in embedded / nested struct array case, and always need to implement Loader / Saver methods per a entity also workaround to ignore the error ErrFieldMismatch in Loader method.

Requested Behavior Please enhance WithIgnoreFieldMismatch option behavior to retrieve all records even in embedded / nested struct array case.

Reproduced Code Please confirm following code behavior like following 3 steps.

  1. Uncomment CASE1 field and code and run the code. Note that you can retrieve 2 records.
  2. Undo the step1 uncomments and run the code. Note that you can only retrieve 1 record.
  3. Uncomment CASE OPT code and use known workaround. Note that you can retrieve 2 records.
package main

import (
	"context"
	"log"
	"os"

	"cloud.google.com/go/datastore"
)

type Tier1 struct {
	Tier1Field string
	Tier2      []*Tier2
}

type Tier2 struct {
	Tier2Field1 string

	// CASE1: First time, uncomment / enable following field to put the data and retrieve it correctly.
	// CASE2: Second time, undo / comment out following field to reproduct the issue.
	//Tier2Field2 string
}

func (entity *Tier2) Load(p []datastore.Property) error {
	return datastore.LoadStruct(entity, p)
	// CASE OPTION: We know that this workaround works regardless WithIgnoreFieldMismatch option use.
	/*
		err := datastore.LoadStruct(entity, p)
		if fmerr, ok := err.(*datastore.ErrFieldMismatch); ok && fmerr != nil {
			err = nil
		}
		return err
	*/
}

func (entity *Tier2) Save() ([]datastore.Property, error) {
	return datastore.SaveStruct(entity)
}

func main() {
	os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "menu4today-850612-eb5fb6a47e42.json")

	ctx := context.Background()
	client, err := datastore.NewClient(ctx, "menu4today-850612", datastore.WithIgnoreFieldMismatch())
	if err != nil {
		panic(err)
	}

	key := datastore.NameKey("TempEntity", "test", nil)

	// CASE1: First time, use following code to put the data and retrieve it correctly.
	// CASE2: Second time, undo / comment out following code to reproduct the issue.
	/*
		newEntity := &Tier1{
			Tier1Field: "tier1",
			Tier2: []*Tier2{
				{
					Tier2Field1: "tier2-1-1",
					Tier2Field2: "tier2-1-2",
				},
				{
					Tier2Field1: "tier2-2-1",
					Tier2Field2: "tier2-2-2",
				},
			},
		}

		if _, err := client.Put(ctx, key, newEntity); err != nil {
			log.Fatalf("Failed to put entity: %v", err)
		}
		fmt.Println("Entity saved successfully")
	*/

	var retrievedEntity Tier1
	if err := client.Get(ctx, key, &retrievedEntity); err != nil {
		log.Fatalf("Failed to get entity: %v", err)
	}

	// CASE1: The result is to get 2 records.
	// CASE2: The result is to get only 1 record.
	// CASE OPT: The result is to get 2 records.
	println(len(retrievedEntity.Tier2))
}

My go.mod

module test-ds

go 1.23.0

require (
	cloud.google.com/go/datastore v1.18.0
)

smrfs avatar Sep 05 '24 04:09 smrfs

Working on it but reducing the priority since there is a workaround

bhshkh avatar Mar 05 '25 17:03 bhshkh