Golang mgo retrieve nested structured arrays

24 May, 2016

Small tutorial on how to retrieve nested arrays/lists from a 'mgo.Find()' result.

Lets say we have a collection called 'users' that contains, obviously, the data of our users, and in each user document there is a nested array that keeps track of the login attempts, the document of user 'John' looks like this:

[
	{
		"_id": "57437f271e02f4bae78788af",
		"firstname": "John",
		"lastname": "doe",
		"email": "johndoe@gmail.com",
		"password": "somehashedpassword",
		"loginattempts": [
			{
				"timestamp": 1464042139,
				"ip": "192.168.0.2"
			},
			{
				"timestamp": 1464040000,
				"ip": "192.168.0.5"
			},
			{
				"timestamp": 1464000139,
				"ip": "127.0.0.1"
			}
		]
	}
]

Find() John's data

In order to store the the data that results from the Find().One() function we need to define some structs. One struct that contains the data and the other one that contains the login attempts.

type UserData struct {
	ID            bson.ObjectId       `bson:"_id,omitempty"`
	Firstname     string              "firstname"
	Lastname      string              "lastname"
	Email         string              "email"
	Password      string              "password"
	LoginAttempts []UserLoginAttempts "loginattemtps"
}
type UserLoginAttempts struct {
	Timestamp time.Time "timestamp"
	IP string "ip"
}

Notice that the 'LoginAttempts' has the type array of 'UserLoginAttempts', and that the fields need to be exported (public) with their first letter being a capital letter.

Simply open a new struct, find John and return the results into the new struct.

result := UserData{}
collection.Find("email" : "johndoe@gmail.com").One(&result)

The result variable will now contain all the information. If you wish to iterate over the login attempts just write this:

for _, attempt := range result.LoginAttempts {
	fmt.Println(attempt.Timestamp, attempt.IP)
}