todo app-v2 in golang with mongodb

t

In our previous article, be build this app.

Not the best todo app, but it works. Still it has a problem.

Once you stop the program and re run it, all the data stored in it will be lost. So how to solve this?

First you might think about storing the values in a .txt file and then read it in the program. Sure its doable. But it needs a lot of parsing, etc.

So use a Database. In this article I’m gonna use mongodb, a nosql database.

https://www.mongodb.com/

setting up mongodb

Watch the installation steps in the video.

I don’t know about you. But in my case I have to specify dbpath whenever i run mongo. So I used an alias.

alias mongorun="mongod --dbpath ~/data/db" 

Next once you installed mongodb, You need mongodb driver to make mongo work with GOlang.

https://www.mongodb.com/blog/post/mongodb-go-driver-tutorial

go get go.mongodb.org/mongo-driver

Run the above code in your terminal. It should download that driver.

go-mongo basics

This is how your basic go code looks like:

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"
)

func main() {
	
}

Now lets connect to the database. Make sure you run mongod before this.

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"
)

func main() {
	
	// Set client options
	clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

	// Connect to MongoDB
	client, err := mongo.Connect(context.TODO(), clientOptions)

	if err != nil {
		log.Fatal(err)
	}

	// Check the connection
	err = client.Ping(context.TODO(), nil)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Connected to MongoDB!")

}

crud

Ok so for starters. Lets store a string.

create a struct

type Todo struct {
	Name string
}

insert into the document

todoObj := Todo{"hi"}

	insertResult, err := collection.InsertOne(context.TODO(), todoObj)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Inserted a single document: ", insertResult.InsertedID)

Good. But you might need to check to make sure.

Install mongodb compass. https://www.mongodb.com/download-center/compass

Or just use the terminal(mongo shell)

Type mongo in terminal.

Looks like it worked.

Now lets print it.

// Here's an array in which you can store the decoded documents
	var results []*Todo

	// Passing bson.D{{}} as the filter matches all documents in the collection
	cur, err := collection.Find(context.TODO(), bson.D{{}})
	if err != nil {
		log.Fatal(err)
	}

	// Finding multiple documents returns a cursor
	// Iterating through the cursor allows us to decode documents one at a time
	for cur.Next(context.TODO()) {

		// create a value into which the single document can be decoded
		var elem Todo
		err := cur.Decode(&elem)
		if err != nil {
			log.Fatal(err)
		}

		results = append(results, &elem)
	}

	if err := cur.Err(); err != nil {
		log.Fatal(err)
	}

	// Close the cursor once finished
	cur.Close(context.TODO())

	fmt.Printf("Found multiple documents (array of pointers): %+v\n", results)

Now as we got the pointer, we can just iterate it.

	//iterate
	for i := 0; i < len(results); i++ {
		fmt.Println(results[i].Name)
	}

Now lets delete

res, err := collection.DeleteOne(context.TODO(), bson.M{"name": "hi"})
	if err != nil {
		log.Fatal("DeleteOne() ERROR:", err)
	} else {
		// Check if the response is 'nil'
		if res.DeletedCount == 0 {
			fmt.Println("DeleteOne() document not found:", res)
		} else {
			// Print the results of the DeleteOne() method
			fmt.Println("DeleteOne Result:", res)

			// *mongo.DeleteResult object returned by API call
			fmt.Println("DeleteOne TYPE:", reflect.TypeOf(res))
		}
	}

And thats the basic. Here is the complete code:

package main

import (
	"context"
	"fmt"
	"log"
	"reflect"

	"go.mongodb.org/mongo-driver/bson"

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type Todo struct {
	Name string
}

func main() {
	//connect to mongo

	// Set client options
	clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

	// Connect to MongoDB
	client, err := mongo.Connect(context.TODO(), clientOptions)

	if err != nil {
		log.Fatal(err)
	}

	// Check the connection
	err = client.Ping(context.TODO(), nil)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Connected to MongoDB!")

	//choose your collection
	collection := client.Database("test").Collection("todolist")

	//insert
	todoObj := Todo{}
	todoObj.Name = "hihi"
	

	insertResult, err := collection.InsertOne(context.TODO(), todoObj)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Inserted a single document: ", insertResult.InsertedID)

	//

	// Here's an array in which you can store the decoded documents
	var results []*Todo

	// Passing bson.D{{}} as the filter matches all documents in the collection
	cur, err := collection.Find(context.TODO(), bson.D{{}})
	if err != nil {
		log.Fatal(err)
	}

	// Finding multiple documents returns a cursor
	// Iterating through the cursor allows us to decode documents one at a time
	for cur.Next(context.TODO()) {

		// create a value into which the single document can be decoded
		var elem Todo
		err := cur.Decode(&elem)
		if err != nil {
			log.Fatal(err)
		}

		results = append(results, &elem)
	}

	if err := cur.Err(); err != nil {
		log.Fatal(err)
	}

	// Close the cursor once finished
	cur.Close(context.TODO())

	fmt.Printf("Found multiple documents (array of pointers): %+v\n", results)

	//iterate
	for i := 0; i < len(results); i++ {
		fmt.Println(results[i])
	}

	//delete
	res, err := collection.DeleteOne(context.TODO(), bson.M{"name": "hi"})
	if err != nil {
		log.Fatal("DeleteOne() ERROR:", err)
	} else {
		// Check if the response is 'nil'
		if res.DeletedCount == 0 {
			fmt.Println("DeleteOne() document not found:", res)
		} else {
			// Print the results of the DeleteOne() method
			fmt.Println("DeleteOne Result:", res)

			// *mongo.DeleteResult object returned by API call
			fmt.Println("DeleteOne TYPE:", reflect.TypeOf(res))
		}
	}

}

And so I reformatted the code. Now it looks like this:

package main

import (
	"context"
	"fmt"
	"log"
	"reflect"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

//connect to mongo

// Set client options
var clientOptions = options.Client().ApplyURI("mongodb://localhost:27017")

// Connect to MongoDB
var client, err = mongo.Connect(context.TODO(), clientOptions)

//choose your collection
var collection = client.Database("test").Collection("todolist")


type Todo struct {
	Name string
}

func deleteTodo(val string) {
	//delete
	res, err := collection.DeleteOne(context.TODO(), bson.M{"name": val})
	if err != nil {
		log.Fatal("DeleteOne() ERROR:", err)
	} else {
		// Check if the response is 'nil'
		if res.DeletedCount == 0 {
			fmt.Println("DeleteOne() document not found:", res)
		} else {
			// Print the results of the DeleteOne() method
			fmt.Println("DeleteOne Result:", res)

			// *mongo.DeleteResult object returned by API call
			fmt.Println("DeleteOne TYPE:", reflect.TypeOf(res))
		}
	}
}

func connectmongo(){
	if err != nil {
		log.Fatal(err)
	}

	// Check the connection
	err = client.Ping(context.TODO(), nil)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Connected to MongoDB!")
}

func inserttodo(val string){
	//insert
	todoObj := Todo{}
	todoObj.Name = val

	insertResult, err := collection.InsertOne(context.TODO(), todoObj)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Inserted a single document: ", insertResult.InsertedID)

}

func printtodo(){
	// Here's an array in which you can store the decoded documents
	var results []*Todo

	// Passing bson.D{{}} as the filter matches all documents in the collection
	cur, err := collection.Find(context.TODO(), bson.D{{}})
	if err != nil {
		log.Fatal(err)
	}

	// Finding multiple documents returns a cursor
	// Iterating through the cursor allows us to decode documents one at a time
	for cur.Next(context.TODO()) {

		// create a value into which the single document can be decoded
		var elem Todo
		err := cur.Decode(&elem)
		if err != nil {
			log.Fatal(err)
		}

		results = append(results, &elem)
	}

	if err := cur.Err(); err != nil {
		log.Fatal(err)
	}

	// Close the cursor once finished
	cur.Close(context.TODO())

	fmt.Printf("Found multiple documents (array of pointers): %+v\n", results)

	//iterate
	for i := 0; i < len(results); i++ {
		fmt.Println(results[i])
	}
}

func main() {

	//connect to mongo
	connectmongo()

	//insert
	inserttodo("ash")

	//print
	printtodo()

	//delete
	deleteTodo("ash")

	//print
	printtodo()

}

todo-v2

Now as we know, how to add, delete, print, we can use it in out todolist app.

So I re-write our previous code. Now we added storage to our cli app.

package main

import (
	"bufio"
	"context"
	"fmt"
	"log"
	"os"
	"reflect"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

//connect to mongo

// Set client options
var clientOptions = options.Client().ApplyURI("mongodb://localhost:27017")

// Connect to MongoDB
var client, err = mongo.Connect(context.TODO(), clientOptions)

//choose your collection
var collection = client.Database("test").Collection("todolist")

type Todo struct {
	Name string
}

func deleteTodo(val string) {
	//delete
	res, err := collection.DeleteOne(context.TODO(), bson.M{"name": val})
	if err != nil {
		log.Fatal("DeleteOne() ERROR:", err)
	} else {
		// Check if the response is 'nil'
		if res.DeletedCount == 0 {
			fmt.Println("DeleteOne() document not found:", res)
		} else {
			// Print the results of the DeleteOne() method
			fmt.Println("DeleteOne Result:", res)

			// *mongo.DeleteResult object returned by API call
			fmt.Println("DeleteOne TYPE:", reflect.TypeOf(res))
		}
	}
}

func connectmongo() {
	if err != nil {
		log.Fatal(err)
	}

	// Check the connection
	err = client.Ping(context.TODO(), nil)

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Connected to MongoDB!")
}

func inserttodo(val string) {
	//insert
	todoObj := Todo{}
	todoObj.Name = val

	insertResult, err := collection.InsertOne(context.TODO(), todoObj)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Inserted a single document: ", insertResult.InsertedID)

}

func printtodo() {
	// Here's an array in which you can store the decoded documents
	var results []*Todo

	// Passing bson.D{{}} as the filter matches all documents in the collection
	cur, err := collection.Find(context.TODO(), bson.D{{}})
	if err != nil {
		log.Fatal(err)
	}

	// Finding multiple documents returns a cursor
	// Iterating through the cursor allows us to decode documents one at a time
	for cur.Next(context.TODO()) {

		// create a value into which the single document can be decoded
		var elem Todo
		err := cur.Decode(&elem)
		if err != nil {
			log.Fatal(err)
		}

		results = append(results, &elem)
	}

	if err := cur.Err(); err != nil {
		log.Fatal(err)
	}

	// Close the cursor once finished
	cur.Close(context.TODO())

//	fmt.Printf("Found multiple documents (array of pointers): %+v\n", results)

	//iterate
	fmt.Println("--------your todo--------")
	for i := 0; i < len(results); i++ {
		fmt.Println(results[i].Name)
	}
  fmt.Println("-------------------------")
}

func greet() {
	fmt.Println("1) add")
	fmt.Println("2) delete")
	fmt.Println("3) print")
	fmt.Println("4) quit")
}

func scan() string {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		return scanner.Text()
	}
	return ""
}

func main() {

	//connect to mongo
	connectmongo()

	isRun := true

	for isRun {

		greet()

		var choice int
		fmt.Print("Enter your choice: ")
		fmt.Scan(&choice)

		switch choice {
		case 1:
			//add some todos
			var todo string
			todo = scan()
			inserttodo(todo)
		case 2:
			//deletion
			var todostr string
			todostr = scan()
			deleteTodo(todostr)
		case 3:
			printtodo()
		case 4:
			isRun = false
			break
		default:
			fmt.Println("enter a valid number")
		}

	}

}

And thats all for today.

Happy coding!

About the author

vigneshwar

2 comments

Leave a Reply

By vigneshwar

Most common tags

%d bloggers like this: