One place for hosting & domains

      utiliser

      Comment utiliser Go avec MongoDB en utilisant le pilote MongoDB Go


      L’auteur a choisi la Free Software Foundation pour recevoir un don dans le cadre du programme Write for DOnations.

      Introduction

      Après s’être appuyée sur des solutions développées par la communauté pendant de nombreuses années, MongoDB a annoncé qu’elle travaillait sur un pilote officiel pour Go. En mars 2019, ce nouveau pilote a atteint un statut prêt pour la production avec la sortie de la v1.0.0 et a été continuellement mis à jour depuis lors.

      Comme les autres pilotes officiels de MongoDB, le pilote Go est idiomatique au langage de programmation Go et fournit un moyen facile d’utiliser MongoDB comme solution de base de données pour un programme Go. Il est entièrement intégré à l’API MongoDB, et expose toutes les fonctions d’interrogation, d’indexation et d’agrégation de l’API, ainsi que d’autres fonctions avancées. Contrairement aux bibliothèques tierces, il sera entièrement pris en charge par les ingénieurs de MongoDB afin que vous puissiez être assuré de son développement et de sa maintenance continus.

      Dans ce tutoriel, vous commencerez à utiliser le pilote officiel Go de MongoDB. Vous allez installer le pilote, vous connecter à une base de données MongoDB et effectuer plusieurs opérations CRUD. Dans le processus, vous créerez un programme de gestion des tâches pour gérer les tâches par la ligne de commande.

      Conditions préalables

      Pour ce tutoriel, vous aurez besoin des éléments suivants :

      Si vous utilisez Go v1.11 ou 1.12, assurez-vous que Go Modules est activé en définissant la variable d’environnement GO111MODULE sur on comme indiqué ci-dessous :

      Pour plus d’informations sur l’implémentation des variables d’environnement, lisez ce tutoriel sur Comment lire et configurer les variables d’environnements et les variables shell.

      Les commandes et le code présentés dans ce guide ont été testés avec Go v1.14.1 et MongoDB v3.6.3.

      Étape 1 — Installation du pilote Go de MongoDB

      Dans cette étape, vous allez installer le package Go Driver pour MongoDB et l’importer dans votre projet. Vous vous connecterez également à votre base de données MongoDB et vérifierez l’état de la connexion.

      Allez-y et créez un nouveau répertoire pour ce tutoriel dans votre système de fichiers :

      Une fois que votre répertoire de projet est configuré, modifiez-le avec la commande suivante :

      Ensuite, initialisez le projet Go avec un fichier go.mod. Ce fichier définit les exigences du projet et verrouille les dépendances à leurs versions correctes :

      Si votre répertoire de projet se trouve en dehors du $GOPATH, vous devez spécifier l’itinéraire d’importation de votre module comme suit :

      • go mod init github.com/<your_username>/tasker

      À ce stade, votre fichier go.mod ressemblera à ceci :

      go.mod

      module github.com/<your_username>/tasker
      
      go 1.14
      

      Ajoutez le pilote MongoDB Go comme dépendance pour votre projet en utilisant la commande suivante :

      • go get go.mongodb.org/mongo-driver

      Vous verrez une sortie comme celle-ci :

      Output

      go: downloading go.mongodb.org/mongo-driver v1.3.2 go: go.mongodb.org/mongo-driver upgrade => v1.3.2

      À ce stade, votre fichier go.mod ressemblera à ceci :

      go.mod

      module github.com/<your_username>/tasker
      
      go 1.14
      
      require go.mongodb.org/mongo-driver v1.3.1 // indirect
      

      Ensuite, créez un fichier main.go dans votre projet root et ouvrez-le dans votre éditeur de texte :

      Pour commencer à utiliser le pilote, importez les packages suivants dans votre fichier main.go :

      main.go

      package main
      
      import (
          "context"
          "log"
      
          "go.mongodb.org/mongo-driver/mongo"
          "go.mongodb.org/mongo-driver/mongo/options"
      )
      

      Vous ajoutez ici les packages mongo et options, que le pilote Go de MongoDB fournit.

      Ensuite, après vos importations, créez un nouveau client MongoDB et connectez-vous à votre serveur MongoDB en cours d’exécution :

      main.go

      . . .
      var collection *mongo.Collection
      var ctx = context.TODO()
      
      func init() {
          clientOptions := options.Client().ApplyURI("mongodb://localhost:27017/")
          client, err := mongo.Connect(ctx, clientOptions)
          if err != nil {
              log.Fatal(err)
          }
      }
      

      mongo.Connect() accepte un Context et un objet options.ClientOptions, qui est utilisé pour définir la chaîne de connexion et d’autres paramètres du pilote. Vous pouvez consulter la documentation du package options pour voir quelles sont les options de configuration disponibles.

      Context est comme un délai ou une date limite qui indique quand une opération doit s’arrêter et reprendre. Il contribue à prévenir la dégradation des performances des systèmes de production lorsque certaines opérations sont lentes. Dans ce code, vous passez context.TODO() pour indiquer que vous n’êtes pas sûr du contexte à utiliser pour le moment, mais que vous prévoyez d’en ajouter un à l’avenir.

      Ensuite, assurons-nous que votre serveur MongoDB a été trouvé et connecté avec succès en utilisant la méthode Ping. Ajoutez le code suivant à l’intérieur de la fonction init :

      main.go

      . . .
          log.Fatal(err)
        }
      
        err = client.Ping(ctx, nil)
        if err != nil {
          log.Fatal(err)
        }
      }
      

      S’il y a des erreurs lors de la connexion à la base de données, le programme devrait planter pendant que vous essayez de résoudre le problème, car il ne sert à rien de laisser le programme fonctionner sans une connexion active à la base de données.

      Ajoutez le code suivant pour créer une base de données :

      main.go

      . . .
        err = client.Ping(ctx, nil)
        if err != nil {
          log.Fatal(err)
        }
      
        collection = client.Database("tasker").Collection("tasks")
      }
      

      Vous créez une base de données tasker et une collection task pour stocker les tâches que vous allez créer. Vous configurez également la collection comme une variable au niveau du package afin de pouvoir réutiliser la connexion à la base de données dans l’ensemble du package.

      Enregistrez et quittez le fichier.

      Le main.go complet à ce stade est le suivant :

      main.go

      package main
      
      import (
          "context"
          "log"
      
          "go.mongodb.org/mongo-driver/mongo"
          "go.mongodb.org/mongo-driver/mongo/options"
      )
      
      var collection *mongo.Collection
      var ctx = context.TODO()
      
      func init() {
          clientOptions := options.Client().ApplyURI("mongodb://localhost:27017/")
          client, err := mongo.Connect(ctx, clientOptions)
          if err != nil {
              log.Fatal(err)
          }
      
          err = client.Ping(ctx, nil)
          if err != nil {
              log.Fatal(err)
          }
      
          collection = client.Database("tasker").Collection("tasks")
      }
      

      Vous avez configuré votre programme pour vous connecter à votre serveur MongoDB en utilisant le pilote Go. Dans l’étape suivante, vous procéderez à la création de votre programme de gestion des tâches.

      Étape 2 — Création d’un programme CLI

      Au cours de cette étape, vous installerez le célèbre package cli pour vous aider à développer votre programme de gestion des tâches. Il offre une interface dont vous pouvez tirer parti pour créer rapidement des outils modernes en ligne de commande. Par exemple, ce package donne la possibilité de définir des sous-commandes destinées à votre programme, pour une expérience en ligne de commande plus proche de celle de git.

      Exécutez la commande suivante pour ajouter le package en tant que dépendance :

      • go get github.com/urfave/cli/v2

      Ensuite, ouvrez à nouveau votre fichier main.go :

      Ajoutez le code surligné suivant à votre fichier main.go :

      main.go

      package main
      
      import (
          "context"
          "log"
          "os"
      
          "github.com/urfave/cli/v2"
          "go.mongodb.org/mongo-driver/mongo"
          "go.mongodb.org/mongo-driver/mongo/options"
      )
      . . .
      

      Vous importez le package cli comme mentionné. Vous importez également le package os, que vous utiliserez pour passer les arguments de la ligne de commande à votre programme :

      Ajoutez le code suivant après votre fonction init pour créer votre programme CLI et faire en sorte que votre code se compile :

      main.go

      . . .
      func main() {
          app := &cli.App{
              Name:     "tasker",
              Usage:    "A simple CLI program to manage your tasks",
              Commands: []*cli.Command{},
          }
      
          err := app.Run(os.Args)
          if err != nil {
              log.Fatal(err)
          }
      }
      

      Ce snippet crée un programme CLI appelé tasker et ajoute une courte description d’utilisation qui sera imprimée lorsque vous exécuterez le programme. La slice Commands est le lieu où vous ajouterez des commandes pour votre programme. La commande Run décrit la slice arguments à la commande appropriée.

      Enregistrez et fermez votre fichier

      Voici la commande dont vous avez besoin pour construire et exécuter le programme :

      Vous verrez la sortie suivante :

      Output

      NAME: tasker - A simple CLI program to manage your tasks USAGE: main [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help (default: false)

      Le programme s’exécute et affiche un texte d’aide, qui est pratique pour apprendre ce que le programme peut faire, et comment l’utiliser.

      Dans les prochaines étapes, vous améliorerez l’utilité de votre programme en ajoutant des sous-commandes pour vous aider à gérer vos tâches dans MongoDB.

      Étape 3 — Création d’une tâche

      Au cours de cette étape, vous ajouterez une sous-commande à votre programme CLI en utilisant le package cli. À la fin de cette section, vous pourrez ajouter une nouvelle tâche à votre base de données MongoDB en utilisant une nouvelle commande add dans votre programme CLI.

      Commencez par ouvrir votre fichier main.go :

      Ensuite, importez les packages go.mongodb.org/mongo-driver/bson/primitive, time et errors :

      main.go

      package main
      
      import (
          "context"
          "errors"
          "log"
          "os"
          "time"
      
          "github.com/urfave/cli/v2"
          "go.mongodb.org/mongo-driver/bson/primitive"
          "go.mongodb.org/mongo-driver/mongo"
          "go.mongodb.org/mongo-driver/mongo/options"
      )
      . . .
      

      Puis, créez une nouvelle structure pour représenter une seule tâche dans la base de données et insérez-la immédiatement avant la fonction main :

      main.go

      . . .
      type Task struct {
          ID        primitive.ObjectID `bson:"_id"`
          CreatedAt time.Time          `bson:"created_at"`
          UpdatedAt time.Time          `bson:"updated_at"`
          Text      string             `bson:"text"`
          Completed bool               `bson:"completed"`
      }
      . . .
      

      Vous utilisez le package primitive pour définir le type de l’ID de chaque tâche, puisque MongoDB utilise les ObjectID par défaut pour le champ _id. L’un des autres comportements par défaut de MongoDB est que le nom de champ en minuscules est utilisé comme clé pour chaque champ exporté lors de sa sérialisation, mais cela peut être modifié en utilisant les balises struct bson.

      Ensuite, créez une fonction qui reçoit une instance de Task et l’enregistre dans la base de données. Ajoutez ce snippet à la fonction main :

      main.go

      . . .
      func createTask(task *Task) error {
          _, err := collection.InsertOne(ctx, task)
        return err
      }
      . . .
      

      La méthode collection.InsertOne() insère la tâche fournie dans la collection de la base de données et renvoie l’identifiant du document qui a été inséré. Étant donné que vous n’avez pas besoin de cet identifiant, vous le supprimez en l’attribuant à l’opérateur de soulignement.

      L’étape suivante consiste à ajouter une nouvelle commande à votre programme de gestion des tâches pour créer de nouvelles tâches. Appelons-la add :

      main.go

      . . .
      func main() {
          app := &cli.App{
              Name:  "tasker",
              Usage: "A simple CLI program to manage your tasks",
              Commands: []*cli.Command{
                  {
                      Name:    "add",
                      Aliases: []string{"a"},
                      Usage:   "add a task to the list",
                      Action: func(c *cli.Context) error {
                          str := c.Args().First()
                          if str == "" {
                              return errors.New("Cannot add an empty task")
                          }
      
                          task := &Task{
                              ID:        primitive.NewObjectID(),
                              CreatedAt: time.Now(),
                              UpdatedAt: time.Now(),
                              Text:      str,
                              Completed: false,
                          }
      
                          return createTask(task)
                      },
                  },
              },
          }
      
          err := app.Run(os.Args)
          if err != nil {
              log.Fatal(err)
          }
      }
      

      Toute nouvelle commande ajoutée à votre programme CLI est placée dans la slice Commands. Chacune d’entre elles se compose d’un nom, d’une description d’usage et d’une action. C’est le code qui sera exécuté lors de l’exécution de la commande.

      Dans ce code, vous collectez le premier argument à add et l’utilisez pour définir la propriété Text d’une nouvelle instance Task tout en attribuant les valeurs par défaut appropriées pour les autres propriétés. La nouvelle tâche est ensuite transmise à createTask, qui insère la tâche dans la base de données et renvoie nil si tout va bien, ce qui entraîne la sortie de la commande.

      Enregistrez et fermez votre fichier

      Testez ce processus en ajoutant quelques tâches à l’aide de la commande add. En cas de succès, vous ne verrez aucune erreur s’imprimer sur votre écran :

      • go run main.go add "Learn Go"
      • go run main.go add "Read a book"

      Maintenant que vous pouvez ajouter des tâches avec succès, mettons en place un moyen d’afficher toutes les tâches que vous avez ajoutées à la base de données.

      Étape 4 — Liste de toutes les tâches

      Le listage des documents d’une collection peut être effectué à l’aide de la méthode collection.Find(), qui attend un filtre ainsi qu’un pointeur vers une valeur en laquelle le résultat peut être décodé. Sa valeur de retour est un Curseur, qui fournit un flux de documents qui peuvent être répétés et décodés un à la fois. Le Curseur est ensuite fermé une fois qu’il a été épuisé.

      Ouvrez votre fichier main.go :

      Veillez à importer le package bson :

      main.go

      package main
      
      import (
          "context"
          "errors"
          "log"
          "os"
          "time"
      
          "github.com/urfave/cli/v2"
          "go.mongodb.org/mongo-driver/bson"
          "go.mongodb.org/mongo-driver/bson/primitive"
          "go.mongodb.org/mongo-driver/mongo"
          "go.mongodb.org/mongo-driver/mongo/options"
      )
      . . .
      

      Ensuite, créez les fonctions suivantes immédiatement après createTask :

      main.go

      . . .
      func getAll() ([]*Task, error) {
        // passing bson.D{{}} matches all documents in the collection
          filter := bson.D{{}}
          return filterTasks(filter)
      }
      
      func filterTasks(filter interface{}) ([]*Task, error) {
          // A slice of tasks for storing the decoded documents
          var tasks []*Task
      
          cur, err := collection.Find(ctx, filter)
          if err != nil {
              return tasks, err
          }
      
          for cur.Next(ctx) {
              var t Task
              err := cur.Decode(&t)
              if err != nil {
                  return tasks, err
              }
      
              tasks = append(tasks, &t)
          }
      
          if err := cur.Err(); err != nil {
              return tasks, err
          }
      
        // once exhausted, close the cursor
          cur.Close(ctx)
      
          if len(tasks) == 0 {
              return tasks, mongo.ErrNoDocuments
          }
      
          return tasks, nil
      }
      

      BSON (Binary-encoded JSON) est la façon dont les documents sont représentés dans une base de données MongoDB et le package bson est ce qui nous aide à travailler avec les objets BSON dans Go. Le type bson.D utilisé dans la fonction getAll() représente un document BSON et il est utilisé lorsque l’ordre des propriétés est important. En passant bson.D{{}} comme filtre à filterTasks(), vous indiquez que vous voulez faire correspondre tous les documents de la collection.

      Dans la fonction filterTasks(), vous itérez sur le Curseur renvoyé par la méthode collection.Find() et décodez chaque document en une instance de Task. Chaque Task est ensuite annexée à la slice de tâches créée au début de la fonction. Une fois que le Curseur est épuisé, il est fermé et la slice des tasks est retournée.

      Avant de créer une commande pour lister toutes les tâches, créons une fonction d’aide qui prend une slice de tasks et l’imprime sur la sortie standard. Vous utiliserez le package color pour colorer la sortie.

      Avant de pouvoir utiliser ce package, installez le avec :

      • go get gopkg.in/gookit/color.v1

      Vous verrez la sortie suivante :

      Output

      go: downloading gopkg.in/gookit/color.v1 v1.1.6 go: gopkg.in/gookit/color.v1 upgrade => v1.1.6

      Et importez-le dans votre fichier main.go en même temps que le package fmt :

      main.go

      package main
      
      import (
          "context"
          "errors"
        "fmt"
          "log"
          "os"
          "time"
      
          "github.com/urfave/cli/v2"
          "go.mongodb.org/mongo-driver/bson"
          "go.mongodb.org/mongo-driver/bson/primitive"
          "go.mongodb.org/mongo-driver/mongo"
          "go.mongodb.org/mongo-driver/mongo/options"
          "gopkg.in/gookit/color.v1"
      )
      . . .
      

      Ensuite, créez une nouvelle fonction printTasks à la suite de votre fonction main :

      main.go

      . . .
      func printTasks(tasks []*Task) {
          for i, v := range tasks {
              if v.Completed {
                  color.Green.Printf("%d: %sn", i+1, v.Text)
              } else {
                  color.Yellow.Printf("%d: %sn", i+1, v.Text)
              }
          }
      }
      . . .
      

      Cette fonction printTasks prend une slice de tasks, l’itére sur chacune et l’imprime sur la sortie standard en utilisant la couleur verte pour indiquer les tâches terminées, et jaune pour les tâches incomplètes.

      Poursuivez en ajoutant les lignes surlignées suivantes pour créer une nouvelle commande all dans la slice Commands. Cette commande permet d’imprimer toutes les tâches ajoutées à la sortie standard :

      main.go

      . . .
      func main() {
          app := &cli.App{
              Name:  "tasker",
              Usage: "A simple CLI program to manage your tasks",
              Commands: []*cli.Command{
                  {
                      Name:    "add",
                      Aliases: []string{"a"},
                      Usage:   "add a task to the list",
                      Action: func(c *cli.Context) error {
                          str := c.Args().First()
                          if str == "" {
                              return errors.New("Cannot add an empty task")
                          }
      
                          task := &Task{
                              ID:        primitive.NewObjectID(),
                              CreatedAt: time.Now(),
                              UpdatedAt: time.Now(),
                              Text:      str,
                              Completed: false,
                          }
      
                          return createTask(task)
                      },
                  },
                  {
                      Name:    "all",
                      Aliases: []string{"l"},
                      Usage:   "list all tasks",
                      Action: func(c *cli.Context) error {
                          tasks, err := getAll()
                          if err != nil {
                              if err == mongo.ErrNoDocuments {
                                  fmt.Print("Nothing to see here.nRun `add 'task'` to add a task")
                                  return nil
                              }
      
                              return err
                          }
      
                          printTasks(tasks)
                          return nil
                      },
                  },
              },
          }
      
          err := app.Run(os.Args)
          if err != nil {
              log.Fatal(err)
          }
      }
      
      . . .
      

      La commande all récupère toutes les tâches présentes dans la base de données et les imprime sur la sortie standard. Si aucune tâche n’est présente, une invite à ajouter une nouvelle tâche est imprimée à la place.

      Enregistrez et fermez votre fichier

      Construisez et exécutez votre programme avec la commande all :

      Il énumérera toutes les tâches que vous avez ajoutées jusqu’à présent :

      Output

      1: Learn Go 2: Read a book

      Maintenant que vous pouvez visualiser toutes les tâches dans la base de données, ajoutons la possibilité de marquer une tâche comme terminée à l’étape suivante.

      Étape 5 — Fin d’une tâche

      Dans cette étape, vous créerez une nouvelle sous-commande appelée done qui vous permettra de marquer une tâche existante dans la base de données comme étant terminée. Pour marquer une tâche comme terminée, vous pouvez utiliser la méthode collection.FindOneAndUpdate(). Il permet de localiser un document dans une collection et de mettre à jour toutes ou une partie de ses propriétés. Cette méthode nécessite un filtre pour localiser le document et un document de mise à jour pour décrire l’opération. Les deux sont construits en utilisant les types bson.D.

      Commencez par ouvrir votre fichier main.go :

      Insérez le snippet suivant à la suite de votre fonction filterTasks :

      main.go

      . . .
      func completeTask(text string) error {
          filter := bson.D{primitive.E{Key: "text", Value: text}}
      
          update := bson.D{primitive.E{Key: "$set", Value: bson.D{
              primitive.E{Key: "completed", Value: true},
          }}}
      
          t := &Task{}
          return collection.FindOneAndUpdate(ctx, filter, update).Decode(t)
      }
      . . .
      

      La fonction correspond au premier document où la propriété de texte est égale au paramètre text. Le document update précise que la propriété completed doit être réglée sur true. S’il y a une erreur dans l’opération FindOneAndUpdate(), elle sera renvoyée par completeTask(). Sinon, nil est renvoyé.

      Ensuite, ajoutons une nouvelle commande done à votre programme CLI qui marque une tâche comme étant terminée :

      main.go

      . . .
      func main() {
          app := &cli.App{
              Name:  "tasker",
              Usage: "A simple CLI program to manage your tasks",
              Commands: []*cli.Command{
                  {
                      Name:    "add",
                      Aliases: []string{"a"},
                      Usage:   "add a task to the list",
                      Action: func(c *cli.Context) error {
                          str := c.Args().First()
                          if str == "" {
                              return errors.New("Cannot add an empty task")
                          }
      
                          task := &Task{
                              ID:        primitive.NewObjectID(),
                              CreatedAt: time.Now(),
                              UpdatedAt: time.Now(),
                              Text:      str,
                              Completed: false,
                          }
      
                          return createTask(task)
                      },
                  },
                  {
                      Name:    "all",
                      Aliases: []string{"l"},
                      Usage:   "list all tasks",
                      Action: func(c *cli.Context) error {
                          tasks, err := getAll()
                          if err != nil {
                              if err == mongo.ErrNoDocuments {
                                  fmt.Print("Nothing to see here.nRun `add 'task'` to add a task")
                                  return nil
                              }
      
                              return err
                          }
      
                          printTasks(tasks)
                          return nil
                      },
                  },
                  {
                      Name:    "done",
                      Aliases: []string{"d"},
                      Usage:   "complete a task on the list",
                      Action: func(c *cli.Context) error {
                          text := c.Args().First()
                          return completeTask(text)
                      },
                  },
              },
          }
      
          err := app.Run(os.Args)
          if err != nil {
              log.Fatal(err)
          }
      }
      
      . . .
      

      Vous utilisez l’argument passé à la commande done pour trouver le premier document dont la propriété text correspond. S’il est trouvé, la propriété completed sur le document est réglée sur true.

      Enregistrez et fermez votre fichier

      Ensuite, exécutez votre programme avec la commande done :

      • go run main.go done "Learn Go"

      Si vous utilisez à nouveau la commande all, vous remarquerez que la tâche qui était marquée comme terminée est maintenant imprimée en vert.

      Capture d'écran de la sortie du terminal après l'achèvement d'une tâche

      Parfois, vous voulez seulement voir les tâches qui n’ont pas encore été terminées. Nous ajouterons cet élément par la suite.

      Étape 6 — Affichage des tâches en attente uniquement

      Dans cette étape, vous allez incorporer du code pour récupérer les tâches en attente dans la base de données, en utilisant le pilote MongoDB. Les tâches en attente sont celles dont la propriété completed est réglée sur false.

      Ajoutons une nouvelle fonction qui récupère les tâches qui n’ont pas encore été terminées. Ouvrez votre fichier main.go :

      Ajoutez ensuite ce snippet à la suite de la fonction completeTask :

      main.go

      . . .
      func getPending() ([]*Task, error) {
          filter := bson.D{
              primitive.E{Key: "completed", Value: false},
          }
      
          return filterTasks(filter)
      }
      . . .
      

      Vous créez un filtre en utilisant les package bson et primitive du pilote MongoDB, qui correspondra aux documents dont la propriété completed est réglée sur false. La slice des tâches en attente est ensuite renvoyée à l’appelant.

      Au lieu de créer une nouvelle commande pour lister les tâches en attente, faisons en sorte que ce soit l’action par défaut lors de l’exécution du programme sans aucune commande. Vous pouvez effectuer ceci en ajoutant une propriété Action au programme comme suit :

      main.go

      . . .
      func main() {
          app := &cli.App{
              Name:  "tasker",
              Usage: "A simple CLI program to manage your tasks",
              Action: func(c *cli.Context) error {
                  tasks, err := getPending()
                  if err != nil {
                      if err == mongo.ErrNoDocuments {
                          fmt.Print("Nothing to see here.nRun `add 'task'` to add a task")
                          return nil
                      }
      
                      return err
                  }
      
                  printTasks(tasks)
                  return nil
              },
              Commands: []*cli.Command{
                  {
                      Name:    "add",
                      Aliases: []string{"a"},
                      Usage:   "add a task to the list",
                      Action: func(c *cli.Context) error {
                          str := c.Args().First()
                          if str == "" {
                              return errors.New("Cannot add an empty task")
                          }
      
                          task := &Task{
                              ID:        primitive.NewObjectID(),
                              CreatedAt: time.Now(),
                              UpdatedAt: time.Now(),
                              Text:      str,
                              Completed: false,
                          }
      
                          return createTask(task)
                      },
                  },
      . . .
      

      La propriété Action exécute une action par défaut lorsque le programme est exécuté sans aucune sous-commande. C’est là que se situe la logique de la liste des tâches en attente. La fonction getPending() est appelée et les tâches résultantes sont imprimées sur la sortie standard à l’aide de printTasks(). S’il n’y a pas de tâches en attente, une invite est affichée à la place, encourageant l’utilisateur à ajouter une nouvelle tâche à l’aide de la commande add.

      Enregistrez et fermez votre fichier

      Si vous exécutez le programme maintenant sans ajouter de commandes, toutes les tâches en attente seront listées dans la base de données :

      Vous verrez la sortie suivante :

      Output

      1: Read a book

      Maintenant que vous pouvez lister les tâches incomplètes, ajoutons une autre commande qui vous permet de ne voir que les tâches terminées.

      Étape 7 — Affichage des tâches terminées

      Dans cette étape, vous ajouterez une nouvelle sous-commande finished qui récupère les tâches terminées dans la base de données et les affiche à l’écran. Cela implique de filtrer et de renvoyer les tâches dont la propriété completed est réglée sur true.

      Ouvrez votre fichier main.go :

      Ajoutez ensuite le code suivant à la fin de votre dossier :

      main.go

      . . .
      func getFinished() ([]*Task, error) {
          filter := bson.D{
              primitive.E{Key: "completed", Value: true},
          }
      
          return filterTasks(filter)
      }
      . . .
      

      Comme pour la fonction getPending(), vous avez ajouté une fonction getFinished() qui renvoie une slice de tâches terminées. Dans ce cas, le filtre a la propriété completed réglée sur true, de sorte que seuls les documents qui correspondent à cette condition seront retournés.

      Ensuite, créez une commande finished qui imprime toutes les tâches terminées :

      main.go

      . . .
      func main() {
          app := &cli.App{
              Name:  "tasker",
              Usage: "A simple CLI program to manage your tasks",
              Action: func(c *cli.Context) error {
                  tasks, err := getPending()
                  if err != nil {
                      if err == mongo.ErrNoDocuments {
                          fmt.Print("Nothing to see here.nRun `add 'task'` to add a task")
                          return nil
                      }
      
                      return err
                  }
      
                  printTasks(tasks)
                  return nil
              },
              Commands: []*cli.Command{
                  {
                      Name:    "add",
                      Aliases: []string{"a"},
                      Usage:   "add a task to the list",
                      Action: func(c *cli.Context) error {
                          str := c.Args().First()
                          if str == "" {
                              return errors.New("Cannot add an empty task")
                          }
      
                          task := &Task{
                              ID:        primitive.NewObjectID(),
                              CreatedAt: time.Now(),
                              UpdatedAt: time.Now(),
                              Text:      str,
                              Completed: false,
                          }
      
                          return createTask(task)
                      },
                  },
                  {
                      Name:    "all",
                      Aliases: []string{"l"},
                      Usage:   "list all tasks",
                      Action: func(c *cli.Context) error {
                          tasks, err := getAll()
                          if err != nil {
                              if err == mongo.ErrNoDocuments {
                                  fmt.Print("Nothing to see here.nRun `add 'task'` to add a task")
                                  return nil
                              }
      
                              return err
                          }
      
                          printTasks(tasks)
                          return nil
                      },
                  },
                  {
                      Name:    "done",
                      Aliases: []string{"d"},
                      Usage:   "complete a task on the list",
                      Action: func(c *cli.Context) error {
                          text := c.Args().First()
                          return completeTask(text)
                      },
                  },
                  {
                      Name:    "finished",
                      Aliases: []string{"f"},
                      Usage:   "list completed tasks",
                      Action: func(c *cli.Context) error {
                          tasks, err := getFinished()
                          if err != nil {
                              if err == mongo.ErrNoDocuments {
                                  fmt.Print("Nothing to see here.nRun `done 'task'` to complete a task")
                                  return nil
                              }
      
                              return err
                          }
      
                          printTasks(tasks)
                          return nil
                      },
                  },
              }
          }
      
          err := app.Run(os.Args)
          if err != nil {
              log.Fatal(err)
          }
      }
      . . .
      

      La commande finished récupère les tâches dont la propriété completed est réglée sur true via la fonction getFinished() créée ici. Il la transmet ensuite à la fonction printTasks afin qu’elle soit imprimée sur la sortie standard.

      Enregistrez et fermez votre fichier

      Exécutez la commande suivante :

      Vous verrez la sortie suivante :

      Output

      1: Learn Go

      Dans la dernière étape, vous donnerez aux utilisateurs la possibilité de supprimer des tâches de la base de données.

      Étape 8 — Suppression d’une tâche

      Dans cette étape, vous ajouterez une nouvelle sous-commande delete pour permettre aux utilisateurs de supprimer une tâche de la base de données. Pour supprimer une seule tâche, vous utiliserez la méthode collection.DeleteOne() du pilote MongoDB. Il s’appuie également sur un filtre pour faire correspondre le document à supprimer.

      Ouvrez votre fichier main.go une fois de plus :

      Ajoutez cette fonction deleteTask pour supprimer des tâches de la base de données juste après votre fonction getFinished :

      main.go

      . . .
      func deleteTask(text string) error {
          filter := bson.D{primitive.E{Key: "text", Value: text}}
      
          res, err := collection.DeleteOne(ctx, filter)
          if err != nil {
              return err
          }
      
          if res.DeletedCount == 0 {
              return errors.New("No tasks were deleted")
          }
      
          return nil
      }
      . . .
      

      Cette méthode deleteTask prend un argument de type chaîne de caractères qui représente l’élément de tâche à supprimer. Un filtre est construit pour correspondre à l’élément de tâche dont la propriété text est réglée sur l’argument de la chaîne de caractères. Vous passez le filtre à la méthode DeleteOne() qui correspond à l’article dans la collection et le supprime.

      Vous pouvez vérifier la propriété DeletedCount sur le résultat de la méthode DeleteOne pour confirmer si des documents ont été supprimés. Si le filtre ne parvient pas à faire correspondre un document à supprimer, le DeletedCount sera égal à zéro et vous pourrez renvoyer une erreur dans ce cas.

      Ajoutez maintenant une nouvelle commande rm comme surlignée :

      main.go

      . . .
      func main() {
          app := &cli.App{
              Name:  "tasker",
              Usage: "A simple CLI program to manage your tasks",
              Action: func(c *cli.Context) error {
                  tasks, err := getPending()
                  if err != nil {
                      if err == mongo.ErrNoDocuments {
                          fmt.Print("Nothing to see here.nRun `add 'task'` to add a task")
                          return nil
                      }
      
                      return err
                  }
      
                  printTasks(tasks)
                  return nil
              },
              Commands: []*cli.Command{
                  {
                      Name:    "add",
                      Aliases: []string{"a"},
                      Usage:   "add a task to the list",
                      Action: func(c *cli.Context) error {
                          str := c.Args().First()
                          if str == "" {
                              return errors.New("Cannot add an empty task")
                          }
      
                          task := &Task{
                              ID:        primitive.NewObjectID(),
                              CreatedAt: time.Now(),
                              UpdatedAt: time.Now(),
                              Text:      str,
                              Completed: false,
                          }
      
                          return createTask(task)
                      },
                  },
                  {
                      Name:    "all",
                      Aliases: []string{"l"},
                      Usage:   "list all tasks",
                      Action: func(c *cli.Context) error {
                          tasks, err := getAll()
                          if err != nil {
                              if err == mongo.ErrNoDocuments {
                                  fmt.Print("Nothing to see here.nRun `add 'task'` to add a task")
                                  return nil
                              }
      
                              return err
                          }
      
                          printTasks(tasks)
                          return nil
                      },
                  },
                  {
                      Name:    "done",
                      Aliases: []string{"d"},
                      Usage:   "complete a task on the list",
                      Action: func(c *cli.Context) error {
                          text := c.Args().First()
                          return completeTask(text)
                      },
                  },
                  {
                      Name:    "finished",
                      Aliases: []string{"f"},
                      Usage:   "list completed tasks",
                      Action: func(c *cli.Context) error {
                          tasks, err := getFinished()
                          if err != nil {
                              if err == mongo.ErrNoDocuments {
                                  fmt.Print("Nothing to see here.nRun `done 'task'` to complete a task")
                                  return nil
                              }
      
                              return err
                          }
      
                          printTasks(tasks)
                          return nil
                      },
                  },
                  {
                      Name:  "rm",
                      Usage: "deletes a task on the list",
                      Action: func(c *cli.Context) error {
                          text := c.Args().First()
                          err := deleteTask(text)
                          if err != nil {
                              return err
                          }
      
                          return nil
                      },
                  },
              }
          }
      
          err := app.Run(os.Args)
          if err != nil {
              log.Fatal(err)
          }
      }
      . . .
      

      Comme pour toutes les autres sous-commandes ajoutées précédemment, la commande rm utilise son premier argument pour faire correspondre une tâche dans la base de données et la supprime.

      Enregistrez et fermez votre fichier

      Vous pouvez lister les tâches en attente en exécutant votre programme sans passer par des sous-commandes :

      Output

      1: Read a book

      L’exécution de la sous-commande rm sur la tâche "Read a book" la supprimera de la base de données :

      • go run main.go rm "Read a book"

      Si vous listez à nouveau toutes les tâches en attente, vous remarquerez que la tâche "Read a book" n’apparaît plus et qu’une invite à ajouter une nouvelle tâche est affichée à la place :

      Output

      Nothing to see here Run `add 'task'` to add a task

      Dans cette étape, vous avez ajouté une fonction permettant de supprimer des tâches de la base de données.

      Conclusion

      Vous avez créé avec succès un programme de ligne de commande de gestionnaire de tâches et appris les bases de l’utilisation du pilote MongoDB Go dans le processus.

      N’oubliez pas de consulter la documentation complète du pilote MongoDB Go sur GoDoc pour en savoir plus sur les fonctionnalités qu’offre l’utilisation du pilote. La documentation qui décrit l’utilisation des agrégats ou des transactions peut vous intéresser particulièrement.

      Le code final de ce tutoriel peut être consulté dans ce dépôt GitHub.



      Source link

      Utiliser les buffers dans Node.js


      L’auteur a choisi le COVID-19 Relief Fund pour recevoir un don dans le cadre du programme Write for DOnations.

      Introduction

      Un buffer est un espace en mémoire (généralement de la RAM) qui stocke des données binaires. Dans Node.js, nous pouvons accéder à ces espaces de mémoire grâce à la classe Buffer intégrée. Les buffers stockent une séquence d’entiers, comme un array en JavaScript. Contrairement aux arrays, vous ne pouvez pas modifier la taille d’un buffer une fois qu’il est créé.

      Vous avez peut-être utilisé des buffers sans vous en rendre compte si vous avez déjà écrit du code de Node.js. Par exemple, lorsque vous lisez à partir d’un fichier avec fs.readFile(), les données renvoyées au callback ou à la Promise sont un objet buffer. En outre, lorsque des requêtes HTTP sont effectuées dans Node.js, elles renvoient des flux de données qui sont temporairement stockés dans un buffer interne lorsque le client ne peut pas traiter le flux en une seule fois.

      Les buffers sont utiles lorsque vous interagissez avec des données binaires, généralement à des niveaux de réseau inférieurs. Ils vous donnent également la possibilité d’effectuer des manipulations fines de données dans Node.js.

      Dans ce tutoriel, vous utiliserez Node.js REPL pour parcourir divers exemples de buffers, comme la création de buffers, la lecture de buffers, l’écriture et la copie de buffers, et l’utilisation de buffers pour convertir des données binaires en données codées. À la fin du tutoriel, vous aurez appris comment utiliser la classe Buffer pour travailler avec des données binaires.

      Conditions préalables

      Étape 1 — Création d’un buffer

      Cette première étape vous présentera les deux façons principales de créer un objet buffer dans Node.js.

      Pour décider de la méthode à utiliser, vous devez répondre à cette question : voulez-vous créer un nouveau buffer ou extraire un buffer à partir de données existantes ? Si vous allez stocker en mémoire des données que vous n’avez pas encore reçues, vous voudrez créer un nouveau buffer. Dans Node.js, nous utilisons la fonction alloc() de la classe Buffer pour ce faire.

      Ouvrons le Node.js REPL pour voir par nous-mêmes. Dans votre terminal, entrez la commande node :

      Vous verrez que l’invite commence par >.

      La fonction alloc() prend la taille du buffer comme premier et seul argument requis. La taille est un nombre entier représentant le nombre d’octets de mémoire que l’objet buffer utilisera. Par exemple, si nous voulions créer un buffer de 1 Ko (kilooctet), équivalent à 1024 octets, nous entrerions ceci dans la console :

      • const firstBuf = Buffer.alloc(1024);

      Pour créer un nouveau buffer, nous avons utilisé la classe Buffer globalement disponible, qui contient la méthode alloc(). En fournissant 1024 comme argument pour alloc(), nous avons créé un buffer d’une taille de 1 Ko.

      Par défaut, lorsque vous initialisez un buffer avec alloc(), le buffer est rempli de zéros binaires en guise d’emplacement pour les données ultérieures. Cependant, nous pouvons modifier la valeur par défaut si nous le souhaitons. Si nous voulions créer un nouveau buffer avec des 1 au lieu des 0, nous définirions le deuxième paramètre de la fonction alloc()fill.

      Dans votre terminal, créez un nouveau buffer à l’invite REPL qui est rempli de 1 :

      • const filledBuf = Buffer.alloc(1024, 1);

      Nous venons de créer un nouvel objet buffer qui fait référence à un espace en mémoire qui stocke 1 Ko de 1. Bien que nous ayons saisi un nombre entier, toutes les données stockées dans un buffer sont des données binaires.

      Les données binaires peuvent se présenter sous de nombreux formats différents. Considérons par exemple une séquence binaire représentant un octet de données : 01110110. Si cette séquence binaire représentait une chaîne en anglais utilisant la norme de codage ASCII, il s’agirait de la lettre v. Cependant, si notre ordinateur traitait une image, cette séquence binaire pourrait contenir des informations sur la couleur d’un pixel.

      L’ordinateur sait les traiter différemment parce que les octets sont codés différemment. L’encodage des octets est le format de l’octet. Un buffer dans Node.js utilise le schéma d’encodage UTF-8 par défaut s’il est initialisé avec des données de chaîne. Un octet en UTF-8 représente un nombre, une lettre (en anglais et dans d’autres langues) ou un symbole. L’UTF-8 est un superset de l’ASCII, le code standard américain pour l’échange d’informations. L’ASCII peut coder des octets avec des lettres anglaises majuscules et minuscules, les chiffres 0-9, et quelques autres symboles comme le point d’exclamation (!) ou le signe esperluette (&).

      Si nous écrivions un programme qui ne pourrait fonctionner qu’avec des caractères ASCII, nous pourrions modifier l’encodage utilisé par notre buffer avec le troisième argument de la fonction alloc()encoding.

      Créons un nouveau buffer de cinq octets de long qui ne stocke que des caractères ASCII :

      • const asciiBuf = Buffer.alloc(5, 'a', 'ascii');

      Le buffer est initialisé avec cinq octets du caractère a, en utilisant la représentation ASCII.

      Remarque : par défaut, Node.js prend en charge les codages de caractères suivants :

      • ASCII, représenté sous le nom ascii
      • UTF-8, représenté sous le nom utf-8 ou utf8
      • UTF-16, représenté sous le nom utf-16le ou utf16le
      • UCS-2, représentée sous le nom ucs-2 ou ucs2
      • Base64, représentée sous le nom base64
      • Hexadecimal, représenté sous le nom hex
      • ISO/IEC 8859-1, représenté sous le nom latin1 ou binary

      Toutes ces valeurs peuvent être utilisées dans les fonctions de la classe Buffer qui acceptent un paramètre encoding. Par conséquent, ces valeurs sont toutes valables pour la méthode alloc().

      Jusqu’à présent, nous avons créé de nouveaux buffers avec la fonction alloc(). Mais nous pouvons parfois vouloir créer un buffer à partir de données qui existent déjà, comme une chaîne ou un tableau.

      Pour créer un buffer à partir de données pré-existantes, nous utilisons la méthode from(). Nous pouvons utiliser cette fonction pour créer des buffers à partir de :

      • Un tableau d’entiers : les valeurs des entiers peuvent être comprises entre 0 et 255.
      • Un ArrayBuffer : c’est un objet JavaScript qui stocke une longueur fixe d’octets.
      • Une chaîne.
      • Un autre buffer.
      • D’autres objets JavaScript qui ont une propriété Symbol.toPrimitive. Cette propriété indique à JavaScript comment convertir l’objet en un type de données primitives : boolean, null, undefined, number, string, or symbol. Vous pouvez en savoir plus sur les symboles en consultant la documentation JavaScript de Mozilla.

      Voyons comment nous pouvons créer un buffer à partir d’une chaîne. Dans l’invite Node.js, saisissez ceci :

      • const stringBuf = Buffer.from('My name is Paul');

      Nous avons maintenant un objet buffer créé à partir de la chaîne de caractères My name is Paul. Créons un nouveau buffer à partir d’un autre buffer que nous avons créé précédemment :

      • const asciiCopy = Buffer.from(asciiBuf);

      Nous avons maintenant créé un nouveau buffer asciiCopy qui contient les mêmes données que asciiBuf.

      Maintenant que nous avons fait l’expérience de la création de buffers, nous pouvons nous plonger dans des exemples de lecture de leurs données.

      Étape 2 — Lecture à partir d’un buffer

      Il existe de nombreuses façons d’accéder aux données dans un buffer. Nous pouvons accéder à un octet individuel dans un buffer ou nous pouvons extraire le contenu entier.

      Pour accéder à un octet d’un buffer, nous passons l’index ou l’emplacement de l’octet que nous voulons. Les buffers stockent les données de manière séquentielle comme des tableaux. Ils indexent également leurs données comme des tableaux, à partir de 0. Nous pouvons utiliser la notation de tableau sur l’objet buffer pour obtenir un octet individuel.

      Voyons à quoi cela ressemble en créant un buffer à partir d’une chaîne dans le REPL :

      • const hiBuf = Buffer.from('Hi!');

      Maintenant, lisons le premier octet du buffer :

      Lorsque vous appuyez sur ENTER, le REPL affiche :

      Output

      72

      L’entier 72 correspond à la représentation UTF-8 pour la lettre H.

      Remarque : les valeurs pour les octets peuvent être des nombres entre 0 et 255. Un octet est une séquence de 8 bits. Un bit est binaire, et ne peut donc avoir qu’une seule des deux valeurs : 0 ou 1. Si nous avons une séquence de 8 bits et deux valeurs possibles par bit, alors nous avons un maximum de 2⁸ valeurs possibles pour un octet. Cela correspond à un maximum de 256 valeurs. Comme nous commençons à compter à partir de zéro, cela signifie que notre nombre le plus élevé est 255.

      Faisons de même pour le deuxième octet. Entrez ce qui suit dans le REPL :

      Le REPL renvoie 105, qui représente le i minuscule.

      Enfin, obtenons le troisième caractère :

      Vous verrez 33 affiché dans le REPL, ce qui correspond à !

      Essayons de récupérer un octet d’un index non valide :

      Le REPL renverra :

      Output

      undefined

      C’est comme si nous essayions d’accéder à un élément d’un tableau avec un index incorrect.

      Maintenant que nous avons vu comment lire les octets individuels d’un buffer, voyons nos options pour récupérer en une seule fois toutes les données stockées dans un buffer. L’objet buffer est doté des méthodes toString() et toJSON(), qui renvoient l’intégralité du contenu d’un buffer dans deux formats différents.

      Comme son nom l’indique, la méthode toString() convertit les octets du buffer en une chaîne de caractères et la renvoie à l’utilisateur. Si nous utilisons cette méthode sur hiBuf, nous obtiendrons la chaîne Hi!. Essayons !

      Dans l’invite, saisissez :

      Le REPL renverra :

      Output

      'Hi!'

      Ce buffer a été créé à partir d’une chaîne. Voyons ce qui se passe si nous utilisons la fonction toString() sur un buffer qui n’a pas été créé à partir de données de chaîne.

      Créons un nouveau buffer vide d’une taille de 10 octets :

      • const tenZeroes = Buffer.alloc(10);

      Maintenant, utilisons la méthode toString() :

      Nous verrons le résultat suivant :

      'u0000u0000u0000u0000u0000u0000u0000u0000u0000u0000'
      

      La chaîne u0000 est le caractère Unicode pour NULL. Il correspond au nombre 0. Lorsque les données du buffer ne sont pas encodées sous forme de chaîne, la méthode toString() renvoie l’encodage UTF-8 des octets.

      La toString() a un paramètre optionnel, encoding. Nous pouvons utiliser ce paramètre pour modifier l’encodage des données du buffer qui sont renvoyées.

      Par exemple, si vous voulez l’encodage hexadecimal pour hiBuf, vous devez entrer ce qui suit à l’invite :

      Cette instruction évaluera :

      Output

      '486921'

      486921 est la représentation hexadécimale des octets qui représentent la chaîne Hi! Dans Node.js, lorsque les utilisateurs veulent convertir l’encodage des données d’un formulaire à un autre, ils mettent généralement la chaîne dans un buffer et appellent toString() avec l’encodage souhaité.

      La méthode toJSON() se comporte différemment. Que le buffer ait été fait à partir d’une chaîne ou non, il renvoie toujours les données sous forme de représentation entière de l’octet.

      Réutilisons les buffers hiBuf et tenZeroes pour nous entraîner à utiliser toJSON(). Dans l’invite, saisissez :

      Le REPL renverra :

      Output

      { type: 'Buffer', data: [ 72, 105, 33 ] }

      L’objet JSON a une propriété type qui sera toujours Buffer. C’est ainsi que les programmes peuvent distinguer ces objets JSON des autres objets JSON.

      La propriété data contient un tableau de la représentation entière des octets. Vous avez peut-être remarqué que 72, 105 et 33 correspondent aux valeurs que nous avons reçues lorsque nous avons tiré les octets individuellement.

      Essayons la méthode toJSON() avec tenZeroes :

      Dans le REPL, vous verrez ce qui suit :

      Output

      { type: 'Buffer', data: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }

      Le type est le même que celui indiqué précédemment. Cependant, les données sont maintenant un tableau avec dix zéros.

      Maintenant que nous avons couvert les principales façons de lire à partir d’un buffer, voyons comment nous modifions le contenu d’un buffer.

      Étape 3 — Modification d’un buffer

      Il existe de nombreuses façons de modifier un objet buffer existant. Comme pour la lecture, nous pouvons modifier individuellement les octets du buffer en utilisant la syntaxe du tableau. Nous pouvons également écrire de nouveaux contenus dans un buffer, en remplacement des données existantes.

      Commençons par examiner comment nous pouvons modifier les octets individuels d’un buffer. Rappelons notre variable tampon hiBuf, qui contient la chaîne de caractères Hi!. Changeons chaque octet pour qu’elle contienne plutôt Hey.

      Dans le REPL, essayons d’abord de remplacer le deuxième élément de hiBuf par e :

      Voyons maintenant ce buffer comme une chaîne de caractères pour confirmer qu’il stocke les bonnes données. Poursuivez en appelant la méthode toString() :

      Il sera évalué comme :

      Output

      'Hu0000!'

      Nous avons reçu cette étrange sortie parce que le buffer ne peut accepter qu’une valeur entière. On ne peut pas l’attribuer à la lettre e ; il faut plutôt lui attribuer le nombre dont l’équivalent binaire représente e :

      Maintenant, lorsque nous appelons la méthode toString() :

      Nous obtenons cette sortie dans le REPL :

      Output

      'He!'

      Pour modifier le dernier caractère du buffer, nous devons fixer le troisième élément au nombre entier qui correspond à l’octet pour y :

      Confirmez en utilisant à nouveau la méthode toString() :

      Votre REPL affichera :

      Output

      'Hey'

      Si nous essayons d’écrire un octet qui est en dehors de la plage du buffer, il sera ignoré et le contenu du buffer ne changera pas. Essayons, par exemple, de régler le quatrième élément inexistant du buffer sur o :

      Nous pouvons confirmer que le buffer est inchangé avec la méthode toString() :

      La sortie est toujours :

      Output

      'Hey'

      Si nous voulons modifier le contenu de l’ensemble du buffer, nous pouvons utiliser la méthode write(). La méthode write() accepte une chaîne de caractères qui remplacera le contenu d’un buffer.

      Utilisons la méthode write() pour modifier le contenu de hiBuf et revenir à Hi!. Dans votre shell Node.js, tapez la commande suivante à l’invite :

      La méthode write() a renvoyé 3 dans le REPL. C’est parce qu’il a écrit trois octets de données. Chaque lettre a la taille d’un octet, puisque ce buffer utilise le codage UTF-8, qui utilise un octet pour chaque caractère. Si le buffer avait utilisé le codage UTF-16, qui comporte un minimum de deux octets par caractère, la fonction write() aurait renvoyé 6.

      Maintenant, vérifiez le contenu du buffer en utilisant toString() :

      Le REPL produira :

      Output

      'Hi!'

      Cette technique est plus rapide que de devoir modifier chaque élément octet par octet.

      Si vous essayez d’écrire plus d’octets que la taille d’un buffer, l’objet buffer n’acceptera que le nombre d’octets qu’il peut recevoir. Pour illustrer cela, créons un buffer qui stocke trois octets :

      • const petBuf = Buffer.alloc(3);

      Essayons maintenant d’y écrire Cats :

      Lorsque l’appel write() est évalué, le REPL renvoie 3, indiquant que seuls trois octets ont été écrits dans le buffer. Confirmez maintenant que le buffer contient les trois premiers octets :

      Le REPL renvoie :

      Output

      'Cat'

      La fonction write() ajoute les octets dans un ordre séquentiel, de sorte que seuls les trois premiers octets ont été placés dans le buffer.

      En revanche, faisons un Buffer qui stocke quatre octets :

      • const petBuf2 = Buffer.alloc(4);

      Ecrivez-lui le même contenu :

      Ensuite, ajoutez un nouveau contenu qui occupe moins d’espace que le contenu original :

      Puisque les buffers s’écrivent séquentiellement en partant de 0, si nous imprimions le contenu du buffer :

      Nous verrions :

      Output

      'Hits'

      Les deux premiers caractères sont écrasés, mais le reste du buffer est intact.

      Parfois, les données que nous voulons dans notre buffer préexistant ne sont pas dans une chaîne mais résident dans un autre objet buffer. Dans ces cas, nous pouvons utiliser la fonction copy() pour modifier ce que notre buffer stocke.

      Créons deux nouveaux buffers :

      • const wordsBuf = Buffer.from('Banana Nananana');
      • const catchphraseBuf = Buffer.from('Not sure Turtle!');

      Les buffers wordsBuf et catchphraseBuf contiennent tous deux des données de chaîne. Nous voulons modifier catchphraseBuf pour qu’il stocke Nananana Turtle! au lieu de Not sure Turtle! . Nous utiliserons copy() pour faire passer Nananana de wordsBuf à catchphraseBuf.

      Pour copier des données d’un buffer à l’autre, nous utiliserons la méthode copy() sur le buffer qui est la source de l’information. Par conséquent, comme wordsBuf possède les données des chaînes de caractères que nous voulons copier, nous devons copier comme ceci :

      • wordsBuf.copy(catchphraseBuf);

      Dans ce cas, le paramètre target est le buffer catchphraseBuf.

      Lorsque nous entrons cela dans le REPL, il renvoie 15 indiquant que 15 octets ont été écrits. La chaîne Nananana n’utilise que 8 octets de données, nous savons donc immédiatement que notre copie ne s’est pas déroulée comme prévu. Utilisez la méthode toString() pour voir le contenu de catchphraseBuf :

      • catchphraseBuf.toString();

      Le REPL renvoie :

      Output

      'Banana Nananana!'

      Par défaut, copy() a pris tout le contenu de wordsBuf et l’a placé dans catchphraseBuf. Nous devons être plus sélectifs dans notre objectif et ne copier que Nananana. Réécrivons le contenu original de catchphraseBuf avant de continuer :

      • catchphraseBuf.write('Not sure Turtle!');

      La fonction copy() dispose de quelques paramètres supplémentaires qui nous permettent de personnaliser les données qui sont copiées dans l’autre buffer. Voici une liste de tous les paramètres de cette fonction :

      • target – C’est le seul paramètre requis de copy(). Comme nous l’avons vu lors de notre précédente utilisation, il s’agit du buffer vers lequel nous voulons copier.
      • targetStart – Il s’agit de l’index des octets du buffer cible vers lequel nous devons commencer la copie. Par défaut, il est de 0, ce qui signifie qu’il copie les données à partir du début du buffer.
      • sourceStart – C’est l’index des octets du buffer source où nous devons copier.
      • sourceEnd – C’est l’index des octets dans le buffer source où nous devons arrêter la copie. Par défaut, il s’agit de la longueur du buffer.

      Donc, pour copier Nananana de wordsBuf à catchphraseBuf, notre target devrait être catchphraseBuf comme avant. targetStart serait 0, car nous voulons que Nananana apparaisse au début de catchphraseBuf. Le sourceStart devrait être 7, car c’est l’indice où commence Nananana dans wordsBuf. Le sourceEnd continuerait à être la longueur des buffers.

      À l’invite du REPL, copiez le contenu de wordsBuf comme ceci :

      • wordsBuf.copy(catchphraseBuf, 0, 7, wordsBuf.length);

      Le REPL confirme que 8 octets ont été écrits. Notez comment wordsBuf.length est utilisé comme valeur pour le paramètre sourceEnd. Comme pour les tableaux, la propriété length nous donne la taille du buffer.

      Voyons maintenant le contenu de catchphraseBuf :

      • catchphraseBuf.toString();

      Le REPL renvoie :

      Output

      'Nananana Turtle!'

      Bravo! Nous avons pu modifier les données de catchphraseBuf en copiant le contenu de wordsBuf.

      Vous pouvez quitter le Node.js REPL si vous le souhaitez. Notez que toutes les variables qui ont été créées ne seront plus disponibles lorsque vous le ferez :

      Conclusion

      Dans ce tutoriel, vous avez appris que les buffers sont des allocations de longueur fixe en mémoire qui stockent des données binaires. Vous avez d’abord créé des buffers en définissant leur taille en mémoire et en les initialisant avec des données pré-existantes. Vous avez ensuite lu les données d’un buffer en examinant leurs octets individuels et en utilisant les méthodes toString() et toJSON(). Enfin, vous avez modifié les données stockées par un buffer en changeant ses octets individuels et en utilisant les méthodes write() et copy().

      Les buffers vous donnent un bon aperçu de la façon dont les données binaires sont manipulées par Node.js. Maintenant que vous pouvez interagir avec les buffers, vous pouvez observer les différentes façons dont l’encodage des caractères affecte la façon dont les données sont stockées. Par exemple, vous pouvez créer des buffers à partir de données de chaînes qui ne sont pas codées en UTF-8 ou ASCII et observer leur différence de taille. Vous pouvez également choisir un buffer avec UTF-8 et utiliser toString() pour le convertir en d’autres schémas d’encodage.

      Pour en savoir plus sur les buffers dans Node.js, vous pouvez lire la documentation de Node.js sur l’objet Buffer. Si vous souhaitez continuer à apprendre Node.js, vous pouvez revenir à la série Comment coder dans Node.js, ou parcourir les projets de programmation et les configurations sur notre page thématique Node.



      Source link

      Comment installer et utiliser PostgreSQL sur CentOS 8


      Introduction

      Les systèmes de gestion de bases de données relationnelles sont un élément clé de nombreux sites web et applications. Ils offrent un moyen structuré de stocker les informations, de les organiser et d’y accéder.

      PostgreSQL, également connu sous le nom de Postgres, est un système de gestion de base de données relationnelle qui fournit une implémentation du langage de requête structuré, mieux connu sous le nom de SQL. Il est utilisé par de nombreux projets populaires, grands et petits, est conforme aux normes et dispose de nombreuses fonctionnalités avancées, telles que des transactions fiables et une simultanéité sans serrures en lecture. 

      En suivant ce guide, vous installerez la dernière version de PostgreSQL sur un serveur CentOS 8.

      Conditions préalables

      Pour suivre ce tutoriel, vous aurez besoin d’un serveur fonctionnant sous CentOS 8. Ce serveur devra avoir un utilisateur non root avec des privilèges administratifs et un pare-feu configuré avec firewalld. Pour le mettre en place, suivez notre Guide de configuration initiale du serveur pour CentOS 8.

      Étape 1 – Installation de PostgreSQL

      PostgreSQL est disponible sur l’AppStream par défaut de CentOS 8 et il existe plusieurs versions que vous pouvez installer. Vous pouvez choisir entre ces versions en activant la collection appropriée de paquets et de dépendances qui s’alignent sur la version que vous voulez installer, chaque collection étant appelée module stream. 

      Dans DNF, le gestionnaire de paquets par défaut de CentOS 8, les modules sont des collections spéciales de paquets RPM qui, ensemble, constituent une application plus importante. L’objectif est de rendre l’installation des paquets et de leurs dépendances plus intuitive pour les utilisateurs.

      Listez les flux disponibles pour le module postgresql en utilisant la commande dnf :

      • dnf module list postgresql

      Output

      postgresql 9.6 client, server [d] PostgreSQL server and client module postgresql 10 [d] client, server [d] PostgreSQL server and client module postgresql 12 client, server PostgreSQL server and client module

      Vous pouvez voir dans cette sortie qu’il existe trois versions de PostgreSQL disponibles sur le site de dépôt AppStream : 9.6, 10, et 12. Le flux qui fournit la version 10 de Postgres est le flux par défaut, comme l’indique le [d] qui le suit. Si vous souhaitez installer cette version, il vous suffit d’exécuter sudo dnf install postgresql-server et de passer à l’étape suivante. Cependant, même si la version 10 est toujours maintenue, ce tutoriel installera la version 12 de Postgres, la dernière version en date au moment de la rédaction de ce document.

      Pour installer PostgreSQL version 12, vous devez activer le flux de modules de cette version. Lorsque vous activez un flux de modules, vous remplacez le flux par défaut et rendez tous les paquets liés au flux activé disponibles sur le système. Notez qu’un seul flux de tout module donné peut être activé sur un système en même temps.

      Pour activer le flux de modules pour la version 12 de Postgres, exécutez la commande suivante :

      • sudo dnf module enable postgresql:12

      Lorsque vous y êtes invité, appuyez sur y et ensuite ENTER pour confirmer que vous voulez activer le flux : 

      Output

      ==================================================================== Package Architecture Version Repository Size ==================================================================== Enabling module streams: postgresql 12 Transaction Summary ==================================================================== Is this ok [y/N]: y

      Après avoir activé le flux du module de la version 12, vous pouvez installer le postgresql-server pour installer PostgreSQL 12 et toutes ses dépendances : 

      • sudo dnf install postgresql-server

      A l’invite, confirmez l’installation en appuyant sur y puis sur ENTER (ENTRÉE) :

      Output

      . . . Install 4 Packages Total download size: 16 M Installed size: 62 M Is this ok [y/N]: y

      Maintenant que le logiciel est installé, vous allez effectuer quelques étapes d’initialisation pour préparer un nouveau cluster de base de données pour PostgreSQL.

      Étape 2 – Création d’un nouveau cluster de bases de données PostgreSQL

      Vous devez créer un nouveau cluster de base de données PostgreSQL avant de pouvoir commencer à créer des tables et à les charger avec des données. Un cluster de bases de données est un ensemble de bases de données gérées par une seule instance de serveur. La création d’un cluster de base de données consiste à créer les répertoires dans lesquels les données de la base de données seront placées, à générer les tables de catalogue partagées et à créer le template1 et les bases de données postgres.

      La base de données template1 est une sorte de modèle utilisé pour créer de nouvelles bases de données ; tout ce qui est stocké dans template1,même les objets que vous ajoutez vous-même seront placés dans de nouvelles bases de données lors de leur création. La base de données postgres est une base de données par défaut conçue pour être utilisée par les utilisateurs, les services publics et les applications tierces.

      Le paquet Postgres que nous avons installé à l’étape précédente est accompagné d’un script pratique appelé postgresql-setup qui aide à l’administration des clusters de bases de données de bas niveau. Pour créer un cluster de base de données, exécutez le script en utilisant sudo et avec l’option --initdb :

      • sudo postgresql-setup --initdb

      Vous verrez le résultat suivant :

      Output

      * Initializing database in '/var/lib/pgsql/data' * Initialized, logs are in /var/lib/pgsql/initdb_postgresql.log

      Démarrez maintenant le service PostgreSQL en utilisant systemctl : 

      • sudo systemctl start postgresql

      Ensuite, utilisez une fois de plus systemctl pour permettre au service de démarrer à chaque fois que le serveur démarre :

      • sudo systemctl enable postgresql

      Cela donnera le résultat suivant :

      Output

      Created symlink /etc/systemd/system/multi-user.target.wants/postgresql.service → /usr/lib/systemd/system/postgresql.service.

      Maintenant que PostgreSQL est opérationnel, nous allons passer en revue l’utilisation des rôles pour apprendre comment fonctionne Postgres et en quoi il est différent des systèmes de gestion de bases de données similaires que vous avez pu utiliser par le passé.

      Étape 3 – Utilisation des rôles et bases de données PostgreSQL

      PostgreSQL utilise un concept appelé « rôles » pour gérer l’authentification et l’autorisation des clients. Ceux-ci sont en quelque sorte similaires aux comptes réguliers de type Unix, mais Postgres ne fait pas de distinction entre les utilisateurs et les groupes et préfère plutôt le terme plus souple de « rôle ».

      Une fois installé, Postgres est configuré pour utiliser l’authentification ident, ce qui signifie qu’il associe les rôles Postgres à un compte système Unix/Linux correspondant. Si un rôle existe au sein de Postgres, un nom d’utilisateur Unix/Linux portant le même nom peut se connecter à ce rôle.

      La procédure d’installation a créé un compte utilisateur nommé postgres qui est associé au rôle Postgres par défaut. Pour utiliser PostgreSQL, vous pouvez vous connecter à ce compte.

      Il existe plusieurs façons d’utiliser ce compte pour accéder à l’invite PostgreSQL.

      Basculer sur le compte postgres

      Basculez sur le compte postgres sur votre serveur en tapant :

      Vous pouvez maintenant accéder immédiatement à une invite Postgres en tapant :

      Cela vous permettra de vous connecter à l’invite PostgreSQL, et de là, vous serez libre d’interagir immédiatement avec le système de gestion de la base de données.

      Sortez de l’invite PostgreSQL en tapant :

      Cela vous ramènera à la l’invite de commande Linux du compte postgres. Revenez ensuite à votre compte d’origine avec ce qui suit :

      Accéder à une invite Postgres sans changer de compte

      Vous pouvez également exécuter des commandes avec le comptepostgres directement en utilisant sudo. 

      Par exemple, dans l’exemple précédent, on vous a demandé d’accéder à l’invite de Postgres en passant d’abord à l’écran postgres user et ensuite de lancer psql pour ouvrir l’invite de Postgres. Vous pouvez faire cela en une seule étape en exécutant la commande unique psql en tant qu’utilisateur postgres avec privilèges sudo, comme ceci :

      Cela vous permettra de vous connecter directement à Postgres sans passer par le bash shell intermédiaire.

      Là encore, vous pouvez quitter la session Postgres interactive en tapant :

      Dans cette étape, vous avez utilisé le compte postgres pour accéder à l’invite psql. Mais de nombreux cas d’utilisation nécessitent plusieurs rôles Postgres. Lisez ce qui suit pour savoir comment configurer de nouveaux rôles.

      Étape 4 – Création d’un nouveau rôle

      Actuellement, le rôle postgres est le seul à être configuré dans la base de données. Vous pouvez créer de nouveaux rôles à partir de la ligne de commande avec la commande createrole. L’indicateur --interactive vous invitera à indiquer le nom du nouveau rôle et vous demandera également si celui-ci doit disposer des autorisations de superutilisateur.

      Si vous êtes connecté en tant que compte postgres, vous pouvez créer un nouvel utilisateur en tapant :

      Si, au contraire, vous préférez utiliser sudo pour chaque commande sans quitter votre compte normal, tapez :

      • sudo -u postgres createuser --interactive

      Le script vous proposera quelques choix et, en fonction de vos réponses, exécutera les commandes Postgres nécessaires pour créer un utilisateur selon vos spécifications. Pour ce tutoriel, créez un rôle nommé sammy et donnez-lui des privilèges de super-utilisateur en entrant y lorsque cela est nécessaire :

      Output

      Enter name of role to add: sammy Shall the new role be a superuser? (y/n) y

      Vous pouvez obtenir plus de contrôle en passant des indicateurs supplémentaires. Consultez les options en consultant la page de manuel de create user: 

      Votre installation de Postgres a maintenant un nouveau rôle, mais vous n’avez encore ajouté aucune base de données. La section suivante décrit ce processus.

      Étape 5 – Création d’une nouvelle base de données

      Une autre hypothèse posée par défaut par le système d’authentification Postgres est que tout rôle utilisé pour se connecter disposera d’une base de données du même nom à laquelle il pourra accéder.

      Cela signifie que si l’utilisateur que vous avez créé dans la dernière section est appelé sammy, ce rôle tentera de se connecter à une base de données qui est également appelée sammy par défaut. Vous pouvez créer une telle base de données avec la commande createdb.

      Si vous êtes connecté en tant que compte postgres, vous devez taper quelque chose comme :

      Si, au contraire, vous préférez utiliser sudo pour chaque commande sans quitter votre compte normal, vous devez taper :

      • sudo -u postgres createdb sammy

      Cette flexibilité offre de multiples façons de créer des bases de données au besoin.

      Maintenant que vous avez créé une nouvelle base de données, vous allez vous y connecter avec votre nouveau rôle.

      Étape 6 – Ouvrir une invite Postgres avec le nouveau rôle

      Pour vous connecter avec une authentification basée sur l’identité, vous aurez besoin d’un utilisateur Linux portant le même nom que votre rôle et votre base de données Postgres.

      Si vous n’avez pas d’utilisateur Linux correspondant disponible, vous pouvez en créer un avec la commande adduser. Vous devrez le faire à partir de votre compte non root avec privilèges sudo (c’est-à-dire sans être connecté en tant qu’utilisateur postgres) :

      Une fois ce nouveau compte disponible, vous pouvez basculer sur ce dernier et vous connecter à la base de données en tapant :

      Ou alors, vous pouvez le faire en ligne :

      Cette commande vous permettra de vous connecter automatiquement.

      Si vous souhaitez que votre utilisateur se connecte à une autre base de données, vous pouvez le faire en incluant le drapeau -d et en spécifiant la base de données, comme ceci : 

      Une fois connecté, vous pouvez vérifier vos informations de connexion actuelles en tapant :

      Cela donnera le résultat suivant :

      Output

      You are connected to database "sammy" as user "sammy" via socket in "/var/run/postgresql" at port "5432".

      Cette commande est utile si vous vous connectez à des bases de données qui ne sont pas celles par défaut ou avec des utilisateurs qui ne sont pas ceux par défaut.

      Après vous être connecté à votre base de données, vous pouvez maintenant essayer de créer et de supprimer des tables.

      Étape 7 – Créer et supprimer des tables

      Maintenant que vous savez comment vous connecter au système de base de données PostgreSQL, il est temps de découvrir quelques tâches de gestion basiques de Postgres.

      Tout d’abord, créez une table pour stocker certaines données. Par exemple, vous allez créer une table qui décrit certains équipements d’une aire de jeux.

      La syntaxe de base de cette commande est la suivante :

      CREATE TABLE table_name (
          column_name1 col_type (field_length) column_constraints,
          column_name2 col_type (field_length),
          column_name3 col_type (field_length)
      );
      

      Ces commandes donnent un nom à la table, puis définissent les colonnes ainsi que le type de colonne et la longueur maximale des données du champ. Vous pouvez également ajouter des contraintes de table pour chaque colonne.

      À des fins de démonstration, créez une simple table comme celle-ci :

      • CREATE TABLE playground (
      • equip_id serial PRIMARY KEY,
      • type varchar (50) NOT NULL,
      • color varchar (25) NOT NULL,
      • location varchar(25) check (location in ('north', 'south', 'west', 'east', 'northeast', 'southeast', 'southwest', 'northwest')),
      • install_date date
      • );

      Cette commande permet de créer une table d’inventaire des équipements de terrain de jeu. Elle commence par une identification de l’équipement, qui est de type série. Ce type de données est un entier auto-incrémenté. Vous avez également donné à cette colonne la contrainte de CLÉ PRIMAIRE ce qui signifie que les valeurs doivent être uniques et non nulles. 

      Pour deux des colonnes (equip_id et install_date), la commande ne spécifie pas de longueur de champ. En effet, certains types de colonnes n’ont pas besoin d’une longueur fixe car la longueur est déduite du type.

      Les deux lignes suivantes créent des colonnes pour l’équipement respectivement le type et color, chacune ne pouvant pas être vide. La ligne après ceux-ci crée une colonne location et une contrainte qui requiert que la valeur soit l’une des huit valeurs possibles. La dernière ligne crée une colonne de date qui enregistre la date à laquelle vous avez installé l’équipement.

      Vous pouvez voir votre nouvelle table en tapant :

      Cela donnera le résultat suivant :

      Output

      List of relations Schema | Name | Type | Owner --------+-------------------------+----------+------- public | playground | table | sammy public | playground_equip_id_seq | sequence | sammy (2 rows)

      Votre table « playground » est là, mais il y a aussi un élément nommé playground_equip_id_seq qui est du type sequence. Il s’agit d’une représentation du type de série que vous avez donné à votre colonne equip_id. Ceci permet de garder une trace du prochain numéro de la séquence et est créé automatiquement pour les colonnes de ce type.

      Si vous voulez voir uniquement la table sans la séquence, vous pouvez taper :

      Il en résultera ce qui suit :

      Output

      List of relations Schema | Name | Type | Owner --------+------------+-------+------- public | playground | table | sammy (1 row)

      Dans cette étape, vous avez créé un exemple de table. Dans l’étape suivante, vous allez essayer d’ajouter, d’interroger et de supprimer des entrées dans cette table.

      Étape 8 – Ajouter, interroger et supprimer des données dans une table

      Maintenant que vous avez une table, vous pouvez y insérer quelques données.

      Par exemple, ajoutez une diapositive et une balançoire en appelant la table à laquelle vous voulez ajouter, en nommant les colonnes, puis en fournissant des données pour chaque colonne, comme ceci :

      • INSERT INTO playground (type, color, location, install_date) VALUES ('slide', 'blue', 'south', '2017-04-28');
      • INSERT INTO playground (type, color, location, install_date) VALUES ('swing', 'yellow', 'northwest', '2018-08-16');

      Vous devez faire attention lors de la saisie des données afin d’éviter quelques problèmes courants. Pour commencer, les noms des colonnes ne doivent pas être mis entre guillemets, mais les valeurs des colonnes que vous saisissez doivent l’être.

      Une autre chose à garder à l’esprit est que vous n’entrez pas de valeur pour la colonne equip_id. En effet, il est généré automatiquement chaque fois qu’une nouvelle ligne est créée dans la table.

      Récupérez les informations que vous avez ajoutées en tapant :

      • SELECT * FROM playground;

      Vous verrez le résultat suivant :

      Output

      equip_id | type | color | location | install_date ----------+-------+--------+-----------+-------------- 1 | slide | blue | south | 2017-04-28 2 | swing | yellow | northwest | 2018-08-16 (2 rows)

      Ici, vous pouvez voir que votre equip_id a bien été renseigné et que toutes vos autres données ont été organisées correctement.

      Si le toboggan de l’aire de jeu se casse et que vous devez l’enlever, vous pouvez également enlever la ligne de votre table en tapant :

      • DELETE FROM playground WHERE type = 'slide';

      Interrogez à nouveau la table :

      • SELECT * FROM playground;

      Vous verrez ce qui suit :

      Output

      equip_id | type | color | location | install_date ----------+-------+--------+-----------+-------------- 2 | swing | yellow | northwest | 2018-08-16 (1 row)

      Remarquez que votre diapositive ne fait plus partie de la table.

      Maintenant que vous avez ajouté et supprimé des entrées dans votre table, vous pouvez essayer d’ajouter et de supprimer des colonnes.

      Étape 9 – Ajouter et supprimer des colonnes d’une table

      Après avoir créé une table, vous pouvez la modifier pour ajouter ou supprimer des colonnes. Ajoutez une colonne pour indiquer la dernière visite de maintenance pour chaque équipement en tapant :

      • ALTER TABLE playground ADD last_maint date;

      Si vous affichez à nouveau les informations de votre table, vous verrez que la nouvelle colonne a été ajoutée (mais qu’aucune donnée n’a été saisie) :

      • SELECT * FROM playground;

      Vous verrez ce qui suit :

      Output

      equip_id | type | color | location | install_date | last_maint ----------+-------+--------+-----------+--------------+------------ 2 | swing | yellow | northwest | 2018-08-16 | (1 row)

      La suppression d’une colonne est tout aussi simple. Si vous constatez que votre équipe de travail utilise un outil distinct pour suivre l’historique de la maintenance, vous pouvez supprimer la colonne en tapant :

      • ALTER TABLE playground DROP last_maint;

      Cela supprime la colonne last_maint et toutes les valeurs qui s’y trouvent, mais laisse toutes les autres données intactes.

      Maintenant que vous avez ajouté et supprimé des colonnes, vous pouvez essayer de mettre à jour les données existantes lors de la dernière étape.

      Étape 10 – Mise à à jour les données dans une table

      Jusqu’à présent, vous avez appris comment ajouter des enregistrements à une table et comment les supprimer, mais ce tutoriel n’a pas encore évoqué comment modifier des entrées existantes.

      Vous pouvez mettre à jour les valeurs d’une entrée existante en interrogeant l’enregistrement que vous souhaitez et en paramétrant la colonne sur la valeur que vous souhaitez utiliser. Vous pouvez interroger l’enregistrement swing (balançoire) ; cela correspondra à chaque balançoire de votre table et changera sa couleur en rouge :

      • UPDATE playground SET color = 'red' WHERE type = 'swing';

      Vous pouvez vérifier que l’opération a réussi en effectuant une nouvelle requête :

      • SELECT * FROM playground;

      Vous verrez ce qui suit :

      Output

      equip_id | type | color | location | install_date ----------+-------+-------+-----------+-------------- 2 | swing | red | northwest | 2010-08-16 (1 row)

      Comme vous pouvez le voir, votre balançoire est maintenant enregistrée comme étant rouge.

      Conclusion

      Vous êtes maintenant paramétré avec PostgreSQL sur votre serveur CentOS 8. Cependant, il y a encore beaucoup de choses à découvrir sur Postgres. Voici d’autres guides sur l’utilisation de Postgres :



      Source link