trouble looping through google places API with pagetoken in Go - api

Im having trouble looping through the Google Places API in Go.
Google's Places API returns 20 results max with a pagetoken parameter to add to the query to return the next 20 results until theres none left.
I currently am able to send a query request, return the json and output it in terminal, but when i try to loop back through and add the pagetoken parameter to the query, it runs but only returns the first page results again but with another page token. Any Idea what im doing wrong?
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strconv"
// "os"
)
type GooglePlaces struct {
HTMLAttributions []interface{} `json:"html_attributions"`
NextPageToken string `json:"next_page_token"`
Results []struct {
Geometry struct {
Location struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
} `json:"location"`
Viewport struct {
Northeast struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
} `json:"northeast"`
Southwest struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
} `json:"southwest"`
} `json:"viewport"`
} `json:"geometry"`
Icon string `json:"icon"`
ID string `json:"id"`
Name string `json:"name"`
OpeningHours struct {
OpenNow bool `json:"open_now"`
WeekdayText []interface{} `json:"weekday_text"`
} `json:"opening_hours,omitempty"`
Photos []struct {
Height int `json:"height"`
HTMLAttributions []string `json:"html_attributions"`
PhotoReference string `json:"photo_reference"`
Width int `json:"width"`
} `json:"photos,omitempty"`
PlaceID string `json:"place_id"`
Reference string `json:"reference"`
Scope string `json:"scope"`
Types []string `json:"types"`
Vicinity string `json:"vicinity"`
Rating float64 `json:"rating,omitempty"`
} `json:"results"`
Status string `json:"status"`
}
func searchPlaces(page string) {
apiKey := "API_KEY_HERE"
keyword := "residential+bank+33131"
latLong := "25.766144,-80.190589"
pageToken := page
var buffer bytes.Buffer
buffer.WriteString("https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=")
buffer.WriteString(latLong)
buffer.WriteString("&radius=50000&keyword=")
buffer.WriteString(keyword)
buffer.WriteString("&key=")
buffer.WriteString(apiKey)
buffer.WriteString("&pagetoken=")
buffer.WriteString(pageToken)
query := buffer.String()
// PRINT CURRENT SEARCH
println("query is ", query)
println("\n")
// SEND REQUEST WITH QUERY
resp, err := http.Get(query)
if err != nil {
log.Fatal(err)
}
// CLOSE THE PRECLOSER THATS RETURNED WITH HTTP RESPONSE
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
res := GooglePlaces{}
json.Unmarshal([]byte(body), &res)
var listings bytes.Buffer
for i := 0; i < len(res.Results); i++ {
listings.WriteString(strconv.Itoa(i + 1))
listings.WriteString("\nName: ")
listings.WriteString(res.Results[i].Name)
listings.WriteString("\nAddress: ")
listings.WriteString(res.Results[i].Vicinity)
listings.WriteString("\nPlace ID: ")
listings.WriteString(res.Results[i].PlaceID)
listings.WriteString("\n---------------------------------------------\n\n")
}
listings.WriteString("\npagetoken is now:\n")
listings.WriteString(res.NextPageToken)
if err != nil {
log.Fatal(err)
}
fmt.Println(listings.String())
fmt.Printf("\n\n\n")
// LOOP BACK THROUGH FUNCTION
searchPlaces(res.NextPageToken)
}
func main() {
searchPlaces("")
}

Note that in the documentation for Google Place Search they state:
There is a short delay between when a next_page_token is issued, and when it will become valid.
But in your code you immediately send a request with the new token.
Adding a sleep for a few seconds before using the token solves the problem for me. I changed your code to
if res.NextPageToken != "" {
time.Sleep(3000 * time.Millisecond)
searchPlaces(res.NextPageToken)
} else {
fmt.Println("No more pagetoken, we're done.")
}
Unfortunately there's no documentation about for how long a token is valid.

Related

Storing and Retrieving Lat Long Values Stored as Geography Point Type in Database GoLang

I am trying to save to my database, latitude and longitude values as the geography point datatype and i want to be able to retrieve the values accordingly. I have implemented the following
my model device.go looks like this
device.go
package models
import (
"bytes"
"database/sql/driver"
"encoding/binary"
"encoding/hex"
"fmt"
"time"
"gorm.io/gorm"
)
type GeoPoint struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
}
func (p *GeoPoint) String() string {
return fmt.Sprintf("SRID=4326;POINT(%v %v)", p.Lng, p.Lat)
}
// Scan implements the sql.Scanner interface.
func (p *GeoPoint) Scan(val interface{}) error {
b, err := hex.DecodeString(string(val.(string)))
if err != nil {
return err
}
r := bytes.NewReader(b)
var wkbByteOrder uint8
if err := binary.Read(r, binary.LittleEndian, &wkbByteOrder); err != nil {
return err
}
var byteOrder binary.ByteOrder
switch wkbByteOrder {
case 0:
byteOrder = binary.BigEndian
case 1:
byteOrder = binary.LittleEndian
default:
return fmt.Errorf("invalid byte order %d", wkbByteOrder)
}
var wkbGeometryType uint32
if err := binary.Read(r, byteOrder, &wkbGeometryType); err != nil {
return err
}
if err := binary.Read(r, byteOrder, p); err != nil {
return err
}
return nil
}
// Value impl.
func (p GeoPoint) Value() (driver.Value, error) {
return p.String(), nil
}
type Device struct {
gorm.Model
Id int `json:"id" gorm:"primaryKey"`
UserId int `json:"user_id" gorm:"uniqueIndex"`
LatestLocation GeoPoint `json:"latest_location" gorm:"type:geography(POINT, 4326)"`
CreatedAt time.Time
UpdatedAt time.Time
}
I am able to save data to the database and this is how it looks like in the database
But when i want to retrieve the record with the latitude and longitude, I get wrong data records and i am not sure why.
this is my code
location.go
package apisLocation
import (
"fmt"
db "atm/pkg/configs/database"
models "atm/pkg/models"
"strconv"
"github.com/gofiber/fiber/v2"
)
func GetLocation(c *fiber.Ctx) error {
userId, err := strconv.Atoi(c.Params("userId"))
if err != nil {
return c.Status(400).JSON(err.Error())
}
if checkIfUserExists(userId) {
return c.Status(400).JSON(fiber.Map{"error": "User does not exist"})
}
var device models.Device
db.DB.Db.Find(&device, models.Device{UserId: userId})
return c.Status(200).JSON(fiber.Map{"location": device.LatestLocation})
}
func checkIfUserExists(userId int) bool {
var device models.Device
db.DB.Db.Find(&device, models.Device{UserId: userId})
return device.Id == 0
}
when i run the GetLocation method, the response i get is not accurate, i get a value of this
"location": {
"lat": 1.7689674224598998e+71,
"lng": -3.639753837714837e+173
},
which isn't the lat and long that is saved in the database.
I think somehow when it is being decoded, something changes but i am not sure how to fix this issue.
Any help is appreciated
I found a solution to your problem here https://github.com/go-pg/pg/issues/829#issuecomment-505882885
The problem in your code was just declaring your wkbGeometryType as uint32, not uint64.

How requesting data from an API

I am trying to fetch data from:
Graduates Data
But don't know how to capture data and use it from api and have error if my responseObject isnt defined.
My Code
type Response struct {
Hasil string `json:"result"`
}
type Hasil struct {
Data []Data `json:"records"`
}
type Data struct {
Jurusan string `json:"type_of_course"`
Tahun string `json:"year"`
}
func main () {
response, err := http.Get("https://data.gov.sg/api/action/datastore_search?resource_id=eb8b932c-503c-41e7-b513-114cffbe2338")
if err != nil {
fmt.Print(err.Error())
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
var responseObject Response
json.Unmarshal(responseData, &responseObject)
for i := 0; i < len(responseObject.Hasil.Data); i++ {
fmt.Println(responseObject.Hasil.Data[i].Jurusan)
}
}
And might best if you can share some reference where i can learn go language from scratch? i have no base on Go Lang

How do I get a specific api by it's name and then get it's ID from this "Apis" list of structs?

type Apis struct {
Items []struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
CreatedDate int `json:"createdDate"`
APIKeySource string `json:"apiKeySource"`
EndpointConfiguration struct {
Types []string `json:"types"`
} `json:"endpointConfiguration"`
} `json:"items"`
}
This the struct I have defined to store the APIs i get in json format. How do I get a specific API by its name and then get it's ID. For example lets say, apiname == Shopping and i want Shopping API's ID assigned to id variable.
ps : I'm new to golang and a well explained answer will be very much appreciated.
Thanks guys
In your case Items is slice of custom structs, so you have to perform search over loop, like this:
package main
import (
"encoding/json"
"fmt"
)
type Apis struct {
Items []struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
CreatedDate int `json:"createdDate"`
APIKeySource string `json:"apiKeySource"`
EndpointConfiguration struct {
Types []string `json:"types"`
} `json:"endpointConfiguration"`
} `json:"items"`
}
func main() {
// Some JSON example:
jsonStr := `{"items": [{"id":"1","name":"foo"},{"id":"2","name":"bar"}]}`
// Unmarshal from JSON into Apis struct.
apis := Apis{}
err := json.Unmarshal([]byte(jsonStr), &apis)
if err != nil {
// error handling
}
// Actual search:
nameToFind := "bar"
for _, item := range apis.Items {
if item.Name == nameToFind {
fmt.Printf("found: %+v", item.ID)
break
}
}
}
It would be better to have map of custom structs instead of slice, so you could to do something like this:
package main
import (
"encoding/json"
"fmt"
)
type Apis struct {
Items map[string]struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
CreatedDate int `json:"createdDate"`
APIKeySource string `json:"apiKeySource"`
EndpointConfiguration struct {
Types []string `json:"types"`
} `json:"endpointConfiguration"`
} `json:"items"`
}
func main() {
// Some JSON example:
jsonStr := `{"items": {"foo":{"id":"1","name":"foo"},"bar":{"id":"2","name":"bar"}}}`
// Unmarshal from JSON into Apis struct.
apis := Apis{}
err := json.Unmarshal([]byte(jsonStr), &apis)
if err != nil {
// error handling
}
// Actual search:
nameToFind := "bar"
item, found := apis.Items[nameToFind]
if !found {
fmt.Printf("item not found")
}
fmt.Printf("found: %+v", item)
}
IMPORTANT: with slice you complexity of algorithm will be O(n) with map - O(1) which is way better (it's best what possible).

Convert query result from struct to string for another Golang package

I have searched for a solution on the net and in SO, but found nothing that apply to return values. It is a simple sql query with several rows that I want to return. Error handling not included:
func Fetch(query string) (string) {
type User struct{
id string
name string
}
rows, err := db.Query(query)
users := make([]*User, 0)
for rows.Next() {
user := new(User)
err := rows.Scan(&user.id, &user.name)
users = append(users, user)
}
return(users)
}
I get this error when compiling:
cannot use users (type []*User) as type string in return argument
How should I do to get a correct return value?
The expected input is
JD John Doe --OR-- {id:"JD",name:"John Doe"}
Add this to your code:
type userSlice []*User
func (us userSlice) String() string{
var s []string
for _, u := range us {
if u != nil {
s = append(s, fmt.Sprintf("%s %s", u.id, u.name))
}
}
return strings.Join(s, "\n")
}
type User struct{
id string
name string
}
In your Fetch function replace the last return statement like this:
func Fetch(query string) (string) {
// Note that we declare the User type outside the function.
rows, err := db.Query(query)
users := make([]*User, 0)
for rows.Next() {
user := new(User)
err := rows.Scan(&user.id, &user.name)
users = append(users, user)
}
return(userSlice(users).String()) // Replace this line in your code
}
If you have to return a String you can use the encoding/json package to serialize your user object, but you have to use fields that begin with capital letters for them to be exported. See the full example:
import (
"encoding/json"
)
func Fetch(query string) (string) {
type User struct{
Id string // <-- CHANGED THIS LINE
Name string // <-- CHANGED THIS LINE
}
rows, err := db.Query(query)
users := make([]*User, 0)
for rows.Next() {
user := new(User)
err := rows.Scan(&user.id, &user.name)
users = append(users, user)
}
return(ToJSON(users)) // <-- CHANGED THIS LINE
}
func ToJSON(obj interface{}) (string) {
res, err := json.Marshal(obj)
if err != nil {
panic("error with json serialization " + err.Error())
}
return string(res)
}

redigo, SMEMBERS, how to get strings

I am redigo to connect from Go to a redis database. How can I convert a type of []interface {}{[]byte{} []byte{}} to a set of strings? In this case I'd like to get the two strings Hello and World.
package main
import (
"fmt"
"github.com/garyburd/redigo/redis"
)
func main() {
c, err := redis.Dial("tcp", ":6379")
defer c.Close()
if err != nil {
fmt.Println(err)
}
c.Send("SADD", "myset", "Hello")
c.Send("SADD", "myset", "World")
c.Flush()
c.Receive()
c.Receive()
err = c.Send("SMEMBERS", "myset")
if err != nil {
fmt.Println(err)
}
c.Flush()
// both give the same return value!?!?
// reply, err := c.Receive()
reply, err := redis.MultiBulk(c.Receive())
if err != nil {
fmt.Println(err)
}
fmt.Printf("%#v\n", reply)
// $ go run main.go
// []interface {}{[]byte{0x57, 0x6f, 0x72, 0x6c, 0x64}, []byte{0x48, 0x65, 0x6c, 0x6c, 0x6f}}
// How do I get 'Hello' and 'World' from this data?
}
Look in module source code
// String is a helper that converts a Redis reply to a string.
//
// Reply type Result
// integer format as decimal string
// bulk return reply as string
// string return as is
// nil return error ErrNil
// other return error
func String(v interface{}, err error) (string, error) {
redis.String will convert (v interface{}, err error) in (string, error)
reply, err := redis.MultiBulk(c.Receive())
replace with
s, err := redis.String(redis.MultiBulk(c.Receive()))
Looking at the source code for the module, you can see the type signature returned from Receive will be:
func (c *conn) Receive() (reply interface{}, err error)
and in your case, you're using MultiBulk:
func MultiBulk(v interface{}, err error) ([]interface{}, error)
This gives a reply of multiple interface{} 's in a slice: []interface{}
Before an untyped interface{} you have to assert its type like so:
x.(T)
Where T is a type (eg, int, string etc.)
In your case, you have a slice of interfaces (type: []interface{}) so, if you want a string, you need to first assert that each one has type []bytes, and then cast them to a string eg:
for _, x := range reply {
var v, ok = x.([]byte)
if ok {
fmt.Println(string(v))
}
}
Here's an example: http://play.golang.org/p/ZifbbZxEeJ
You can also use a type switch to check what kind of data you got back:
http://golang.org/ref/spec#Type_switches
for _, y := range reply {
switch i := y.(type) {
case nil:
printString("x is nil")
case int:
printInt(i) // i is an int
etc...
}
}
Or, as someone mentioned, use the built in redis.String etc. methods which will check and convert them for you.
I think the key is, each one needs to be converted, you can't just do them as a chunk (unless you write a method to do so!).
Since redis.MultiBulk() now is deprecated, it might be a good way to use redis.Values() and convert the result into String:
import "github.com/gomodule/redigo/redis"
type RedisClient struct {
Conn redis.Conn
}
func (r *RedisClient) SMEMBERS(key string) interface{} {
tmp, err := redis.Values(r.Conn.Do("smembers", key))
if err != nil {
fmt.Println(err)
return nil
}
res := make([]string, 0)
for _, v := range tmp {
res = append(res, string(v.([]byte)))
}
return res
}