How use update function of mongo-go-driver using struct - mongodb

The update function of mongo-go-driver can be called like this.
filter := bson.D{"username", username}
update := bson.D{{"$set",
bson.D{
{"name", person.Name},
},
}}
result, err := collection.UpdateOne(ctx, filter, update)
type Person struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
Username string `json:"username,omitempty" bson:"username,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
}
But, I need to call the update function using the person struct, without mentioning every field of person struct like this.
filter := bson.D{"username", username}
update := bson.D{{"$set", <<how to convert person struct to bson document?>>}}
result, err := collection.UpdateOne(ctx, filter, update)
How can I convert the person struct to bson document?

ReplaceOne I think is what you're after:
// Use it's ID to replace
filter := bson.M{"_id": existing.ID}
// Create a replacement object using the existing object
replacementObj := existing
replacementObj.SomeFieldToChange = "new-replacement-object"
updateResult, err := coll.ReplaceOne(context.Background(), filter, replacementObj)
assertNotErr(t, err)
assertEquals(t, 1, int(updateResult.ModifiedCount))
A note that ErrNotFound is no longer thrown as it was in mgo - you have to check the Modified/Upserted count.

You could do something like this:
func Update(person Person) error {
pByte, err := bson.Marshal(person)
if err != nil {
return err
}
var update bson.M
err = bson.Unmarshal(pByte, &update)
if err != nil {
return
}
// NOTE: filter and ctx(Context) should be already defined
_, err = collection.UpdateOne(ctx, filter, bson.D{{Key: "$set", Value: update}})
if err != nil {
return err
}
return nil
}

how about marshalling the person struct to bson?
package main
import (
"fmt"
"labix.org/v2/mgo/bson"
)
type person struct {
ID string `json:"_id,omitempty" bson:"_id,omitempty"`
Username string `json:"username,omitempty" bson:"username,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
}
func main() {
p := person{
ID: "id",
Username: "uname",
Name: "name",
}
var (
bm []byte
err error
)
if bm, err = bson.Marshal(p); err != nil {
panic(fmt.Errorf("can't marshal:%s", err))
}
update := bson.D{{"$set", bm}}
fmt.Printf("update is:%q\n", update)
}
run:
./sobson
update is:[{"$set" "4\x00\x00\x00\x02_id\x00\x03\x00\x00\x00id\x00\x02username\x00\x06\x00\x00\x00uname\x00\x02name\x00\x05\x00\x00\x00name\x00\x00"}]

Related

MongoDB Golang UpdateOne array

I made a method UpdateMessage to update the message, I can only update a few fields: message.body, message.update_at and message.modifications array
Below I attached a method that I wrote, I do NOT get an error, none at all, but the data are not updated, if I make a query to the database and check after calling this method, the data will not change.
What am I doing wrong when trying to update a database record?
Is there anything I can do to simplify the ChangeMessage method, namely to get rid of passing the old message to the new one by parameters? I need each message to keep the old version of the message in the message.modifications field if the data changes?
type Message struct {
ID string `json:"id" bson:"id"`
ChatID string `json:"chat_id" bson:"chat_id"`
FromID string `json:"from_id" bson:"from_id"`
CreateDate int64 `json:"create_date" bson:"create_date"`
Type string `json:"type" bson:"type"`
Media string `json:"media" bson:"media"`
Body string `json:"body" bson:"body"`
UpdateAt int64 `json:"update_at" bson:"update_at"`
Modifications []*Message `json:"modifications,omitempty" bson:"modifications"`
Viewed bool `json:"viewed" bson:"viewed"`
}
func (srv *Service) ChangeMessage(ctx context.Context, message *Message) error {
...
chat, err := srv.db.Chat.Find(ctx, &Chat{ID: message.ChatID})
msg, err := srv.db.Chat.FindMessage(ctx, &Message{
ID: message.ID,
ChatID: chat.ID,
FromID: uid,
})
....
message.Modifications = msg.Modifications
message.Modifications = append(message.Modifications, msg)
message.FromID = msg.FromID
message.CreateDate = msg.CreateDate
message.Type = msg.Type
message.Media = msg.Media
message.ChatID = msg.ChatID
message.Viewed = msg.Viewed
err = srv.db.Chat.UpdateMessage(ctx, message)
.......
}
func (m *Mongo) UpdateMessage(ctx context.Context, msg *Message) error {
...
update := bson.M{
"$set": bson.M{
"body": msg.Body,
"update_at": time.Now().UnixMilli(),
},
"$push": bson.M{
"modifications": msg.Modifications,
},
}
_, err := m.col.UpdateOne(ctx, bson.M{"id": msg.ID}, update, options.Update().SetUpsert(true))
....
}
Let me try to help here. I put together a small example to show you how to deal with your scenario (at least for the updating issue):
package main
import (
"context"
"fmt"
"strings"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var (
ctx context.Context
cancel context.CancelFunc
)
type Message struct {
ID string `json:"id" bson:"id"`
Name string `json:"name" bson:"name"`
}
func main() {
ctx, cancel = context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
// set MongoDB connection
clientOptions := options.Client().ApplyURI("mongodb://root:root#localhost:27017")
mongoClient, err := mongo.Connect(ctx, clientOptions)
if err != nil {
panic(err)
}
defer mongoClient.Disconnect(ctx)
// select collection
collection := mongoClient.Database("demodb").Collection("myCollection")
defer collection.Drop(ctx)
fmt.Println("original documents")
// insert some random msg
if _, err := collection.InsertMany(ctx, []interface{}{
Message{"1", "John Doe"},
Message{"2", "Suzy Lee"},
}); err != nil {
panic(err)
}
// get records
var messagesTmp []bson.M
cursorTmp, err := collection.Find(ctx, bson.M{})
if err != nil {
panic(err)
}
if err := cursorTmp.All(ctx, &messagesTmp); err != nil {
panic(err)
}
for _, v := range messagesTmp {
fmt.Println(v)
}
fmt.Println(strings.Repeat("#", 100))
// update
var updateRes *mongo.UpdateResult
if updateRes, err = collection.UpdateOne(ctx,
bson.M{"name": "John Doe"},
bson.D{
bson.E{
Key: "$set",
Value: bson.D{
bson.E{
Key: "name",
Value: "John Doe - Edited",
},
},
},
},
); err != nil {
panic(err)
}
fmt.Println("num docs updated:", updateRes.ModifiedCount)
// list all
fmt.Println(strings.Repeat("#", 100))
fmt.Println("new documents")
var messages []Message
cursor, err := collection.Find(ctx, bson.M{})
if err != nil {
panic(err)
}
if err := cursor.All(ctx, &messages); err != nil {
panic(err)
}
for _, v := range messages {
fmt.Println(v)
}
}
With this code, you should be able to successfully update your documents in MongoDB. To give it a try, make sure to run MongoDB at the right address with the credentials expected in the code.
About the document versioning, I think that you're already following a kind of Document Versioning Pattern. You can find more about it at this link.
Let me know if this clarifies your doubts or if you need something else, thanks!

How to convert a slice of bson.M to a slice of struct ? when working with MongoDB

I am working with MongoDB. I query a slice (list) of data then I want to convert it to a slice (list) of struct.
Here is my so far code:
type PostDBTagMongo struct {
IDPost int `bson:"id_post"`
Name string `bson:"name"`
}
var tagsList []model.PostDBTagMongo
// Query
ctx := context.Background()
cursor, _:= p.postTagCollection.Find(ctx, bson.M{"id_post": id})
var postTag []bson.M
if err = cursor.All(ctx, &postTag); err != nil {
log.Fatal(err)
}
postTagBytes, err := bson.Marshal(postTag)
bson.Unmarshal(postTagBytes, &tagsList)
Error at this line of code
postTagBytes, err := bson.Marshal(postTag)
Here is the error
{
"name": "WriteArray",
"parent": 0,
"action": "write",
....
}

Mongo FindOne and interface{}

edit: solved, see first answer. Should have really noticed that while I was creating this example..
I'm trying to create a function that takes an interface instead of a specific type and calls the FindOne function. Does anyone know why the printFirstTrainerByInterface function does not work properly?
I'm using the official Go Mongo-Driver and sample snippets from mongodb-go-driver-tutorial.
package main
import (
"context"
"fmt"
"log"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Trainer struct {
Name string
Age int
City string
}
var db *mongo.Database
func main() {
opts := options.Client().ApplyURI("mongodb://localhost:27017")
client, err := mongo.Connect(context.TODO(), opts)
if err != nil {
log.Fatal(err)
}
err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
db = client.Database("test")
insertTestDocument()
var result Trainer
printFirstTrainer(result)
var result2 Trainer
printFirstTrainerByInterface(&result2)
}
func insertTestDocument() {
ash := Trainer{"Ash", 10, "Pallet Town"}
res, err := db.Collection("trainers").InsertOne(context.TODO(), ash)
if err != nil {
log.Fatal(err)
}
fmt.Println("Inserted a test document: ", res.InsertedID)
}
func printFirstTrainer(result Trainer) {
collection := db.Collection("trainers")
err := collection.FindOne(context.TODO(), bson.M{}).Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found a single document: %+v\n", result)
}
func printFirstTrainerByInterface(result interface{}) {
collection := db.Collection("trainers")
err := collection.FindOne(context.TODO(), bson.M{}).Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found a single document: %+v\n", result)
}
Output:
Inserted a test document: ObjectID("5e8216f74f41a13f01061d61")
Found a single document: {Name:Ash Age:10 City:Pallet Town}
Found a single document: [{Key:_id Value:ObjectID("5e8216f74f41a13f01061d61")} {Key:name Value:Ash} {Key:age Value:10} {Key:city Value:Pallet Town}]
You are passing in the address of the struct you want to decode into as an interface. You have to pass that as the argument to decode, not the address of the interface. Try:
err := collection.FindOne(context.TODO(), bson.M{}).Decode(result)

How to wrtie data to influxdb from golang array?

The problem is:
I have a database in PostgreSQL and I read all the data in Golang and make arrays from it. The question is: How can I take this arrays and put it in influxdb?
package main
import (
"database/sql"
"log"
_ "./pq"
"fmt"
)
type DbInfo struct {
id string
person_id int
timestamp int
age string
gender string
attention string
interest int
happines int
surprise int
anger int
disgust int
fear string
sadness int
neutrall string
}
var DBObject DbInfo
var DbArray []DbInfo
func main() {
db, err := sql.Open("****", "postgres://postgres:********/cam?
sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()
rows, err := db.Query(`SELECT avg(age), avg(id), avg(neutrall),avg(fear)
FROM public.stat`) //stringTimeDayAgoQuery )
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
//err := rows.Scan(&DBObject.id, &DBObject.person_id,
&DBObject.timestamp,&DBObject.age,
&DBObject.gender,&DBObject.attention,&DBObject.interest,&DBObject.happines,
&DBObject.surprise,&DBObject.anger,&DBObject.disgust,&DBObject.fear,
&DBObject.sadness,&DBObject.neutrall)
err := rows.Scan(&DBObject.age,&DBObject.id,&DBObject.neutrall,
&DBObject.fear)
if err != nil {
log.Fatal(err)
}
DbArray = append(DbArray, DBObject)
}
fmt.Println(DbArray)
}
In this code. I read my data from postgreSQL. I read it as an array, now I want to import my output to Influxdb. Because I want to use Grafana and it doesn't work with postgresql.

How to find by id in golang and mongodb

I need get values using ObjectIdHex and do update and also view the result. I'm using mongodb and golang.But following code doesn't work as expected
package main
import (
"fmt"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
type Person struct {
Id bson.ObjectId `json:"id" bson:"_id,omitempty"`
Name string
Phone string
}
func checkError(err error) {
if err != nil {
panic(err)
}
}
const (
DB_NAME = "gotest"
DB_COLLECTION = "pepole_new1"
)
func main() {
session, err := mgo.Dial("localhost")
checkError(err)
defer session.Close()
session.SetMode(mgo.Monotonic, true)
c := session.DB(DB_NAME).C(DB_COLLECTION)
err = c.DropCollection()
checkError(err)
ale := Person{Name:"Ale", Phone:"555-5555"}
cla := Person{Name:"Cla", Phone:"555-1234-2222"}
kasaun := Person{Name:"kasaun", Phone:"533-12554-2222"}
chamila := Person{Name:"chamila", Phone:"533-545-6784"}
fmt.Println("Inserting")
err = c.Insert(&ale, &cla, &kasaun, &chamila)
checkError(err)
fmt.Println("findbyID")
var resultsID []Person
//err = c.FindId(bson.ObjectIdHex("56bdd27ecfa93bfe3d35047d")).One(&resultsID)
err = c.FindId(bson.M{"Id": bson.ObjectIdHex("56bdd27ecfa93bfe3d35047d")}).One(&resultsID)
checkError(err)
if err != nil {
panic(err)
}
fmt.Println("Phone:", resultsID)
fmt.Println("Queryingall")
var results []Person
err = c.Find(nil).All(&results)
if err != nil {
panic(err)
}
fmt.Println("Results All: ", results)
}
FindId(bson.M{"Id": bson.ObjectIdHex("56bdd27ecfa93bfe3d35047d")}).One(&resultsID) didn't work for me and giving me following output
Inserting
Queryingall
Results All: [{ObjectIdHex("56bddee2cfa93bfe3d3504a1") Ale 555-5555} {ObjectIdHex("56bddee2cfa93bfe3d3504a2") Cla 555-1234-2222} {ObjectIdHex("56bddee2cfa93bfe3d3504a3") kasaun 533-12554-2222} {ObjectIdHex("56bddee2cfa93bfe3d3504a4") chamila 533-545-6784}]
findbyID
panic: not found
goroutine 1 [running]:
main.checkError(0x7f33d524b000, 0xc8200689b0)
How can i fix this problem? i need get value using oid and do update also how can i do that
Use can do the same with Golang official driver as follows:
// convert id string to ObjectId
objectId, err := primitive.ObjectIDFromHex("5b9223c86486b341ea76910c")
if err != nil{
log.Println("Invalid id")
}
// find
result:= client.Database(database).Collection("user").FindOne(context.Background(), bson.M{"_id": objectId})
user := model.User{}
result.Decode(user)
It should be _id not Id:
c.FindId(bson.M{"_id": bson.ObjectIdHex("56bdd27ecfa93bfe3d35047d")})
Some sample code that i use.
func (model *SomeModel) FindId(id string) error {
db, ctx, client := Drivers.MongoCollection("collection")
defer client.Disconnect(ctx)
objID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return err
}
filter := bson.M{"_id": bson.M{"$eq": objID}}
if err := db.FindOne(ctx, filter).Decode(&model); err != nil {
//fmt.Println(err)
return err
}
fmt.Println(model)
return nil
}