I'm developing with Go lang and very new to it. I would like to use the redis GEORADIUS command and get back my results including the distance.
I have now used two packages radix.v2 and redigo to try and get results but had no joy. I have no issues using simple commands such as SET and GET.
Any help will be greatly appreciated.
Edit: added code :-)
package main
import (
"fmt"
"github.com/mediocregopher/radix.v2/redis"
//"reflect"
"encoding/json"
)
var (
client *redis.Client
)
type Facility struct {
Id string
Dist string
}
func main() {
client, err := redis.Dial("tcp", "192.168.99.100:6379")
if err != nil {
// handle err
}
surname, err := client.Cmd("GET", "surname").Str()
if err != nil {
// handle err
}
fmt.Println(surname)
reply, _ := client.Cmd("GEORADIUS", "venues", 50, 0, 25, "mi", "WITHDIST", "ASC").Array()
if err != nil {
fmt.Println(err)
}
//fmt.Println(reflect.TypeOf(reply))
//fmt.Println(reply)
var facilities []Facility
for _, results := range reply {
facility, _ := results.Array()
id, _ := facility[0].Str()
dist, _ := facility[1].Str()
facilities = append(facilities, Facility{
Id: id,
Dist: dist,
})
}
//fmt.Println(facilities)
resp, _ := json.Marshal(facilities)
fmt.Fprintf(w, string(resp))
}
Cheers
Stephen
You can look at the code in the redis package.
the file commands.go has all the signatures
a few signatures are below
GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd
GeoPos(key string, members ...string) *GeoPosCmd
GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) GeoRadiusByMember(key, member string, query *GeoRadiusQuery)
GeoDist(key string, member1, member2, unit string) *FloatCmd
Related
I am calling this code:
package multicall
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)
var multicallContractAddress = common.HexToAddress("0x5e227AD1969Ea493B43F840cfF78d08a6fc17796")
var multicallContractEthBalanceSelector = "4d2301cc"
func GetBalances(addresses []string, ETHProviderURL string) ([]string, error) {
ethProvider, err := ethclient.Dial(ETHProviderURL)
if err != nil {
panic(err)
}
multicallContract, err := NewMulticallCaller(multicallContractAddress, ethProvider)
if err != nil {
panic(err)
}
fmt.Println("multicallContract: ", multicallContract)
var calls = []MulticallCall{}
for _, address := range addresses {
hashAddress := common.HexToHash(address)
call := MulticallCall{multicallContractAddress, []byte("0x"+multicallContractEthBalanceSelector+hashAddress.String()[2:])}
calls = append(calls, call)
}
fmt.Println(string(calls[0].CallData))
var results []byte
err = multicallContract.contract.Call(&bind.CallOpts{}, results, "aggregate", common.Hex2Bytes(addresses[0]))
if err != nil {
panic(err)
}
fmt.Println("Result: ", &results)
return nil, nil
}
like this:
multicall.GetBalances([]string{"<MY_ADDRESS>"}, "<INFURA_API_KEY>")
And it returns this error:
# github.com/mteam88/keyswarm/multicall
multicall/multicall.go:37:58: cannot use results (variable of type []byte) as type *[]interface{} in argument to multicallContract.contract.Call
It seems like results should be a specific type/interface (maybe []uint8) but I can't seem to pass it like that to the Call function.
I have tried tons of things and I can't seem to get it. My Google diving has uncovered this Why am I getting a compile error 'cannot use ... as type uint8 in argument to ...' when the parameter is an int which led me to my previous assumption.
If you -1 please comment (:
Please provide links to relevant documentation in your answer.
NOTE: https://github.com/mteam88/keyswarm/blob/multicall/multicall/multicallContract.go is the location of the defenitions of MulticallCall and other Multicall prefixed defenitions.
So I currently have a function that will take in a string APIKey to check it against my Mongo collection. If nothing is found (not authenticated), it returns false - if a user is found, it returns true. My problem, however, is I'm unsure how to integrate this with a Martini POST route. Here is my code:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/go-martini/martini"
_ "github.com/joho/godotenv/autoload"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
Name string
APIKey string
}
func validateAPIKey(users *mongo.Collection, APIKey string) bool {
var user User
filter := bson.D{{"APIKey", APIKey}}
if err := users.FindOne(context.TODO(), filter).Decode(&user); err != nil {
fmt.Printf("Found 0 results for API Key: %s\n", APIKey)
return false
}
fmt.Printf("Found: %s\n", user.Name)
return true
}
func uploadHandler() {
}
func main() {
mongoURI := os.Getenv("MONGO_URI")
mongoOptions := options.Client().ApplyURI(mongoURI)
client, _ := mongo.Connect(context.TODO(), mongoOptions)
defer client.Disconnect(context.TODO())
if err := client.Ping(context.TODO(), nil); err != nil {
log.Fatal(err, "Unable to access MongoDB server, exiting...")
}
// users := client.Database("sharex_api").Collection("authorized_users") // commented out when testing to ignore unused warnings
m := martini.Classic()
m.Post("/api/v1/upload", uploadHandler)
m.RunOnAddr(":8085")
}
The validateAPIKey function works exactly as intended if tested alone, I am just unsure how I would run this function for a specific endpoint (in this case, /api/v1/upload).
I've a go application that gets run periodically by a batch. Each run, it should read some prometheus metrics from a file, run its logic, update a success/fail counter, and write metrics back out to a file.
From looking at How to parse Prometheus data as well as the godocs for prometheus, I'm able to read in the file, but I don't know how to update app_processed_total with the value returned by expfmt.ExtractSamples().
This is what I've done so far. Could someone please tell me how should I proceed from here? How can I typecast the Vector I got into a CounterVec?
package main
import (
"fmt"
"net/http"
"strings"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
"github.com/prometheus/common/model"
)
var (
fileOnDisk = prometheus.NewRegistry()
processedTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "app_processed_total",
Help: "Number of times ran",
}, []string{"status"})
)
func doInit() {
prometheus.MustRegister(processedTotal)
}
func recordMetrics() {
go func() {
for {
processedTotal.With(prometheus.Labels{"status": "ok"}).Inc()
time.Sleep(5 * time.Second)
}
}()
}
func readExistingMetrics() {
var parser expfmt.TextParser
text := `
# HELP app_processed_total Number of times ran
# TYPE app_processed_total counter
app_processed_total{status="ok"} 300
`
parseText := func() ([]*dto.MetricFamily, error) {
parsed, err := parser.TextToMetricFamilies(strings.NewReader(text))
if err != nil {
return nil, err
}
var result []*dto.MetricFamily
for _, mf := range parsed {
result = append(result, mf)
}
return result, nil
}
gatherers := prometheus.Gatherers{
fileOnDisk,
prometheus.GathererFunc(parseText),
}
gathering, err := gatherers.Gather()
if err != nil {
fmt.Println(err)
}
fmt.Println("gathering: ", gathering)
for _, g := range gathering {
vector, err := expfmt.ExtractSamples(&expfmt.DecodeOptions{
Timestamp: model.Now(),
}, g)
fmt.Println("vector: ", vector)
if err != nil {
fmt.Println(err)
}
// How can I update processedTotal with this new value?
}
}
func main() {
doInit()
readExistingMetrics()
recordMetrics()
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe("localhost:2112", nil)
}
I believe you would need to use processedTotal.WithLabelValues("ok").Inc() or something similar to that.
The more complete example is here
func ExampleCounterVec() {
httpReqs := prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "How many HTTP requests processed, partitioned by status code and HTTP method.",
},
[]string{"code", "method"},
)
prometheus.MustRegister(httpReqs)
httpReqs.WithLabelValues("404", "POST").Add(42)
// If you have to access the same set of labels very frequently, it
// might be good to retrieve the metric only once and keep a handle to
// it. But beware of deletion of that metric, see below!
m := httpReqs.WithLabelValues("200", "GET")
for i := 0; i < 1000000; i++ {
m.Inc()
}
// Delete a metric from the vector. If you have previously kept a handle
// to that metric (as above), future updates via that handle will go
// unseen (even if you re-create a metric with the same label set
// later).
httpReqs.DeleteLabelValues("200", "GET")
// Same thing with the more verbose Labels syntax.
httpReqs.Delete(prometheus.Labels{"method": "GET", "code": "200"})
}
This is taken from the Promethus examples on Github
To use the value of vector you can do the following:
vectorFloat, err := strconv.ParseFloat(vector[0].Value.String(), 64)
if err != nil {
panic(err)
}
processedTotal.WithLabelValues("ok").Add(vectorFloat)
This is assuming you will only ever get a single vector value in your response. The value of the vector is stored as a string but you can convert it to a float with the strconv.ParseFloat method.
I am wondering what would be the most efficient way to retrieve the latest comment from a github issue using Go.
I actually know how to do this already but I am not satisfied with the performance so I would love to get some suggestions
package main
import (
"context"
"fmt"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
"net/url"
"os"
)
func main() {
owner, repo := "owner", "repo"
token := oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}
ts := oauth2.StaticTokenSource(&token)
ctx := context.Background()
tc := oauth2.NewClient(ctx, ts)
gc := github.NewClient(tc)
gc.BaseURL, _ = url.Parse("https://api.github.com/")
opts := github.IssueListByRepoOptions{}
issues, _, _ := gc.Issues.ListByRepo(ctx, owner, repo, &opts)
// Implement Here: get latest comment for issues[0]
return
}
Thanks in advance :)
You can use Rest API v3 or GraphQL v4. If you plan to loop through a lot of issues, graphQL definitly worth it
Using Rest API v3
Using go-github as you suggested, you can use :
ListComments(ctx context.Context, owner string, repo string, number int, opts *IssueListCommentsOptions)
For example from this test
For example to get the last comment for the last 20 opened issues (from your code).
package main
import (
"context"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
"net/url"
"os"
"log"
)
func main() {
owner, repo := "google", "gson"
token := oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}
ts := oauth2.StaticTokenSource(&token)
ctx := context.Background()
tc := oauth2.NewClient(ctx, ts)
gc := github.NewClient(tc)
gc.BaseURL, _ = url.Parse("https://api.github.com/")
opts := github.IssueListByRepoOptions{}
issues, _, _ := gc.Issues.ListByRepo(ctx, owner, repo, &opts)
for i := 0; i < len(issues); i++ {
opt := &github.IssueListCommentsOptions{}
comments, _, err := gc.Issues.ListComments(ctx, owner, repo, *issues[i].Number, opt)
if err != nil {
log.Println(err)
} else if len(comments) > 0 {
log.Println(*comments[0].Body)
} else {
log.Println("no comment for this issue")
}
}
}
It will perform :
one request to get the last 20 opened issues
one request for each issue to get the last comments
So a total of 21 requests
Using GraphQL v4
You can use githubv4 library to use Github GraphQL v4.
The same as previous example in GraphQL would be :
package main
import (
"context"
"github.com/shurcooL/githubv4"
"golang.org/x/oauth2"
"os"
"encoding/json"
"log"
)
func main() {
owner, repo := "google", "gson"
token := oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")}
ts := oauth2.StaticTokenSource(&token)
httpClient := oauth2.NewClient(context.Background(), ts)
client := githubv4.NewClient(httpClient)
{
var q struct {
Repository struct {
Issues struct {
Nodes []struct {
Number int
Comments struct {
Nodes []struct {
Body githubv4.String
}
} `graphql:"comments(last:$commentsLast)"`
}
PageInfo struct {
EndCursor githubv4.String
HasNextPage githubv4.Boolean
}
} `graphql:"issues(last:$issuesLast,states:OPEN)"`
} `graphql:"repository(owner:$repositoryOwner,name:$repositoryName)"`
}
variables := map[string]interface{}{
"repositoryOwner": githubv4.String(owner),
"repositoryName": githubv4.String(repo),
"issuesLast": githubv4.NewInt(20),
"commentsLast": githubv4.NewInt(1),
}
err := client.Query(context.Background(), &q, variables)
if err != nil {
log.Println(err)
return
}
printJSON(q)
}
}
func printJSON(v interface{}) {
w := json.NewEncoder(os.Stdout)
w.SetIndent("", "\t")
err := w.Encode(v)
if err != nil {
panic(err)
}
}
This is modification of the example from the github repo
The code above will perform exactly 1 request
I am faily new to Go and I am trying to create a structured application using guidance from Ben Johnson's webpage. Unfortunately, his example is not a complete working application.
His webpage is https://medium.com/#benbjohnson/standard-package-layout-7cdbc8391fc1
I have tried to use his methods and I keep getting "Undefined: db" error. It doesn't tell me what line is causing the error, just the file "MSSQL.go"
Could someone help with guidance to help me fix this error?
Edited code with accepted solution.
StatementPrinter.go
package statementprinter
type Statement struct {
CustomerId string
CustomerName string
}
type StatementService interface {
Statement(id string) (*Statement, error)
}
main.go
package main
import (
"fmt"
"log"
"github.com/ybenjolin/StatementPrinter"
"github.com/ybenjolin/StatementPrinter/mssql"
"database/sql"
_ "github.com/alexbrainman/odbc"
)
const DB_INFO = "Driver={SQL Server};Server=cdc-edb2;Database=CostarReports;Trusted_Connection=yes;"
var db *sql.DB
func init() {
var err error
db, err = sql.Open("odbc", DB_INFO)
if err != nil {
log.Fatal("Error opening database connection.\n", err.Error())
}
err = db.Ping()
if err != nil {
log.Fatal("Error pinging database server.\n", err.Error())
}
fmt.Println("Database connection established.")
}
func main () {
var err error
defer db.Close()
// Create services
// Changes required here. Was ss := &statementprinter.Stat..
ss := &mssql.StatementService{DB: db}
// Use service
var s *statementprinter.Statement
s, err = ss.Statement("101583")
if err != nil {
log.Fatal("Query failed:", err.Error())
}
fmt.Printf("Statement: %+v\n", s)
}
mssql.go
package mssql
import (
_ "github.com/alexbrainman/odbc"
"database/sql"
"github.com/ybenjolin/StatementPrinter"
)
// StatementService represents a MSSQL implementation of statemenetprinter.StatementService.
type StatementService struct {
DB *sql.DB
}
// Statement returns a statement for a given customer.
func (s *StatementService) Statement(customer string) (*statementprinter.Statement, error) {
var err error
var t statementprinter.Statement
// Changes required here. Was row := db.Query......
row := s.DB.QueryRow(`Select Customer, CustomerName From AccountsReceivable.rptfARStatementHeader(?)`, customer)
if row.Scan(&t.CustomerId, &t.CustomerName); err != nil {
return nil, err
}
return &t, nil
This seems like it's just a typo. It seems like the problematic line is in the method
func (s *StatementService) Statement(customer string)
in mssql.go,
row := db.QueryRow(`Select Customer, CustomerName From AccountsReceivable.rptfARStatementHeader(?)`, customer)
QueryRow is supposed to be a method of db, but db is not defined. However, in the struct
type StatementService struct {
DB *sql.DB
}
there's a *sql.DB instance. The method you're using has a *StatementService parameter, s. So, my guess is the intention would be to access the sql.DB field in s like so
func (s *StatementService) Statement(customer string) (*statementprinter.Statement, error) {
var err error
var t statementprinter.Statement
//CHANGED LINE:
row := s.DB.QueryRow(`Select Customer, CustomerName From AccountsReceivable.rptfARStatementHeader(?)`, customer)
if row.Scan(&t.CustomerId, &t.CustomerName); err != nil {
return nil, err
}
return &t, nil
Then, the method is called in main.go, and is passed a StatementService instance that contains a database:
ss := &statementprinter.StatementService{DB: db}
I believe you need to change this line to
ss := &mssql.StatementService{DB: db}
becuase that's the actual interface implementation. The line you have now treats the StatementService interface like a struct which will not compile.
The global db in main.go lives for the lifetime of the application. It's just a pointer which is copied around for use.