Joining 2 structs from mongodb - go

I have a function that retrieve the mongodb admin users using .command
In the below function, I have the DbUsers struct, and I am running the command to retrieve the users from 2 different database.
My question is, how can I concat the 2 results (adminUsers & externalUsers) and return after merged? They are of the same struct.
type DbUsers struct {
...lots of stuff about the server
Users []Users
}
type Users struct {
User string
...lots of stuff
}
func getUsers() Users {
admin := CNX.Database("admin")
external := CNX.Database("$external")
command := bson.D{primitive.E{Key: "usersInfo", Value: 1}}
var adminUsers DbUsers
var externalUsers DbUsers
err := admin.RunCommand(context.TODO(), command).Decode(&adminUsers)
if err != nil {
panic(err)
}
err2 := external.RunCommand(context.TODO(), command).Decode(&externalUsers)
if err2 != nil {
panic(err2)
}
//New to Golang, not sure what I am doing but this doesn't work
return []Users{adminUsers.Users, externalUsers.Users}
}

You can do
return append(adminUsers.Users, externalUsers.Users...)

Related

Error while trying to fetch queryresult.KV object in JSON.Unmarshal

I am a little bit confused here and although I have searched a lot on this, something is clearly missing from my knowledge and I am asking your help.
I have created a Hyperledger Fabric Network and installed a chaincode in it. And I want to make a function that retrieves all the World State inputs about the Keys. I have done it already with the bytes.Buffer and it worked. But what I want to do is to do it with a struct.
So, I created the following struct that has only the key:
type WSKeys struct {
Key string `json: "key"`
Namespace string `json: "Namespace"`
}
And this is my code function:
func (s *SmartContract) getAllWsDataStruct(APIstub shim.ChaincodeStubInterface , args []string) sc.Response {
var keyArrayStr []WSKeys
resultsIterator, err := APIstub.GetQueryResult("{\"selector\":{\"_id\":{\"$ne\": null }} }")
if err != nil {
return shim.Error("Error occured when trying to fetch data: "+err.Error())
}
for resultsIterator.HasNext() {
// Get the next record
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
fmt.Println(queryResponse)
var qry_key_json WSKeys
json.Unmarshal([]byte(queryResponse), &qry_key_json)
keyArray = append(keyArray, qry_key_json)
}
defer resultsIterator.Close()
all_bytes, _ := json.Marshal(keyArray)
fmt.Println(keyArray)
return shim.Success(all_bytes)
}
When executing the above I get the following error:
cannot convert queryResponse (type *queryresult.KV) to type []byte
I can get the results correctly if I, for example do this:
func (s *SmartContract) getAllWsDataStruct(APIstub shim.ChaincodeStubInterface , args []string) sc.Response {
var keyArray []string
resultsIterator, err := APIstub.GetQueryResult("{\"selector\":{\"_id\":{\"$ne\": null }} }")
if err != nil {
return shim.Error("Error occured when trying to fetch data: "+err.Error())
}
for resultsIterator.HasNext() {
// Get the next record
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
fmt.Println(queryResponse)
keyArray = append(keyArray, queryResponse.Key)
}
defer resultsIterator.Close()
all_bytes, _ := json.Marshal(keyArray)
fmt.Println(keyArray)
return shim.Success(all_bytes)
}
But, why I get the above error when trying to add the queryResponse into a custom struct?
Do I need to add it to a struct that is only its type?
Please someone can explain what I am missing here?
The error statement is verbose enough to indicate, that your []byte conversion failed for the type queryResponse which, with a bit of lookup seems to be a struct type. In Go you cannot natively convert a struct instance to its constituent bytes without encoding using gob or other means.
Perhaps your intention was to use the Key record in the struct for un-marshalling
json.Unmarshal([]byte(queryResponse.Key), &qry_key_json)

Is there an API to retrieve the neo4j relationship details using the Go bolt driver?

Using the neo4j Go bolt driver, I am able to get nodes, but not relationships from the graph db.
The Run() API in neo4j.transaction return type is Result which can give nodes, but not to the relationships?
If I try the query in the neo4j browser, it shows me the properties of the relationships, but if I send the same query programmatically, I don’t get anything. Am I missing something?
MATCH (:a {name: ‘foo’})-[r:bar]->() RETURN properties(r)
The above query works
{
"X":"20",
"Y":"40"
}
But the same query sent via the driver returns no error, but has nothing in it.
There might be something wrong with your mapping code.
It should look like this:
func readCoordinates(driver neo4j.Driver) ([]Coordinates, error) {
session := driver.NewSession(neo4j.SessionConfig{})
defer session.Close()
result, err := session.ReadTransaction(executeReadCoordinates)
if err != nil {
return nil, err
}
return result.([]Coordinates), nil
}
func executeReadCoordinates(tx neo4j.Transaction) (interface{}, error) {
records, err := tx.Run("MATCH (:A {name: 'foo'})-[r:BAR]->() RETURN properties(r)", map[string]interface{}{})
if err != nil {
return nil, err
}
var results []Coordinates
for records.Next() {
record := records.Record()
if props, found := record.Get("properties(r)"); !found {
return nil, fmt.Errorf("expected properties not found")
} else {
properties := props.(map[string]interface{})
coordinates := Coordinates{
X: properties["x"].(int64),
Y: properties["y"].(int64),
}
results = append(results, coordinates)
}
}
return results, nil
}
I changed the case of the node label (convention: PascalCase), relationship type (convention: SCREAMING_SNAKE_CASE) and properties (convention: snake_case).
The code assumes those properties are of type int64 and fetches a list.
If you want a single pair of coordinates, then remove the for loop and use records.Single() instead.

Fatal error when retrieving multiple rows with gorm Find()

I'm stuck with an obvious operation: retrieving multiple rows using gorm.Find() method.
(resolver.go)
package resolver
type Root struct {
DB *gorm.DB
}
func (r *Root) Users(ctx context.Context) (*[]*UserResolver, error) {
var userRxs []*UserResolver
var users []model.User
// debug-start
// This is to prove r.DB is allocated and working
// It will print {2 alice#mail.com} in the console
var user model.User
r.DB.Find(&user)
log.Println(user)
// debug-end
if err := r.DB.Find(&users); err != nil { // <-- not working
log.Fatal(err)
}
for _, user := range users {
userRxs = append(userRxs, &UserResolver{user})
log.Println(user)
}
return &userRxs, nil
}
(model.go)
package model
type User struct {
ID graphql.ID `gorm:"primary_key"`
Email string `gorm:"unique;not null"`
}
The mysql table is filled with 2 values. Here is the content in json style:
{
{ Email: bob#mail.com },
{ Email: alice#mail.com },
}
This is the result when I run the program:
2020/05/13 12:23:17 Listening for requests on :8000
2020/05/13 12:23:22 {2 alice#mail.com}
2020/05/13 12:23:22 &{{{0 0} 0 0 0 0} 0xc0004cee40 <nil> 2 0xc00031e3c0 false 0 {0xc00035bea0} 0xc0004b3080 {{0 0} {<nil>} map[] 0} 0xc000408340 <nil> 0xc0004cee60 false <nil>}
What is wrong with my code? It seems from all the tuto/so/etc.. sources that I'm correctly defining a slice var and passing it to the Find() function..
if err := r.DB.Find(&users); err != nil { // <-- not working
log.Fatal(err)
}
Probably you forgot to mention Error property and returned object in this case is not nil for sure (please mention that Find returns not error interface in this case)
Please try something like that
if err := r.DB.Find(&users).Error; err != nil {
log.Fatal(err)
}
Hope it helps
You need to use a slice of pointers:
users := make([]*model.User, 0, 2)
if err := r.DB.Find(&users).Error; err != nil {
log.Fatal(err)
}

GORM Associations

Given the following data structure which has been created in the database and there is valid data in the rows in the appropriate tables:-
type Deployment struct {
gorm.Model
Name string `gorm:"unique_index:idx_name"`
RestAPIUser string
RestAPIPass string
Servers []Server
model *Model
}
type Server struct {
gorm.Model
DeploymentID uint
Hostname string `gorm:"unique_index:idx_hostname"`
RestPort string
Version string
}
I'm trying to select all Deployments and have GORM automatically fill the Servers for each Deployment.
Unfortunately, it doesn't do this. I've tried several variations of using the Associations() func but I can't seem to get it to work. I seem to have to do this manually:-
func (m *Model) GetDeployments() ([]Deployment, error) {
deployments := []Deployment{}
err := m.db.Find(&deployments).Error
if err != nil {
return nil, err
}
deploymentsWithServers := []Deployment{}
for _, d := range deployments {
servers := []Server{}
err := m.db.Model(&d).Association("Servers").Find(&servers).Error
if err != nil {
return nil, err
}
d.Servers = servers
deploymentsWithServers = append(deploymentsWithServers, d)
}
return deploymentsWithServers, nil
}
Does anyone have any suggestions how I can get GORM to fill the Servers field automatically? Thanks!
Try
m.db.Preload("Servers").Find(&Deployment{})

How can I make this object mapping more dry and reusable in Go?

I have created an object mapping in Go that is not relational, it is very simple.
I have several structs that looks like this:
type Message struct {
Id int64
Message string
ReplyTo sql.NullInt64 `db:"reply_to"`
FromId int64 `db:"from_id"`
ToId int64 `db:"to_id"`
IsActive bool `db:"is_active"`
SentTime int64 `db:"sent_time"`
IsViewed bool `db:"is_viewed"`
Method string `db:"-"`
AppendTo int64 `db:"-"`
}
To create a new message I just run this function:
func New() *Message {
return &Message{
IsActive: true,
SentTime: time.Now().Unix(),
Method: "new",
}
}
And then I have a message_crud.go file for this struct that looks like this:
To find a message by a unique column (for example by id) I run this function:
func ByUnique(column string, value interface{}) (*Message, error) {
query := fmt.Sprintf(`
SELECT *
FROM message
WHERE %s = ?
LIMIT 1;
`, column)
message := &Message{}
err := sql.DB.QueryRowx(query, value).StructScan(message)
if err != nil {
return nil, err
}
return message, nil
}
And to save a message (insert or update in the database) I run this method:
func (this *Message) save() error {
s := ""
if this.Id == 0 {
s = "INSERT INTO message SET %s;"
} else {
s = "UPDATE message SET %s WHERE id=:id;"
}
query := fmt.Sprintf(s, sql.PlaceholderPairs(this))
nstmt, err := sql.DB.PrepareNamed(query)
if err != nil {
return err
}
res, err := nstmt.Exec(*this)
if err != nil {
return err
}
if this.Id == 0 {
lastId, err := res.LastInsertId()
if err != nil {
return err
}
this.Id = lastId
}
return nil
}
The sql.PlaceholderPairs() function looks like this:
func PlaceholderPairs(i interface{}) string {
s := ""
val := reflect.ValueOf(i).Elem()
count := val.NumField()
for i := 0; i < count; i++ {
typeField := val.Type().Field(i)
tag := typeField.Tag
fname := strings.ToLower(typeField.Name)
if fname == "id" {
continue
}
if t := tag.Get("db"); t == "-" {
continue
} else if t != "" {
s += t + "=:" + t
} else {
s += fname + "=:" + fname
}
s += ", "
}
s = s[:len(s)-2]
return s
}
But every time I create a new struct, for example a User struct I have to copy paste the "crud section" above and create a user_crud.go file and replace the words "Message" with "User", and the words "message" with "user". I repeat alot of code and it is not very dry. Is there something I could do to not repeat this code for things I would reuse? I always have a save() method, and always have a function ByUnique() where I can return a struct and search by a unique column.
In PHP this was easy because PHP is not statically typed.
Is this possible to do in Go?
Your ByUnique is almost generic already. Just pull out the piece that varies (the table and destination):
func ByUnique(table string, column string, value interface{}, dest interface{}) error {
query := fmt.Sprintf(`
SELECT *
FROM %s
WHERE %s = ?
LIMIT 1;
`, table, column)
return sql.DB.QueryRowx(query, value).StructScan(dest)
}
func ByUniqueMessage(column string, value interface{}) (*Message, error) {
message := &Message{}
if err := ByUnique("message", column, value, &message); err != nil {
return nil, err
}
return message, error
}
Your save is very similar. You just need to make a generic save function along the lines of:
func Save(table string, identifier int64, source interface{}) { ... }
Then inside of (*Message)save, you'd just call the general Save() function. Looks pretty straightforward.
Side notes: do not use this as the name of the object inside a method. See the link from #OneOfOne for more on that. And do not get obsessed about DRY. It is not a goal in itself. Go focuses on code being simple, clear, and reliable. Do not create something complicated and fragile just to avoid typing a simple line of error handling. This doesn't mean that you shouldn't extract duplicated code. It just means that in Go it is usually better to repeat simple code a little bit rather than create complicated code to avoid it.
EDIT: If you want to implement Save using an interface, that's no problem. Just create an Identifier interface.
type Ider interface {
Id() int64
SetId(newId int64)
}
func (msg *Message) Id() int64 {
return msg.Id
}
func (msg *Message) SetId(newId int64) {
msg.Id = newId
}
func Save(table string, source Ider) error {
s := ""
if source.Id() == 0 {
s = fmt.Sprintf("INSERT INTO %s SET %%s;", table)
} else {
s = fmt.Sprintf("UPDATE %s SET %%s WHERE id=:id;", table)
}
query := fmt.Sprintf(s, sql.PlaceholderPairs(source))
nstmt, err := sql.DB.PrepareNamed(query)
if err != nil {
return err
}
res, err := nstmt.Exec(source)
if err != nil {
return err
}
if source.Id() == 0 {
lastId, err := res.LastInsertId()
if err != nil {
return err
}
source.SetId(lastId)
}
return nil
}
func (msg *Message) save() error {
return Save("message", msg)
}
The one piece that might blow up with this is the call to Exec. I don't know what package you're using, and it's possible that Exec won't work correctly if you pass it an interface rather than the actual struct, but it probably will work. That said, I'd probably just pass the identifier rather than adding this overhead.
You probably want to use an ORM.
They eliminate a lot of the boilerplate code you're describing.
See this question for "What is an ORM?"
Here is a list of ORMs for go: https://github.com/avelino/awesome-go#orm
I have never used one myself, so I can't recommend any. The main reason is that an ORM takes a lot of control from the developer and introduces a non-negligible performance overhead. You need to see for yourself if they fit your use-case and/or if you are comfortable with the "magic" that's going on in those libraries.
I don't recommend doing this, i personally would prefer being explicit about scanning into structs and creating queries.
But if you really want to stick to reflection you could do:
func ByUnique(obj interface{}, column string, value interface{}) error {
// ...
return sql.DB.QueryRowx(query, value).StructScan(obj)
}
// Call with
message := &Message{}
ByUnique(message, ...)
And for your save:
type Identifiable interface {
Id() int64
}
// Implement Identifiable for message, etc.
func Save(obj Identifiable) error {
// ...
}
// Call with
Save(message)
The approach i use and would recommend to you:
type Redirect struct {
ID string
URL string
CreatedAt time.Time
}
func FindByID(db *sql.DB, id string) (*Redirect, error) {
var redirect Redirect
err := db.QueryRow(
`SELECT "id", "url", "created_at" FROM "redirect" WHERE "id" = $1`, id).
Scan(&redirect.ID, &redirect.URL, &redirect.CreatedAt)
switch {
case err == sql.ErrNoRows:
return nil, nil
case err != nil:
return nil, err
}
return &redirect, nil
}
func Save(db *sql.DB, redirect *Redirect) error {
redirect.CreatedAt = time.Now()
_, err := db.Exec(
`INSERT INTO "redirect" ("id", "url", "created_at") VALUES ($1, $2, $3)`,
redirect.ID, redirect.URL, redirect.CreatedAt)
return err
}
This has the advantage of using the type system and mapping only things it should actually map.

Resources