How to get and store the document ID using a struct - go

I've defined a data structure like so:
type Person struct {
Name string `firestore:"name,omitempty"`
}
When I query all the documents in a collection I'd like to be able to attach the ID to the documents for later reference, but not necessarily have ID as an attribute stored in Firestore (unless its the only way). In javascript or python this is straightforward as the data structures are dynamic and I can just query the ID post get() and add it as a dynamic key/value. myObj.id = doc.id
How would I do this with Go?
package main
import (
"fmt"
"cloud.google.com/go/firestore"
"context"
"google.golang.org/api/iterator"
"log"
)
type Person struct {
Name string `firestore:"name,omitempty"`
}
func main() {
ctx := context.Background()
c, err := firestore.NewClient(ctx, "my-project")
if err != nil {
log.Fatalf("error: %v", err)
}
var people []Person
iter := c.Collection("people").Documents(ctx)
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("error: %v", err)
}
var p Person
err = doc.DataTo(p)
if err != nil {
log.Fatalf("error: %v", err)
}
// id := doc.Ref.ID
people = append(people, p)
}
fmt.Println(people)
}
output, no ID
>> [{John Smith}]

I believe that the firestore struct tags works the same way as the tags in the encoding/json package. So a tag with a value of "-" would mean ignore the field.
So
type Person struct {
ID int `firestore:"-"`
Name string `firestore:"name,omitempty"`
}
should do the trick.
You can set ID yourself, but the firestore pkg will ignore it when reading/writing data.

If you want to store the firestore document ID on the Person type, your struct must have a declared field for it.
Golang firestore docs don't mention this explicitly, but since a firestore doc ID is not part of the document fields, the func (*DocumentSnapshot) DataTo does not populate the ID. Instead, you may get the document ID from the DocumentRef type and add it to Person yourself.
The doc also states that:
Note that this client supports struct tags beginning with "firestore:" that work like the tags of the encoding/json package, letting you rename fields, ignore them, or omit their values when empty
Therefore, if you want to omit the ID when marshaling back for firestore, your could use the tag firestore:"-"
The Person would look like this:
type Person struct {
ID string `firestore:"-"`
Name string `firestore:"name,omitempty"`
}
inside the loop:
var p Person
err := docSnap.DataTo(&p)
if err != nil {
// handle it
}
p.ID = doc.Ref.ID

Related

Get data from two different struct

Have this struct of User and Post and I try to make Name from User to be included within Post Struct when a user create a new post.
type User struct {
ID int
Name string
Created time.Time
}
type Post struct {
ID int
PostTitle string
PostDesc string
Created time.Time
}
How can I create something connected between this two struct such as Author of the Post ?
The goal is try to get the name of the post author which from User struct with the code below:
post, err := app.Models.Posts.GetPost(id)
GetPost() just run SELECT query and scan row
This approach is without any ORM.
It's a simple query that can return multiple rows. You've to scan the whole resultset and map each column on the struct's fields.
Keep in mind to always check for errors.
Below you can find the solution:
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/lib/pq"
)
type Post struct {
ID int
PostTitle string
PostDesc string
Created time.Time
UserID int
User User
}
type User struct {
ID int
Name string
Created time.Time
}
func main() {
conn, err := sql.Open("postgres", "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable")
if err != nil {
panic(err)
}
defer conn.Close()
// get MAX id
var id int
conn.QueryRow(`SELECT MAX(id) FROM posts`).Scan(&id)
// insert
sqlInsertStmt := `INSERT INTO posts (id, post_title, post_desc, created, user_id) VALUES ($1,$2,$3,$4,$5)`
if _, err = conn.Exec(sqlInsertStmt, id+1, "TDD", "Introduction to Test-Driven-Development", time.Now(), 1); err != nil {
panic(err)
}
// read
rows, err := conn.Query(`SELECT posts.id, post_title, post_desc, posts.created, users.id, users.name, users.created FROM posts INNER JOIN users ON posts.user_id=users.id`)
if err != nil {
panic(err)
}
var posts []Post
for rows.Next() {
var post Post
if err = rows.Scan(&post.ID, &post.PostTitle, &post.PostDesc, &post.Created, &post.User.ID, &post.User.Name, &post.User.Created); err != nil {
panic(err)
}
posts = append(posts, post)
}
if err = rows.Err(); err != nil {
panic(err)
}
for _, v := range posts {
fmt.Printf("author name: %q\n", v.User.Name)
}
}
Let me know if this helps.
Edit
I've also included an example of INSERT in Postgres. To achieve it, we've to use the db.Exec() function, and provide the parameters.
Pay attention to how you construct the query as you can get a SQL-Injection vulnerability.
Lastly, in a real-world scenario, you shouldn't lookup for the MAX id in the posts table but should be automatically generated.
Give it a try to this solution, maybe it resolves your issue.
First, I defined the structs in this way:
// "Post" belongs to "User", "UserID" is the foreign key
type Post struct {
gorm.Model
ID int
PostTitle string
PostDesc string
Created time.Time
UserID int
User User
}
type User struct {
ID int
Name string
Created time.Time
}
In this way, you can say that Post belongs to User and access the User's information within the Post struct.
To query the records, you've to use Preload("User") to be sure to eager load the User records from the separate table.
Keep attention to the name you pass in as the argument in Preload, as it can be tricky.
Lastly, you'll be able to access data in the embedded struct (with the dot notation).
Below, you can find a complete working example (implemented with the use of Docker):
package main
import (
"fmt"
"time"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
// "Post" belongs to "User", "UserID" is the foreign key
type Post struct {
gorm.Model
ID int
PostTitle string
PostDesc string
Created time.Time
UserID int
User User
}
type User struct {
ID int
Name string
Created time.Time
}
func main() {
dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&Post{})
newPost := &Post{ID: 1, PostTitle: "Golang", PostDesc: "Introduction to Golang", Created: time.Now(), UserID: 1, User: User{ID: 1, Name: "John Doe", Created: time.Now()}}
db.Create(newPost)
var post Post
db.Preload("User").Find(&post, 1)
fmt.Printf("author name: %q\n", post.User.Name)
}
Let me know if I answered your question!

Parsing JSON using struct

I'm trying to parse JSON using Go. Can anyone tell me why my code is not working as expected?
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string
Body string
Time int64
}
type Person struct {
M Message
}
func get_content() {
body := []byte(`{"person":{"Name":"Alice","Body":"Hello","Time":1294706395881547000}}`)
var data Person
err := json.Unmarshal(body, &data)
if err != nil {
panic(err.Error())
}
fmt.Printf("%v",data.M.Name)
}
func main() {
get_content()
}
I'm expecting it to print the Name.
Go playground Code
There are two problems in the code.
The first one is what #umar-hayat mentioned above -> you are unmarshalling into the data object and you should be aiming at the data.M field.
The second problem is that your JSON's structure doesn't match your struct's structure. Your Person has a single field called M. If we want to represent this as JSON it would look like this:
{
"M": {
"Name": "Joe",
"Body": "Hi",
"time": 2600
}
}
Instead, you have a field called person in your JSON which cannot be matched to any field in your struct. The fact that it's similar to the name of the struct's type doesn't help in any way, I'm afraid.
So, you can either change your JSON and your target:
body := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
var data Person
err := json.Unmarshal(body, &data.M)
Or just your JSON:
body := []byte(`{"M":{"Name":"Alice","Body":"Hello","Time":1294706395881547000}}`)
var data Person
err := json.Unmarshal(body, &data)
But it's essential that the names of the fields in your JSON match the names of the fields in your struct. Or, as mentioned by Konstantinos, you can use tags in order to specify particular names with which your struct's fields will be represented in the JSON.
You might find this helpful: https://gobyexample.com/json
Here is how to Unmarshel JSON to the struct. you can check it on Go Playground here:
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string
Body string
Time int64
}
type Person struct {
M Message
}
func get_content() {
body := []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
var data Person
err := json.Unmarshal(body, &data.M)
if err != nil {
panic(err.Error())
}
fmt.Printf(data.M.Name)
}
func main() {
get_content()
}
Replace data with data.M in below line.
err := json.Unmarshal(body, &data)
As long as you intent to map Json keys on structs whose fields have different names you should add tags:
type Message struct {
Name string `json:"Name"`
Body string `json:"Body"`
Time int64 `json:"Time"`
}
type Person struct {
M Message `json:"person"`
}
You can find more information here
In addition this answer explains in an nutshell the purpose of tags in go.

How to call gorm alias properly?

Here is my code:
package main
import (
"fmt"
"time"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type ClientCustomer struct {
Id int `json:"Id"`
Name string
Created time.Time
key string
UserId int `gorm:"user_id"`
Modified time.Time
}
func (ClientCustomer) TableName() string {
return "Client_customer"
}
type ClientCustomerInvitation struct {
Id int
CustomerId int `gorm:"customer_id"`
CodeInvitationId int `gorm:"codeinvitation_id"`
}
func (ClientCustomerInvitation) TableName() string {
return "Client_customer_invitation"
}
func main() {
db, err := gorm.Open("sqlite3", "db.sqlite3?cache=shared&mode=rwc")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
var clientCustomer ClientCustomer
rows, err := db.Model(&ClientCustomer{}).Rows()
defer rows.Close()
if err != nil {
panic(err)
}
var clientCustomerInvitation ClientCustomerInvitation
for rows.Next() {
db.ScanRows(rows, &clientCustomer)
db.First(&clientCustomerInvitation, "customer_id = ?", clientCustomer.Id)
fmt.Println(clientCustomer)
fmt.Println(clientCustomerInvitation)
}
}
but I'm not fond of this line:
db.First(&clientCustomerInvitation, "customer_id = ?", clientCustomer.Id)
Is there a way to call "customer_id" from the struct directly instead of using a string?
Ideally I would like to do something like:
db.First(&clientCustomerInvitation, ClientCustomerInvitation.CustomerId.gormAlias+" = ?", clientCustomer.Id)
I'm looking for a way to use the gorm alias for mapping the field in way that is more elegant and re usable than a mere string.
The only way to be able to get tag value from certain struct field, is by using reflect.
My suggestion, create a function that return tag value from specific struct field. Something like below:
func getGormAlias(obj interface{}, fieldName string) string {
if field, ok := reflect.TypeOf(obj).FieldByName(fieldName); ok {
return field.Tag.Get("gorm")
}
return ""
}
Then use it to get the tag value.
gormAliasCustomerId := getGormAlias(ClientCustomerInvitation{}, "CustomerId")
db.First(&clientCustomerInvitation, gormAliasCustomerId + " = ?", clientCustomer.Id)
Basically what getGormAlias() function does:
Use the reflect.Type on obj to get the reflect.Type value.
Then call .FieldByName() to get the reflect.Value object from selected field name.
The tag information is available through .Tag property. Use that to get the tag value of gorm.

How to create a DateTime field in bigquery and update it in golang?

here is basically my creation script for bigquery in golang :
type data_pix struct {
Id string
IdC string
Stamp int64
Tag []string
}
func createTable(client *bigquery.Client, datasetID, tableID string) error {
ctx := context.Background()
// [START bigquery_create_table]
schema, err := bigquery.InferSchema(data_pix{})
if err != nil {
return err
}
table := client.Dataset(datasetID).Table(tableID)
if err := table.Create(ctx, schema); err != nil {
return err
}
// [END bigquery_create_table]
return nil
}
for the moment i use mainly a timestamp in Int64.
But i am looking for any example on how to add Datetime to my struct and btw add Datetime to my data
Thanks and regards
I have not used the bigquery, however I had a look at the godoc and source code.
It seems, you have to use data type civil.DateTime reference in the struct.
For e.g:
As per godoc and source code, following should create DateTime field.
type data_pix struct {
Id string
IdC string
Stamp civil.DateTime
Tag []string
}
schema, err := bigquery.InferSchema(data_pix{})
// now schema should represent DateTime Field
There is a function to get civil.DateTime from time.Time. I would suggest you have a look at this go sourcecode to know more.

Mapping struct to mysql table, and binding row to struct

This is my first script using go-sql-driver.
My mysql table (PRODUCT) looks like:
id int
name varchar(255)
IsMatch tinyint(1)
created datetime
I want to simply load a row from a table, and bind it to a struct.
I have this so far:
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
type Product struct {
Id int64
Name string
IsMatch ??????????
Created ?????
}
func main() {
fmt.Printf("hello, world!\n")
db, err := sql.Open("mysql", "root:#/product_development")
defer db.Close()
err = db.Ping()
if err != nil {
panic(err.Error()) // proper error handling instead of panic in your app
}
rows, err := db.Query("SELECT * FROM products where id=1")
if err != nil {
panic(err.Error()) // proper error handling instead of panic in your app
}
}
Now I need to:
1. What datatype in Go do I use for tinyint and datetime?
2. How to I map the rows to a Product struct?
What datatype in Go do I use for tinyint and datetime?
For a hint as to the types that the database/sql package will be using, have a look at the documentation for database/sql.Scanner, which lists the Go types used within database/sql itself:
int64
float64
bool
[]byte
string
time.Time
nil - for NULL values
This would lead you to try int64 for IsMatch and time.Time for Created. I believe in reality you can use pretty much any sized int (maybe even bool, you'd have to check the source) for IsMatch because it can be stored "without loss of precision." The documentation for go-mysql-driver explains that you will need to add parseTime=true to your DSN in order for it to parse into a time.Time automatically or use NullTime.
How to I map the rows to a Product struct?
It should be something pretty strightforward, using Rows.Scan, like:
var products []*Product
for rows.Next() {
p := new(Product)
if err := rows.Scan(&p.ID, &p.Name, &p.IsMatch, &p.Created); err != nil { ... }
products = append(products, p)
}
if err := rows.Err() { ... }
This scans the columns into the fields of a struct and accumulates them into a slice. (Don't forget to Close the rows!)
How to I map the rows to a Product struct?
You can use reflect to bind table rows in db to a struct, and
automatically match values without long Hard-Code sql string which is easy to make mistakes.
this is a light demo: sqlmapper

Resources