Table of Contents
Preface
Who is the book for?
Are you new to Go and taking part in the Go Challenge? Some of the challenges might require you to write an app in Go that uses MongoDB on MongoLab and Heroku. Don’t know how? If yes, then this eBook is for you and will show you how it’s done.
What will you learn?
In the end, you will understand and know how to write an app in Go that uses MongoDB on MongoLab and Heroku.
Using Code Examples
All of the code in this book can be used pretty much anywhere and anyhow you please.
How to Contact Me
I can be reached via e-mail at golangchallenge@gmail.com. Please contact me if you have any questions, comments, kudos or criticism on the eBook. Constructive criticism is definitely appreciated; I want this eBook to get better through your feedback.
Thanks
Thanks for downloading and checking out this eBook. As part of the lean publishing philosophy, you’ll be able to interact with me as the eBook is completed. I’ll be able to change things, reorganize parts, and generally make a better eBook. I hope you enjoy.
Go and MongoDB on MongoLab and Heroku
What’s NoSQL?
In the words of Karl Seguin:
NoSQL is a broad term that means different things to different people. Personally, I use it very broadly to mean a system that plays a part in the storage of data. Put another way, NoSQL (again, for me), is the belief that your persistence layer isn’t necessarily the responsibility of a single system. Where relational database vendors have historically tried to position their software as a one-size-fits-all solution, NoSQL leans towards smaller units of responsibility where the best tool for a given job can be leveraged. So, your NoSQL stack might still leverage a relational database, say MySQL, but it’ll also contain Redis as a persistence lookup for specific parts of the system as well as Hadoop for your intensive data processing. Put simply, NoSQL is about being open and aware of alternative, existing and additional patterns and tools for managing your data.
You might be wondering where MongoDB fits into all of this. As a document-oriented database, Mongo is a more generalized NoSQL solution. It should be viewed as an alternative to relational databases. Like relational databases, it too can benefit from being paired with some of the more specialized NoSQL solutions.
Also, most web apps will use either a relational or document-oriented database. A number of Platform-as-a-Service (PaaS) providers allow you to use Go applications on their clouds but, as of this writing, only Heroku has a paid MongoDB add-on or we could use Heroku and access our MongoDB database on MongoLab (later on, we shall see how).
What’s MongoDB?
MongoDB is a high-performance, open source, schema-free, document-oriented database written in C++.
MongoDB Core Concepts
- MongoDB has the same concept of a “database” with which you are likely already familiar (or a schema for you Oracle folks). Within a MongoDB instance you can have zero or more databases, each acting as high-level containers for everything else.
- A database can have zero or more “collections”. A collection shares enough in common with a traditional “table” that you can safely think of the two as the same thing.
- Collections are made up of zero or more “documents”. Again, a document can safely be thought of as a “row”.
- A document is made up of one or more “fields”, which you can probably guess are a lot like “columns”.
- “Indexes” in MongoDB function much like their RDBMS counterparts.
- “Cursors” are different than the other five concepts but they are important enough. The important thing to understand about cursors is that when you ask MongoDB for data, it returns a cursor, which we can do things to, such as counting or skipping ahead, without actually pulling down data.
To recap, MongoDB is made up of databases which contain collections. A collection is made up of documents. Each document is made up of fields. Collections can be indexed, which improves lookup and sorting performance. Finally, when we get data from MongoDB we do so through a cursor whose actual execution is delayed until necessary.
Note: The core difference between relational databases and document-oriented databases comes from the fact that relational databases define columns at the table level whereas a document-oriented database defines its fields at the document level.
MongoLab - The Fully-managed MongoDB-as-a-Service
MongoLab is a platform for MongoDB hosting on the web.
Sign Up
Sign up for a free account. When you fill up the online form, remember what you enter for default username and password - we will need this information later on. Next, click on “Plans & Features” and then select the free Sandbox plan.
Create a database
Next, create a free database. I have created a database named godata
in my account. To connect using a driver via the standard URI:
1
mongodb://<dbuser>:<dbpassword>@ds031271.mongolab.com:31271/godata
Database User
A database user is required to connect to the above database. Click here to create a new one. I entered “IndianGuru” for a Database username (you can enter whatever you want) and the requisite password.
mgo
Of all the Go drivers available for MongoDB, mgo is the most advanced and well-maintained.
To install mgo
, in a command window type:
1
go get gopkg.in/mgo.v2
JSON Recap
JSON stands for JavaScript Object Notation.
JSON is syntax for storing and exchanging text information, much like XML. JSON is smaller than XML, and faster and easier to parse. JSON is language independent. Here’s an example:
1
{
2
"employees"
:
[
3
{
"firstName"
:
"John"
,
"lastName"
:
"Doe"
},
4
{
"firstName"
:
"Anna"
,
"lastName"
:
"Smith"
},
5
{
"firstName"
:
"Peter"
,
"lastName"
:
"Jones"
}
6
]
7
}
The employee object is an array of 3 employee records (objects).
JSON data is written as name/value pairs. A name/value pair consists of a field name (in double quotes), followed by a colon and followed by a value:
1
"firstName" : "Satish"
JSON values can be:
- A number (integer or floating point)
- A string (in double quotes)
- A Boolean (true or false)
- An array (in square brackets)
- An object (in curly brackets)
- null
JSON objects are written inside curly brackets, Objects can contain multiple name/values pairs:
1
{ "firstName":"Satish" , "lastName":"Talim" }
package mgo
Usage: import "gopkg.in/mgo.v2"
The mgo project (pronounced as “mango”) is a rich MongoDB driver for the Go language.
Usage of the driver revolves around the concept of sessions. To get started, obtain a session using the Dial function:
1
session
,
err
:=
mgo
.
Dial
(
url
)
This will establish one or more connections with the cluster of servers defined by the url parameter. From then on, the cluster may be queried and documents retrieved with statements such as:
1
c
:=
session
.
DB
(
database
).
C
(
collection
)
2
err
:=
c
.
Find
(
query
).
One
(
&
result
)
Once the session is not useful anymore, Close
must be called to release the resources appropriately.
Function Close
func (s *Session) Close()
Close terminates the session. It’s a runtime error to use a session after it has been closed.
Function SetSafe
func (s *Session) SetSafe(safe *Safe)
SetSafe changes the session safety mode. If the safe parameter is nil
, the session is put in unsafe mode, and writes become fire-and-forget, without error checking. The unsafe mode is faster since operations won’t hold on waiting for a confirmation.
The following statement will make the session check for errors, without imposing further constraints:
1
session
.
SetSafe
(
&
mgo
.
Safe
{})
Function Safe
func (s *Session) Safe() (safe *Safe)
Safe returns the current safety mode for the session.
Function DB
func (s *Session) DB(name string) *Database
DB returns a value representing the named database (in our case godata
). If name is empty, the database name provided in the dialed URL is used instead. If that is also empty, “test” is used as a fallback.
Function C
func (db *Database) C(name string) *Collection
C returns a value representing the named collection (in our case user
).
Function Insert
func (c *Collection) Insert(docs ...interface{}) error
Insert inserts one or more documents in the respective collection.
Function Find
func (c *Collection) Find(query interface{}) *Query
Find prepares a query using the provided document. The document may be a map or a struct value capable of being marshalled with bson
. The map may be a generic one using interface{} for its key and/or values, such as bson.M
, or it may be a properly typed map. Providing nil
as the document is equivalent to providing an empty document such as bson.M{}
.
Further details of the query may be tweaked using the resulting Query value, and then executed to retrieve results using methods such as One
, For
, Iter
, or Tail
.
Function One
func (q *Query) One(result interface{}) (err error)
One executes the query and unmarshals the first obtained document into the result argument. The result must be a struct or map value capable of being unmarshalled into by gobson. This function blocks until either a result is available or an error happens.
package bson
Usage: import "labix.org/v2/mgo/bson"
Package bson is an implementation of the BSON specification for Go.
type M
type M map[string]interface{}
M is a convenient alias for a map[string]interface{}
map, useful for dealing with BSON in a native way. For instance:
bson.M{"a": 1, "b": true}
Program mongohqconnect.go
The program mongohqconnect.go
connects to our database godata
hosted on MongoLab and creates a collection user
.
Program mongohqconnect.go
1
package
main
2
3
import
(
4
"fmt"
5
"gopkg.in/mgo.v2"
6
"gopkg.in/mgo.v2/bson"
7
"log"
8
"os"
9
)
10
11
type
Person
struct
{
12
Name
string
13
Email
string
14
}
15
16
func
main
()
{
17
// Do the following:
18
// In a command window:
19
// set MONGOLAB_URL=mongodb://IndianGuru:password@ds031271.mongolab.com:31271/goda\
20
ta
21
// IndianGuru is my username, replace the same with yours. Type in your password.
22
uri
:=
os
.
Getenv
(
"MONGOLAB_URL"
)
23
if
uri
==
""
{
24
fmt
.
Println
(
"no connection string provided"
)
25
os
.
Exit
(
1
)
26
}
27
28
sess
,
err
:=
mgo
.
Dial
(
uri
)
29
if
err
!=
nil
{
30
fmt
.
Printf
(
"Can't connect to mongo, go error %v\n"
,
err
)
31
os
.
Exit
(
1
)
32
}
33
defer
sess
.
Close
()
34
35
sess
.
SetSafe
(
&
mgo
.
Safe
{})
36
37
collection
:=
sess
.
DB
(
"godata"
).
C
(
"user"
)
38
39
err
=
collection
.
Insert
(
&
Person
{
"Stefan Klaste"
,
"klaste@posteo.de"
},
40
&
Person
{
"Nishant Modak"
,
"modak.nishant@gmail.com"
},
41
&
Person
{
"Prathamesh Sonpatki"
,
"csonpatki@gmail.com"
},
42
&
Person
{
"murtuza kutub"
,
"murtuzafirst@gmail.com"
},
43
&
Person
{
"aniket joshi"
,
"joshianiket22@gmail.com"
},
44
&
Person
{
"Michael de Silva"
,
"michael@mwdesilva.com"
},
45
&
Person
{
"Alejandro Cespedes Vicente"
,
"cesal_vizar@hotmail.com"
})
46
if
err
!=
nil
{
47
log
.
Fatal
(
"Problem inserting data: "
,
err
)
48
return
49
}
50
51
result
:=
Person
{}
52
err
=
collection
.
Find
(
bson
.
M
{
"name"
:
"Prathamesh Sonpatki"
}).
One
(
&
result
)
53
if
err
!=
nil
{
54
log
.
Fatal
(
"Error finding record: "
,
err
)
55
return
56
}
57
58
fmt
.
Println
(
"Email Id:"
,
result
.
Email
)
59
}
Deploying gomongohq.go on Heroku
In a previous free eBook, we have seen how to deploy web apps to Heroku.
The program gomongohq.go
will reside on Heroku and will connect to our database godata
hosted on MongoLab. The app will fetch information (the email id of say user Stefan Klaste) from the collection user
.
Program gomongohq.go
1
package
main
2
3
import
(
4
"fmt"
5
"html/template"
6
"labix.org/v2/mgo"
7
"labix.org/v2/mgo/bson"
8
"log"
9
"net/http"
10
"os"
11
)
12
13
type
Person
struct
{
14
Name
string
15
Email
string
16
}
17
18
func
main
()
{
19
http
.
HandleFunc
(
"/"
,
root
)
20
http
.
HandleFunc
(
"/display"
,
display
)
21
fmt
.
Println
(
"listening..."
)
22
err
:=
http
.
ListenAndServe
(
GetPort
(),
nil
)
23
if
err
!=
nil
{
24
log
.
Fatal
(
"ListenAndServe: "
,
err
)
25
return
26
}
27
}
28
29
func
root
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
30
fmt
.
Fprint
(
w
,
rootForm
)
31
}
32
33
const
rootForm
=
`
34
<!DOCTYPE html>
35
<html>
36
<head>
37
<meta charset="utf-8">
38
<title>Your details</title>
39
<link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.4.2/pure-min.css">
40
</head>
41
<body style="margin: 20px;">
42
<h2>A Fun Go App on Heroku to access MongoDB on MongoLab</h2>
43
<p>This simple app will fetch the email id of a person, if it's already there in t\
44
he MongoDB database.</p>
45
<p>Please enter a name (example: Stefan Klaste)</p>
46
<form action="/display" method="post" accept-charset="utf-8" class="pure-form">
47
<input type="text" name="name" placeholder="name" />
48
<input type="submit" value=".. and query database!" class="pure-button pure-butt\
49
on-primary"/>
50
</form>
51
<div>
52
<p><b>© 2015 Go Challenge. All rights reserved.</b></p>
53
</div>
54
</body>
55
</html>
56
`
57
58
var
displayTemplate
=
template
.
Must
(
template
.
New
(
"display"
).
Parse
(
displayTemplateHTML
))
59
60
func
display
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
61
// In the open command window set the following for Heroku:
62
// heroku config:set MONGOHQ_URL=mongodb://IndianGuru:password@troup.mongohq.com:1\
63
00
80
/
godata
64
uri
:=
os
.
Getenv
(
"MONGOHQ_URL"
)
65
if
uri
==
""
{
66
fmt
.
Println
(
"no connection string provided"
)
67
os
.
Exit
(
1
)
68
}
69
70
sess
,
err
:=
mgo
.
Dial
(
uri
)
71
if
err
!=
nil
{
72
fmt
.
Printf
(
"Can't connect to mongo, go error %v\n"
,
err
)
73
os
.
Exit
(
1
)
74
}
75
defer
sess
.
Close
()
76
77
sess
.
SetSafe
(
&
mgo
.
Safe
{})
78
79
collection
:=
sess
.
DB
(
"godata"
).
C
(
"user"
)
80
81
result
:=
Person
{}
82
83
collection
.
Find
(
bson
.
M
{
"name"
:
r
.
FormValue
(
"name"
)}).
One
(
&
result
)
84
85
if
result
.
Email
!=
""
{
86
errn
:=
displayTemplate
.
Execute
(
w
,
"The email id you wanted is: "
+
result
\
87
.
Email
)
88
if
errn
!=
nil
{
89
http
.
Error
(
w
,
errn
.
Error
(),
http
.
StatusInternalServerError
)
90
}
91
}
else
{
92
displayTemplate
.
Execute
(
w
,
"Sorry... The email id you wanted does not exis\
93
t."
)
94
}
95
}
96
97
const
displayTemplateHTML
=
`
98
<!DOCTYPE html>
99
<html>
100
<head>
101
<meta charset="utf-8">
102
<title>Results</title>
103
<link rel="stylesheet" href="http://yui.yahooapis.com/pure/0.4.2/pure-min.css">
104
</head>
105
<body>
106
<h2>A Fun Go App on Heroku to access MongoDB on MongoLab</h2>
107
<p><b>{{html .}}</b></p>
108
<p><a href="/">Start again!</a></p>
109
<div>
110
<p><b>© 2015 Go Challenge. All rights reserved.</b></p>
111
</div>
112
</body>
113
</html>
114
`
115
116
// Get the Port from the environment so we can run on Heroku
117
func
GetPort
()
string
{
118
var
port
=
os
.
Getenv
(
"PORT"
)
119
// Set a default port if there is nothing in the environment
120
if
port
==
""
{
121
port
=
"4747"
122
fmt
.
Println
(
"INFO: No PORT environment variable detected, defaulting to "
+
port
)
123
}
124
return
":"
+
port
125
}
The program by now should be self explanatory.
- The
rootForm
uses Pure a set of small, responsive CSS modules that you can use in every web project. - The function
display
uses thehtml/template
package and themgo
driver to access the database on MongoLab. If the name is found in the database the function throws a page to the user with the email id for that name.
You can visit this app here.