I am attempting to update a document in firestore using the golang library. For some reason I am getting an error: "no field \"BirthYear\" error and I am not sure why. Birth year is definitely one of the values that I am attempting to update.
I assume that I have configured my struct incorrectly but I cannot see how. Here is my struct and my update code:
sharedstructs.Profile
type Profile struct {
UID string `json:"UID" firestore:"UID"`
ContactEmail string `json:"ContactEmail,omitempty" firestore:"ContactEmail"`
BirthMonth int64 `json:"BirthMonth,omitempty" firestore:"BirthMonth"`
BirthYear int64 `json:"BirthYear,omitempty" firestore:"BirthYear"`
Gender string `json:"Gender,omitempty" firestore:"Gender"`
Unit string `json:"Unit,omitempty" firestore:"Unit"`
CurrentStatus string `json:"CurrentStatus,omitempty" firestore:"CurrentStatus"`
Country string `json:"Country,omitempty" firestore:"Country"`
ExperienceType string `json:"ExperienceType,omitempty" firestore:"ExperienceType"`
DateJoined time.Time `json:"DateJoined,omitempty" firestore:"DateJoined"`
Abilities []Ability `json:"Abilities,omitempty" firestore:"Abilities"`
Goals []Goal `json:"Goals,omitempty" firestore:"Goals"`
Roles []Role `json:"Roles,omitempty" firestore:"Roles"`
TermsAndConditions []TermsAndConditions `json:"TermsAndConditions,omitempty" firestore:"TermsAndConditions"`
TimeZone string `json:"TimeZone,omitempty" firestore:"TimeZone"`
BaselineTests []BaselineTestResults `json:"BaselineTests,omitempty" firestore:"BaselineTests"`
UpdatedDate time.Time `json:"UpdatedDate,omitempty" firestore:"UpdatedDate"`
FirstName *string `json:"FirstName,omitempty" firestore:"FirstName"`
LastName string `json:"LastName,omitempty" firestore:"LastName"`
DisplayName string `json:"DisplayName,omitempty" firestore:"DisplayName"`
}
Update Function
func updateProfileWithSpecficValues(documentName string, values sharedstructs.Profile, overwriteValues []string) error {
ctx := context.Background()
app := firestorehelper.GetFirestoreApp()
client, err := app.Firestore(ctx)
if err != nil {
return err
}
defer client.Close()
//Set the updated date
values.UpdatedDate = time.Now()
wr, error := client.Doc(collectionName+"/"+documentName).Set(ctx, values, firestore.Merge(overwriteValues))
if error != nil {
return error
}
fmt.Println(wr.UpdateTime)
//Assume success
return nil
}
https://godoc.org/cloud.google.com/go/firestore#Merge
Merge returns a SetOption that causes only the given field paths to be
overwritten. Other fields on the existing document will be untouched.
It is an error if a provided field path does not refer to a value in the data passed to Set.
You are sending no BirthYear (default value) in values, but BirthYear is specified in overwriteValues.
As of cloud.google.com/go/firestore v1.3.0, I don't think you can accomplish an update through Set(..., valueStruct, firestore.Merge(sliceOfPaths)) when valueStruct is the complete struct you might read or write to Firestore.
I receive an error string including the 'no field "[SomeFieldName]"' string the OP references, but there is more information in this error. If my sliceOfPaths refers to the names of two elements of my struct, say an int and a time.Time, I often receive an error such as 'no field "[NameOfMyIntField] for value 2020-09-14T00:00:00Z"' or vice-versa, with it trying to update my Firestore doc's time field with the integer.
I just used Update() rather than Set(). It's a little clunkier because you have to pass Update() a specially crafted subset of your original struct (a slice of firestore.Update), but it works.
Related
Here, all i am trying to achieve is to change a user status from CAPTURE to ACTIVATED in the mongodb using golang. This is what i have done so far but yet i am not getting the right response.
So far, I have tried the following implementation but nothing happened... All i am expecting is to see a change on an existing user status.
func UpdateUserActivate(client *mongo.Client, user models.User) (*models.User, error) {
collection := client.Database("coche").Collection("user_entity")
_id, err := primitive.ObjectIDFromHex(user.ID)
if err != nil {
fmt.Println(err)
}
filter := bson.M{"_id": _id}
update := bson.M{"$set": bson.M{user.Status: "ACTIVATED"}}
_, _err := collection.UpdateOne(context.TODO(), filter, update)
if _err != nil {
return &user, errors.New("user activation failed")
}
return &user, nil
}
This is my user model.
type User struct {
gorm.Model
ID string `_id`
FirstName string `json:"firstName"bson:"firstname"`
LastName string `json:"lastName" bson: "lastname"`
Address string `json:"address" bson: "address"`
PhoneNumber string `json:"phoneNumber" bson: "phonenumber"`
Status string `json:"status" bson: "status"`
Email string `gorm:"unique" json:"email" "email"`
Password string `json:"password""password"`
Category string `json:"category" "category"`
Roles []string `json:"roles" "roles"`
}
Try update := bson.D{{"$set", bson.D{{"status": "ACTIVATED"}}}}
After "$set" use a comma ","
bson.M or bson.D shouldn't matter in that case. Just take care of the curly brackets using the one or the other.
Ref: https://www.mongodb.com/docs/drivers/go/current/usage-examples/updateOne/
Guys please i'm trying to query a collection in MongoDB and golan using the user phone, get the id of the user and use it to query another collection but when I try to use that return id it gives me an error of
cannot use userid (variable of type interface{}) as string value in argument to primitive.ObjectIDFromHex: need type assertion
My Code
var result bson.M
err := userdataCollection.FindOne(context.TODO(), bson.M{"phone":"+2347000000"}).Decode(&result)
if err != nil {
if err == mongo.ErrNoDocuments {
// This error means your query did not match any documents.
return
}
log.Fatal(err)
}
var userid = result["_id"]
fmt.Printf("var6 = %T\n", userid)
json.NewEncoder(w).Encode(result["_id"]) // this returns prints the user id
id,_ := primitive.ObjectIDFromHex(userid) // where I am having the error
The _id returned is already of type primitive.ObjectID, so use a simply type assertion (and no need to call primitive.ObjectIDFromHex()):
id := userid.(primitive.ObjectID)
Currently, I got this struct
type Token struct {
gorm.Model
Name string `gorm:"column:name"`
Enabled bool `gorm:"column:enabled"`
Symbol string `gorm:"column:symbol"`
TokenDetails []*EarnTokenDetail `gorm:"foreignkey:TokenID;references:ID"`
}
type EarnTokenDetail struct {
gorm.Model
Name string `gorm:"column:name"`
TokenID uint64 `gorm:"column:token_id"`
Enabled bool `gorm:"column:enabled"`
Chains *EarnChain `gorm:"foreignkey:ID;references:ChainID"`
}
type EarnChain struct {
ID uint64 `gorm:"primary_key column:id"`
Enabled bool `gorm:"column:enabled"`
Name string `gorm:"column:name"`
}
And this GORM query:
var tokens []*model.Token
result := e.db.
WithContext(ctx).
Preload("TokenDetails", "token_details.enabled = true").
Preload("TokenDetails.Chains", "chains.enabled = true").
Find(&tokens, "tokens.enabled = true")
It works fine when everything is enabled, but when I disable chains in the database, the result will still show the tokens with disabled chains, with the Chains field empty.
How can I filter out those rows while still using preload?
According the the GORM documentation, that's the expected behavior. It does one query after another, so there is no way to "drop" results of the former queries. If you don't want these results at all - not even with an empty Chain field, consider to use Join() to filter out those. I think I even found a comment pointing that out: How to multiple table joins in GORM
I was watching the following package: https://github.com/olekukonko/tablewriter
And I want to try to print my struct as something like that, but I couldn't covert my array of structs into the array of strings that the package needs.
So I tried something like that:
func print(quakes []Quake) {
var data [][]string
for _, quake := range quakes {
b, err := json.Marshal(quake)
append(data, []string(b))
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Sign", "Rating"})
for _, v := range newData {
table.Append(v)
}
table.Render() // Send output
}
My Quake Struct:
type Quake struct {
Googlemapref string `json:"googlemapref"`
Segree string `json: "degree"`
DataUpdate string `json: "dataUpdate"`
MagType string `json:"magType"`
ObsRegion string `json: "obsRegion"`
Lon string `json:"lon"`
Source string `json: "source"`
Depth int `json:"depth"`
TensorRef string `json:"tensorRef"`
Sensed string `json:"sensed"`
Shakemapid string `json:"shakemapid"`
Time string `json:"time"`
Lat string `json:"lat"`
Shakemapref string `json:"shakemapref"`
Local string `json:"local"`
Magnitud string `json: "magnitud"`
}
Would appreciate some help since I am new in the laguage, thanks a lot
There are a few issues with your code. Firstly, the append function doesn't append in place so where you do append(data, []string(b)) the result is thrown away so I think you want to do data = append(data, []string(b)) instead.
Also, doing a json.Marshal on the struct will not make a slice of strings which you try to use it as. Instead, it will produce a single string that has all of the values in such as {"googlemapref":"something","depth":10}. The tablewriter you want to use expects to take a slice of values to put in the table that match the headings (where you appear to using the example headings of "Name", "Sign", "Rating".
You could use the reflect package like json does to populate the fields in each row but I think this would be more complexity than it is worth and you are better just filling in each row by calling the relevant fields:
func print(quakes []Quake) {
var data [][]string
for _, quake := range quakes {
row := []string{quake.Googlemapref, quake.Segree, strconv.Itoa(quake.Depth),...}
data = append(data, row)
}
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"googlemapref", "degree", "depth",...})
for _, v := range newData {
table.Append(v)
}
table.Render() // Send output
}
(I've left the ... for you to fill in the other fields yourself but included depth to show how to convert it to a string).
I am trying to perform a filter query on a set of entities in the datastore, but the entity field I am trying to query against, with the equality operator, is of type []byte and I don't know if appengine datastore can perform this comparison
This is my entity:
type Data struct {
Id int64 `json:"id"`
Version int32 `json:"-"`
HMAC []byte `json:"-"`
Status string `json:"status"`
}
And here is my query logic
func (view *DataView) GetDataByHMAC(hmac []byte) (Data, error) {
view_key := datastore.NewKey(view.context, "View", "data-view", 0, nil)
data := make([]Data, 0)
query := datastore.
NewQuery("ViewData").
Ancestor(view_key).
Filter("HMAC = ", hmac)
_, err := query.GetAll(view.context, &data)
if err != nil {
return Data{}, err
}
if len(data) == 0 {
return Data{}, ErrNoData
}
return data[0], nil
}
It builds but does not return any results, even after programmatically retrying over the course of 10 seconds so i do not believe it is an issue of eventual consistency between the datastore and the view data that I've stored there.
My main question is: does the appengine datastore allow for a query to use a comparison filter on a field with type []byte?
In 1.9.11, the ByteString type was introduced to the datastore package. It can be used for storing short, indexed byte slices.
If you change your entity to the following, it should work:
type Data struct {
ID int64 `json:"id"`
Version int32 `json:"-"`
HMAC datastore.ByteString `json:"-"`
Status string `json:"status"`
}
More info: https://developers.google.com/appengine/docs/go/datastore/entities#Go_Properties_and_value_types
The answer to your main question is No, because []byte is stored as blob, which is not indexed by the app engine datastore.
queries with a filter or sort order on the unindexed property
will never match that entity.
Note: In addition to any unindexed properties you declare explicitly,
those typed as []byte are automatically treated as unindexed.
Here is the documentation: https://developers.google.com/appengine/docs/go/datastore/indexes#Go_Unindexed_properties