16/03/2021 - GO, MONGODB
In this example we are going to keep multiple documents in another document. This is called "embedded document model" in MongoDB. Our example uses "tokens" collection. Each token document will contain zero or many "permission" documents in it.
db.createCollection("tokens")
db.tokens.createIndex({"uuid":1},{unique:true,name:"UQ_uuid"})
[
{
"_id": {
"$oid": "6050986944799a96c0f7b1f4"
},
"uuid": "7d5e2a9f-b6b9-4356-af3c-be772b3cf821",
"type": "access token",
"permissions": [
{
"uuid": "f2222ef5-7f9b-46fa-a7e6-c88b4126e9fe",
"type": "accounts",
"actions": [
"read",
"write"
]
},
{
"uuid": "2cffcc36-0efc-4d09-bb0a-a36f2b6433be",
"type": "balances",
"actions": [
"read"
]
}
]
},
{
"_id": {
"$oid": "605098e921410aa61d2be92b"
},
"uuid": "38a934d7-73cf-4874-8af1-874d4c03335f",
"type": "refresh token",
"permissions": null
}
]
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"`
Permissions []Permission `bson:"permissions"`
}
type Permission struct {
UUID string `bson:"uuid"`
Type string `bson:"type"`
Actions []string `bson:"actions"`
}
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
}
storer := TokenStorage{Database: database, Timeout: time.Second * 5}
if err := storer.Insert(context.Background(), Token{
UUID: uuid.New().String(),
Type: "access token",
Permissions: []Permission{
{
UUID: uuid.New().String(),
Type: "accounts",
Actions: []string{"read", "write"},
},
{
UUID: uuid.New().String(),
Type: "balances",
Actions: []string{"read"},
},
},
}); err != nil {
log.Fatalln(err)
}
storer := TokenStorage{Database: database, Timeout: time.Second * 5}
token, err := storer.Find(context.Background(), "7d5e2a9f-b6b9-4356-af3c-be772b3cf821")
if err != nil {
log.Fatalln(err)
}
data, _ := json.MarshalIndent(token, "", " ")
log.Println(string(data))
{
"ID": "6050986944799a96c0f7b1f4",
"UUID": "7d5e2a9f-b6b9-4356-af3c-be772b3cf821",
"Type": "access token",
"Permissions": [
{
"UUID": "f2222ef5-7f9b-46fa-a7e6-c88b4126e9fe",
"Type": "accounts",
"Actions": [
"read",
"write"
]
},
{
"UUID": "2cffcc36-0efc-4d09-bb0a-a36f2b6433be",
"Type": "balances",
"Actions": [
"read"
]
}
]
}