Skip to main content

Datababe - V2

Banco de Dados não relacional - MongoDB

Devido à natureza do projeto, no qual vários sensores produzem uma quantidade massiva de dados não estruturados, a utilização de um banco não relacional faz sentido.

Um banco de dados não relacional armazena dados de uma forma não tabular, tendendo a ser mais flexível que bancos orientados à SQL com estruturas relacionais. No caso do MongoDB, os dados são armazenados em documentos. Essa capacidade de digerir e organizar vários tipos de informações lado a lado torna os bancos de dados não relacionais muito mais flexíveis do que os bancos de dados relacionais.1

Estrutura SQL vs NoSQL2

Escolhemos o MongoDB por ser uma ferramenta simples e versátil. Além disso, possui uma infraestrutura em cloud (MongoDB Atlas) que facilita e lida com toda a a complexidade do deploy, possibilitando um maior foco no desenvolvimento de features.

Criação e setup do MongoDB Atlas

Para configurar uma base de dados no mongoDB Atlas, sugerimos o vídeo explicativo a seguir:

Organização dos dados

Nosso banco de dados está estruturado em quatro coleções principais:

  • alerts: Armazena os alertas relacionados à desastres em determinadas áreas:

        _id: objectId('65f444d56bc7ac0d06e4210f')
    latitude: -23.5872976
    longitude: -46.6825611
    type: "Enchente"
  • gases: Armazena as leituras dos sensores de gases no formato abaixo:

        _id: objectId('65f1a7ec4d7ef06d11aa2eb0')
    unit: "ppm"
    time: "2024-03-13T10:14:56.471628161-03:00"
    methane: 5386.66
    carbon_monoxide:861.17
    nitrogen_dioxide: 3.42
    ethanol: 137.45
    ammonia: 375.11
    sensor: "MiCS-6814"
    id: 3
    propane: 1261.57
    iso_butane: 9664.24
    hydrogen: 884.05
  • radiation: Armazena as leituras dos sensores de gases no formato abaixo:

        _id: objectId('65f1a7eb4d7ef06d11aa2eaf')
    id: 4
    time: "2024-03-13T10:14:56.730273086-03:00"
    radiation: 794.12
    sensor: "RXWLIB900"
    unit: "W/m2"
  • sensors: Armazena os sensores cadastrados no formato abaixo:

        _id: objectId('65f444176bc7ac0d06e4210b')
    latitude: -23.5762
    longitude: -46.72482
    name: "Station 1"

Fluxo

  1. Conexão: A conexão com o banco se dá pela autenticação utilizando as credenciais geradas ao criar um usuário no mongoDB. Note que, na função criada, existem variáveis de ambiente para realizar a autenticação correta do usuário. São elas:
  • MONGO_USER: Nome do usuário configurado no banco de dados.
  • MONGO_PASSWORD: Senha configurada no banco de dados. Na sessão "Instruções para execução do projeto", siga os passos apresentados para configurar corretamente o arquivo que contém as variáveis de ambiente.
func ConnectToMongo() *mongo.Client{
// Carregar variáveis de ambiente do arquivo .env
err := godotenv.Load("../../config/.env")

if err != nil {
log.Fatal("Erro ao carregar o arquivo .env")
}

// Recuperar usuário e senha do arquivo .env
mongoUser := os.Getenv("MONGO_USER")
mongoPassword := os.Getenv("MONGO_PASSWORD")

// Use the SetServerAPIOptions() method to set the version of the Stable API on the client
serverAPI := options.ServerAPI(options.ServerAPIVersion1)
opts := options.Client().ApplyURI(fmt.Sprintf("mongodb+srv://%s:%s@sensors.zyzjabc.mongodb.net/?retryWrites=true&w=majority&appName=sensors", mongoUser, mongoPassword)).SetServerAPIOptions(serverAPI)

// Create a new client and connect to the server
client, err := mongo.Connect(context.TODO(), opts)
if err != nil {
panic(err)
}

return client
}
  1. Inserção: Como ilustrado na nova arquitetura da solução, os dados são produzidos pelos sensores (para testes, utilizamos o simulador), para então serem manuseados pelo serviço de fila Kafka. Este aciona uma função que trata os dados e então os insere no banco de dados, dependendo da coleção.

func InsertIntoMongo(client *mongo.Client, data map[string]interface{}) {
db := client.Database("SmarTopia")

var coll *mongo.Collection
// fmt.Println(data["payload"])

payloadData := data["payload"].(map[string]interface{})

newData := make(map[string]interface{})

if payloadData["gases-values"] != nil {

gasesValues := payloadData["gases-values"].(map[string]interface{})

newData["id"] = data["packet-id"]

newData["time"] = payloadData["current_time"]

for key, value := range gasesValues["gases-values"].(map[string]interface{}) {
newData[key] = value
}

newData["sensor"] = gasesValues["sensor"]
newData["unit"] = gasesValues["unit"]

coll = db.Collection("gases")

} else {

radiationValues := payloadData["radiation-values"].(map[string]interface{})

newData["id"] = data["packet-id"]

newData["time"] = payloadData["current_time"]

for key, value := range radiationValues["radiation-values"].(map[string]interface{}) {
newData[key] = value
}

newData["sensor"] = radiationValues["sensor"]
newData["unit"] = radiationValues["unit"]

coll = db.Collection("radiation")
}

bsonData, err := bson.Marshal(newData)

result, err := coll.InsertOne(context.TODO(), bsonData)

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

fmt.Printf("Inserted document with _id: %v\n", result.InsertedID)
}

  1. Consumo: Após a inserção dos dados, estes podem ser acessados pela plataforma de BI utilizada (nesse caso, o metabase). A conexão entre estes serviços pode ser feita a partir da string de conexão gerada automaticamente pelo mongoDB Atlas. Nas configurações de administrador, ao adicionar uma nova base de dados, selecione MongoDB e utilize a opção Paste a connection string.

Referências

Footnotes

  1. What Is a Non-Relational Database?

  2. Relational and non relational databases