Getting ID from Google's datastore using Golang - go

I have a Go struct:
type Foo struct{
Name: string
Age: int
}
I am able to save and retrieve Foos in datastore.
So I may edit a Foo, I want to get the ID that datastore assigns a Foo, to use in an anchor.
I verified that IDs are assigned using Google's datastore dashboard, but how do I get the ID off of a retrieved Foo?

GetAll returns the keys along with any error.
keys, err := q.GetAll(c, &foos)
keys is an array of datastore.Key

Related

Go GORM two table requests

So I am new to GORM and I have this task where I have two tables accounts and users
I get the user.Id and I need to get the accountId from the user table afterwards I need to only get the account that has the same Id as user.AccountId
type User struct{
Id int
AccountId int
}
type Account struct{
Id int
Balance int
}
It looks like this should be a pretty simple call to do it in a single request, but I can't find a simple implementation for this logic. Can anyone help me
There are multiple ways to do this, and below are a couple of them. I'm assuming that the db variable is of type *gorm.DB. Also, I'm the code below is suited for MySQL (minor changes are needed for PostgreSQL).
Load just the Account data with joins
var account Account
err := db.Joins("JOIN users ON users.account_id = account.id").Where("users.id = ?", user.Id).First(&account).Error
Load the User object with the Account data
Here there are a few changes to the User struct, but the underlying code is simpler. Also, it loads the entire User object, with the related Account object.
type User struct{
Id int
AccountId int
Account Account
}
var fullUser User
err := db.Preload("Account").First(&fullUser, user.Id).Error

Go gorm creating many to many relationship using customised table and extra data

I am trying to achieve a join between two models using gorm and a customised table. On this customised table I would like to store extra information about the joined models.
As per the docs I have the following,
func init() {
database.DB.SetupJoinTable(&Foo{}, "Bar", &FooBar{})
}
type Foo struct {
UUID uuid.UUID `json:"uuid" gorm:"type:uuid;primaryKey;"`
SomeValue string `json:"someValue"`
Bars []Bar `json:"bars" gorm:"many2many:foo_bars"`
}
type Bar struct {
UUID uuid.UUID `json:"uuid" gorm:"type:uuid;primaryKey;"`
SomeValue string `json:"someValue"`
}
type FooBar struct {
FooUUID uuid.UUID `json:"foo" gorm:"primaryKey"`
BarUUID uuid.UUID `json:"bar" gorm:"primaryKey"`
ExtraValue string `json:"extraValue"`
}
The above creates a schema as I would expect. The problem is when trying to persist this relationship and set extra data needed to FooBar. The docs provide documentation on appending relationships directly from Foo to Bar, but not adding data to this customised table.
The documentation states:
JoinTable can be a full-featured model
So I would expect this to be possible.
Is this possible using gorm?
How can I create and save this relationship with additional information?
This is actually very simple, the fact that FooBar is a full-featured model means you can use it to interact with the join table directly the same way that any other model can:
// assuming we have a Foo foo and Bar bar
// read
links := []FooBar{}
db.Where(FooBar{FooUUID: foo.UUID, BarUUID: bar.UUID}).Find(&links)
// create
link := FooBar{FooUUID: foo.UUID, BarUUID: bar.UUID, ExtraValue: "foobar"}
db.Create(&link)
// update
link.ExtraValue = "foobarbaz"
db.Save(&link)
// delete
db.Delete(&link)
But to do all this requires you to treat the join table as a separate model. For example you shouldn't expect to be able to extract the ExtraValue when doing regular Associations operations on Foo, because there is literally no space on the []Bar field to put the value. Similar goes for saving the ExtraValue, unless you do some special trickery with Hooks (see this answer for more on this).

How to read from Datastore using an Ancestor query and latest golang libraries

I want to read all entities from a Datastore kind (around 6 entities/records).
I have a Datastore that is key'ed on a weird type that I am trying to understand. I can't find any uniqueness on the a key to perform a query on.
The table looks like this:
GCP Datastore representing data I want to read into my Go app
When I click on a record, it looks like this:
Key literal exposed and used from here on out to try and get the records in the Go app
``I can perform an ancestor query in the console like this:```
GCP Datastore queried using Ancestor query
Great! So now I want to retrieve this data from my Golang App? But how?
I see a lot of solutions online about using q.Get(...) // where q is a *Query struct
Any of these solutions won't work because they import google.golang.org/appengine/datastore. I understand that this is legacy and deprecated. So I want a solution that imports cloud.google.com/go/datastore.
I tried something along these lines but didn't get much luck:
First try using GetAll and query
I tried this next:
Second try attempting to use ancestor query... not ready yet
Lastly I tried to get a single record directly:
Lastly I tried to get the record directly
In all cases, my err is not nil and the dts that should be populated from datastore query is also nil.
Any guidance to help me understand how to query on this key type? Am I missing something fundamental with the way this table is key'ed and queried?
Thank you
Then I tried this:
It seems you are just missing your Namespace
// Merchant Struct
type MerchantDetails struct {
MEID string
LinkTo *datastore.Key
Title string
}
// Struct array to store in
var tokens []MerchantDetails
// Ancestor Key to filter by
parentKey := datastore.NameKey("A1_1113", "activate", nil)
parentKey.Namespace = "Devs1"
// The call using the new datastore UI. Basically query.Run(), but datastore.GetAll()
keys, err := helpers.DatastoreClient.GetAll(
helpers.Ctx,
datastore.NewQuery("A1_1112").Ancestor(parentKey).Namespace("Devs1"),
&tokens,
)
if err != nil {
return "", err
}
// Print all name/id from the found values
fmt.Printf("keys: %v", keys)

Gorm creates duplicate in association

I have the following 2 structs with a many-2-many relationship.
type Message struct {
gorm.Model
Body string `tag:"body" schema:"body"`
Locations []Location `tag:"locations" gorm:"many2many:message_locations;"`
TimeSent time.Time `tag:"timesent"`
TimeReceived time.Time `tag:"timereceived"`
User User
}
type Location struct {
gorm.Model
PlaceID string `tag:"loc_id" gorm:"unique"`
Lat float64 `tag:"loc_lat"`
Lng float64 `tag:"loc_lng"`
}
If I create a Message with DB.Create(my_message), everything works fine : the Message is created in DB, along with a Location and the join message_locations table is filled with the respective IDs of the Message and the Location.
What I was expecting though was that if the Location already exists in DB (based on the place_id field, which is passed on), gorm would create the Message, retrieve the Location ID and populate message_locations.That's not what is happening.
Since the PlaceID must be unique, gorm finds that a duplicate key value violates unique constraint "locations_place_id_key" and aborts the transaction.
If on the other hand I make the PlaceID not unique, gorm creates the message alright, with the association, but then that creates another, duplicate entry for the Location.
I can test if the location already exists before trying to save the message:
existsLoc := Location{}
DB.Where("place_id = ?", mssg.Locations[0].PlaceID).First(&existsLoc)
then if true switch the association off:
DB.Set("gorm:save_associations", false).Create(mssg)
DB.Create(mssg)
The message is saved without gorm complaining, but then message_locations is not filled.
I could fill it "manually" since I've retrieved the Location ID when testing for its existence, but it seems to me it kind of defeats the purpose of using gorm in the first place.
I'm not sure what the right way to proceed might be. I might be missing something obvious, I suspect maybe something's wrong with the way I declared my structs? Hints welcome.
UPDATE 2016/03/25
I ended up doing the following, which I'm pretty sure is not optimal. If you have a better idea, please chime in.
After testing if the location already exists and it does:
// in a transaction
tx := DB.Begin()
// create the message with transaction disabled
if errMssgCreate := tx.Set("gorm:save_associations", false).Create(mssg).Error; errMssgCreate != nil {
tx.Rollback()
log.Println(errMssgCreate)
}
// then create the association with existing location
if errMssgLocCreate := tx.Model(&mssg).Association("Locations").Replace(&existLoc).Error; errMssgLocCreate != nil {
tx.Rollback()
log.Println(errMssgLocCreate)
}
tx.Commit()
In my situation I was using a UUID for the ID. I coded a BeforeCreate hook to generate the uuid. When saving a new association, there was no need for the beforeCreate hook to create a new ID, but it did so anyway (that feels a bit like it could be a bug?).
Note that it did this even when using "association" mode to append a new relationship. The behaviour was not limited to when calling Create with a nested association.
It took me several hours to debug this because when I inspected the contents of the associated records they matched exactly the instances that I had just created.
In other words:
I made a bunch of Foo
I made some Bar, and tried to attach just certain Foo to each
The Foo in the Bar relationship had the same reference as the objects I had just created.
Removing the beforeCreate hook makes the code behave like I'd expect. And happily I was already in the habit of manually making a uuid whenever needed instead of relying on it so it didn't hurt me to remove it.
I've pasted a minimally reproducible example at https://pastebin.com/wV4h38Qz
package models
import (
"github.com/google/uuid"
"gorm.io/gorm"
"time"
)
type Model struct {
ID uuid.UUID `gorm:"type:char(36);primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt
}
// BeforeCreate will set a UUID rather than numeric ID.
func (m *Model) BeforeCreate(tx *gorm.DB) (err error) {
m.ID = uuid.New()
return
}
gorm:"many2many:message_locations;save_association:false"
Is getting closer to what you would like to have. You need to place it in your struct definition for Message. The field is then assumed to exist in the db and only the associations table will be populated with data.

How to save struct based type with a map property into mongodb

I want to use mongodb as session storage and save a struct based data type into mongodb.
The struct type looks like:
type Session struct {
Id string
Data map[string]interface{}
}
And create reference to Session struct type and put some data into properties like:
type Authen struct {
Name, Email string
}
a := &Authen{Name: "Foo", Email: "foo#example.com"}
s := &Session{}
s.Id = "555555"
s.Data["logged"] = a
How to save the session data s into mongodb and how to query those data back and save into a reference again?
I think the problem can occurs with the data property of type map[string]interface{}.
As driver to mongodb I would use mgo
There's nothing special to be done for inserts. Just insert that session value into the database as usual, and the map type will be properly inserted:
err := collection.Insert(&session)
Assuming the structure described, this will insert the following document into the database:
{id: "555555", data: {logged: {name: "foo", email: "foo#example.com"}}}
You cannot easily query it back like that, though, because the map[string]interface{} does not give the bson package a good hint about what the value type is (it'll end up as a map, instead of Authen). To workaround this, you'd need to implement the bson.Setter interface in the type used by the Data field.

Resources