Hello everyone!

We have been investing plenty of personal time and energy for many years to share our knowledge with you all. However, we now need your help to keep this blog running. All you have to do is just click one of the adverts on the site, otherwise it will sadly be taken down due to hosting etc. costs. Thank you.

In this example we are going to keep one document in another document. This is called "embedded document model" in MongoDB. Our example uses "tokens" collection. Each token document will contain zero or one "permission" document in it.


Database content


Preparation


db.createCollection("tokens")
db.tokens.createIndex({"uuid":1},{unique:true,name:"UQ_uuid"})

Tokens data


[
{
"_id": {
"$oid": "6050b16571b3921d9c825d66"
},
"uuid": "2f249152-6f8f-4873-81ef-ae6d7de0b582",
"type": "access token",
"permission": {
"uuid": "33e5140b-80ac-42ff-b6aa-59e1f855847a",
"type": "accounts",
"actions": [
"read",
"write"
]
}
},
{
"_id": {
"$oid": "6050b16571b3921d9c825d67"
},
"uuid": "e2513685-bfed-4962-a897-230eb149fbac",
"type": "refresh token",
"permission": {

}
}
]

Storage


Models


package storage

import "context"

type TokenStorer interface {
Insert(ctx context.Context, token Token) error
Find(ctx context.Context, uuid string) (Token, error)
}

type Token struct {
ID string `bson:"_id,omitempty"`
UUID string `bson:"uuid"`
Type string `bson:"type"`
Permission Permission `bson:"permission"`
}

type Permission struct {
UUID string `bson:"uuid,omitempty"`
Type string `bson:"type,omitempty"`
Actions []string `bson:"actions,omitempty"`
}

Storer


package mongodb

import (
"context"
"log"
"time"

"github.com/you/mongo/internal/pkg/domain"
"github.com/you/mongo/internal/pkg/storage"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
)

var _ storage.TokenStorer = TokenStorage{}

type TokenStorage struct {
Database *mongo.Database
Timeout time.Duration
}

func (t TokenStorage) Insert(ctx context.Context, token storage.Token) error {
ctx, cancel := context.WithTimeout(ctx, t.Timeout)
defer cancel()

if _, err := t.Database.Collection("tokens").InsertOne(ctx, token); err != nil {
log.Println(err)

if er, ok := err.(mongo.WriteException); ok && er.WriteErrors[0].Code == 11000 {
return domain.ErrConflict
}

return domain.ErrInternal
}

return nil
}

func (t TokenStorage) Find(ctx context.Context, uuid string) (storage.Token, error) {
ctx, cancel := context.WithTimeout(ctx, t.Timeout)
defer cancel()

var tok storage.Token

qry := bson.M{"uuid": uuid}

err := t.Database.Collection("tokens").FindOne(ctx, qry).Decode(&tok)
if err != nil {
log.Println(err)

if err == mongo.ErrNoDocuments {
return storage.Token{}, domain.ErrNotFound
}

return storage.Token{}, domain.ErrInternal
}

return tok, nil
}

Test


Insert


storer := TokenStorage{Database: database, Timeout: time.Second * 5}

if err := storer.Insert(context.Background(), Token{
UUID: uuid.New().String(),
Type: "access token",
Permission: Permission{
UUID: uuid.New().String(),
Type: "accounts",
Actions: []string{"read", "write"},
},
}); err != nil {
log.Fatalln(err)
}

Find


storer := TokenStorage{Database: database, Timeout: time.Second * 5}

token, err := storer.Find(context.Background(), "2f249152-6f8f-4873-81ef-ae6d7de0b582")
if err != nil {
log.Fatalln(err)
}

data, _ := json.MarshalIndent(token, "", " ")
log.Println(string(data))

{
"ID": "6050b16571b3921d9c825d66",
"UUID": "2f249152-6f8f-4873-81ef-ae6d7de0b582",
"Type": "access token",
"Permission": {
"UUID": "33e5140b-80ac-42ff-b6aa-59e1f855847a",
"Type": "accounts",
"Actions": [
"read",
"write"
]
}
}