如何使用MongoDB Go驱动程序在MongoDB中使用Go

本文档是一篇关于如何使用MongoDB官方Go驱动程序开发任务管理器CLI程序的教程。首先介绍了MongoDB Go驱动程序的背景和用途,接着详细讲解了安装驱动程序、创建连接、执行CRUD操作的步骤。教程通过实例展示了如何创建任务、列出任务、完成任务、显示未完成任务、显示已完成任务和删除任务。文章适合Go开发者学习如何在Go环境中使用MongoDB。
摘要由CSDN通过智能技术生成

The author selected the Free Software Foundation to receive a donation as part of the Write for DOnations program.

作者选择了自由软件基金会作为Write for DOnations计划的一部分接受捐赠。

介绍 (Introduction)

After relying on community developed solutions for many years, MongoDB announced that they were working on an official driver for Go. In March 2019, this new driver reached a production-ready status with the release of v1.0.0 and has been updated continually since then.

在依靠社区开发的解决方案多年之后, MongoDB宣布他们正在为Go制定官方驱动程序。 在2019年3月,此新驱动程序在v1.0.0版本中达到了生产就绪状态此后一直在不断更新。

Like the other official MongoDB drivers, the Go driver is idiomatic to the Go programming language and provides an easy way to use MongoDB as the database solution for a Go program. It is fully integrated with the MongoDB API, and exposes all of the query, indexing, and aggregation features of the API, along with other advanced features. Unlike third-party libraries, it will be fully supported by MongoDB engineers so you can be assured of its continued development and maintenance.

像其他官方的MongoDB驱动程序一样, Go驱动程序是Go编程语言的惯用语,它提供了一种简单的方法来将MongoDB用作Go程序的数据库解决方案。 它与MongoDB API完全集成,并公开了API的所有查询,索引和聚合功能以及其他高级功能。 与第三方库不同,它将由MongoDB工程师完全支持,因此您可以放心其继续开发和维护。

In this tutorial you’ll get started with using the official MongoDB Go Driver. You’ll install the driver, connect to a MongoDB database, and perform several CRUD operations. In the process, you’ll create a task manager program for managing tasks through the command line.

在本教程中,您将开始使用官方的MongoDB Go驱动程序。 您将安装驱动程序,连接到MongoDB数据库,并执行一些CRUD操作。 在此过程中,您将创建一个任务管理器程序以通过命令行管理任务。

先决条件 (Prerequisites)

For this tutorial, you’ll need the following:

对于本教程,您将需要以下内容:

  • Go installed on your machine and a Go workspace configured following How To Install Go and Set Up a Local Programming Environment. In this tutorial, the project will be named tasker. You’ll need Go v1.11 or higher installed on your machine with Go Modules enabled.

    Go已安装在您的计算机上,并已按照如何安装Go和设置本地编程环境配置了Go工作区。 在本教程中,该项目将命名为tasker 。 您需要在启用了Go模块的计算机上安装Go v1.11或更高版本。

  • MongoDB installed for your operating system following How To Install MongoDB. MongoDB 2.6 or higher is the minimum version supported by the MongoDB Go driver.

    按照如何安装MongoDB为您的操作系统安装了MongoDB 。 MongoDB 2.6或更高版本是MongoDB Go驱动程序支持的最低版本。

If you’re using Go v1.11 or 1.12, ensure Go Modules is enabled by setting the GO111MODULE environment variable to on as shown following:

如果您使用的是Go v1.11或1.12,请通过将GO111MODULE环境变量设置为GO111MODULE来确保启用了Go模块on如下所示:

  • export GO111MODULE="on"

    导出GO111MODULE =“ on”

For more information on implementing environment variables, read this tutorial on How To Read and Set Environmental and Shell Variables.

有关实现环境变量的更多信息,请阅读有关如何读取和设置环境变量和Shell变量的本教程。

The commands and code shown in this guide were tested with Go v1.14.1 and MongoDB v3.6.3.

本指南中显示的命令和代码已通过Go v1.14.1和MongoDB v3.6.3进行了测试。

第1步-安装MongoDB Go驱动程序 (Step 1 — Installing the MongoDB Go Driver)

In this step, you’ll install the Go Driver package for MongoDB and import it into your project. You’ll also connect to your MongoDB database and check the status of the connection.

在此步骤中,您将安装MongoDB的Go Driver软件包并将其导入到您的项目中。 您还将连接到MongoDB数据库并检查连接状态。

Go ahead and create a new directory for this tutorial in your filesystem:

继续并在文件系统中为本教程创建一个新目录:

  • mkdir tasker

    mkdir Tasker

Once your project directory is set up, change into it with the following command:

设置项目目录后,请使用以下命令将其更改为:

  • cd tasker

    光盘任务

Next, initialize the Go project with a go.mod file. This file defines project requirements and locks dependencies to their correct versions:

接下来,使用go.mod文件初始化Go项目。 此文件定义了项目要求,并将依赖项锁定为其正确的版本:

  • go mod init

    进入mod init

If your project directory is outside the $GOPATH, you need to specify the import path for your module as follows:

如果您的项目目录在$GOPATH之外,则需要为模块指定导入路径,如下所示:

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

    进入mod init github.com/ <您的用户名> / tasker

At this point, your go.mod file will look like this:

此时,您的go.mod文件将如下所示:

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

go 1.14

Add the MongoDB Go Driver as a dependency for your project using following command:

使用以下命令将MongoDB Go驱动程序添加为项目的依赖项:

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

    去得到go.mongodb.org/mongo-driver

You’ll see output like the following:

您将看到类似以下的输出:


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

At this point, your go.mod file will look like this:

此时,您的go.mod文件将如下所示:

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

go 1.14

require go.mongodb.org/mongo-driver v1.3.1 // indirect

Next, create a main.go file in your project root and open it in your text editor:

接下来,在项目根目录中创建一个main.go文件,并在文本编辑器中将其打开:

  • nano main.go

    纳米main.go

To get started with the driver, import the following packages into your main.go file:

要开始使用驱动程序,请将以下软件包导入到main.go文件中:

main.go
main.go
package main

import (
    "context"
    "log"

    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

Here you add the mongo and options packages, which the MongoDB Go driver provides.

在这里,您将添加MongoDB Go驱动程序提供的mongooptions软件包。

Next, following your imports, create a new MongoDB client and connect to your running MongoDB server:

接下来,在导入之后,创建一个新的MongoDB客户端并连接到正在运行的MongoDB服务器:

main.go
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() accepts a Context and a options.ClientOptions object, which is used to set the connection string and other driver settings. You can visit the options package documentation to see what configuration options are available.

mongo.Connect()接受Contextoptions.ClientOptions对象,该对象用于设置连接字符串和其他驱动程序设置。 您可以访问选项包文档以查看可用的配置选项。

Context is like a timeout or deadline that indicates when an operation should stop running and return. It helps to prevent performance degradation on production systems when specific operations are running slow. In this code, you’re passing context.TODO() to indicate that you’re not sure what context to use right now, but you plan to add one in the future.

上下文就像一个超时或截止期限,指示操作何时应停止运行并返回。 当特定操作运行缓慢时,它有助于防止生产系统上的性能下降。 在此代码中,您传递了context.TODO()来指示您不确定现在使用哪种上下文,但是您计划在将来添加一个。

Next, let’s ensure that your MongoDB server was found and connected to successfully using the Ping method. Add the following code inside the init function:

接下来,让我们确保使用Ping方法找到了MongoDB服务器并将其成功连接。 在init函数内添加以下代码:

main.go
main.go
. . .
    log.Fatal(err)
  }

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

If there are any errors while connecting to the database, the program should crash while you try to fix the problem as there’s no point keeping the program running without an active database connection.

如果连接到数据库时出现任何错误,则在尝试解决该问题时该程序应崩溃,因为没有活动的数据库连接就无法保持该程序的运行。

Add the following code to create a database:

添加以下代码以创建数据库:

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

  collection = client.Database("tasker").Collection("tasks")
}

You create a tasker database and a task collection to store the tasks you’ll be creating. You also set up collection as a package-level variable so you can reuse the database connection throughout the package.

您创建一个tasker数据库和一个task集合以存储将要创建的任务。 您还可以将collection设置为程序包级变量,以便可以在整个程序包中重用数据库连接。

Save and exit the file.

保存并退出文件。

The full main.go at this point is as follows:

此时完整的main.go如下:

main.go
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")
}

You’ve set up your program to connect to your MongoDB server using the Go driver. In the next step, you’ll proceed with the creation of your task manager program.

您已经设置程序使用Go驱动程序连接到MongoDB服务器。 在下一步中,您将继续创建任务管理器程序。

第2步-创建CLI程序 (Step 2 — Creating a CLI Program)

In this step, you’ll install the well-known cli package to aid with the development of your task manager program. It provides an interface that you can take advantage of to rapidly create modern command line tools. For example, this package gives the ability to define subcommands for your program for a more git-like command line experience.

在此步骤中,您将安装著名的cli软件包以帮助您开发任务管理器程序。 它提供了一个界面,您可以利用它来快速创建现代的命令行工具。 例如,此软件包提供了为您的程序定义子命令的功能,以提供更像git的命令行体验。

Run the following command to add the package as a dependency:

运行以下命令以将软件包添加为依赖项:

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

    去获取github.com/urfave/cli/v2

Next, open up your main.go file again:

接下来,再次打开您的main.go文件:

  • nano main.go

    纳米main.go

Add the following highlighted code to your main.go file:

将以下突出显示的代码添加到您的main.go文件中:

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"
)
. . .

You import the cli package as mentioned. You also import the os package, which you’ll use to pass command line arguments to your program:

您按照提到的方式导入cli包。 您还将导入os软件包,该软件包将用于将命令行参数传递给程序:

Add the following code after your init function to create your CLI program and cause your code to compile:

init函数之后添加以下代码,以创建CLI程序并使代码编译:

main.go
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)
    }
}

This snippet creates a CLI program called tasker and adds a short usage description that will be printed out when you run the program. The Commands slice is where you’ll add commands for your program. The Run command parses the arguments slice to the appropriate command.

此代码段创建了一个称为tasker的CLI程序,并添加了简短的用法说明,该说明将在您运行该程序时打印出来。 您可以在“ Commands切片中为程序添加命令。 Run命令将参数切片解析为适当的命令。

Save and exit your file.

保存并退出文件。

Here’s the command you need to build and run the program:

这是构建和运行程序所需的命令:

  • go run main.go

    去运行main.go

You’ll see the following output:

您将看到以下输出:


   
   
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)

The program runs and shows help text, which is handy for learning about what the program can do, and how to use it.

该程序将运行并显示帮助文本,这对于了解程序可以做什么以及如何使用它非常有用。

In the next steps, you’ll improve the utility of your program by adding subcommands to help manage your tasks in MongoDB.

在接下来的步骤中,您将通过添加子命令来帮助管理MongoDB中的任务来改善程序的实用性。

第3步-创建任务 (Step 3 — Creating a Task)

In this step, you’ll add a subcommand to your CLI program using the cli package. At the end of this section, you’ll be able to add a new task to your MongoDB database by using a new add command in your CLI program.

在此步骤中,您将使用cli包将子命令添加到CLI程序。 在本节的最后,您将能够通过在CLI程序中使用新的add命令将新任务添加到MongoDB数据库中。

Begin by opening up your main.go file:

首先打开您的main.go文件:

  • nano main.go

    纳米main.go

Next, import the go.mongodb.org/mongo-driver/bson/primitive, time, and errors packages:

接下来,将go.mongodb.org/mongo-driver/bson/primitivetimeerrors的包:

main.go
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"
)
. . .

Then create a new struct to represent a single task in the database and insert it immediately preceding the main function:

然后创建一个新结构以表示数据库中的单个任务,并将其插入到main函数之前:

main.go
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"`
}
. . .

You use the primitive package to set the type of the ID of each task since MongoDB uses ObjectIDs for the _id field by default. Another default behavior of MongoDB is that the lowercase field name is used as the key for each exported field when it is being serialized, but this can be changed using bson struct tags.

由于MongoDB默认将ObjectID用于_id字段,因此您可以使用primitive包来设置每个任务的ID类型。 MongoDB的另一个默认行为是,在序列化每个导出字段时,将小写字段名称用作每个导出字段的键,但是可以使用bson struct标记对此进行更改。

Next, create a function that receives an instance of Task and saves it in the database. Add this snippet following the main function:

接下来,创建一个接收Task实例并将其保存在数据库中的函数。 在main功能后添加以下代码段:

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

The collection.InsertOne() method inserts the provided task in the database collection and returns the ID of the document that was inserted. Since you don’t need this ID, you discard it by assigning to the underscore operator.

collection.InsertOne()方法将提供的任务插入数据库集合中,并返回插入的文档的ID。 由于不需要此ID,因此可以通过分配给下划线运算符来丢弃它。

The next step is to add a new command to your task manager program for creating new tasks. Let’s call it add:

下一步是将新命令添加到任务管理器程序中,以创建新任务。 我们称它为add

main.go
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)
    }
}

Every new command that is added to your CLI program is placed inside the Commands slice. Each one consists of a name, usage description, and action. This is the code that will run upon command execution.

添加到CLI程序的每个新命令都放置在Commands切片内。 每一个都包含一个名称,用法说明和操作。 这是将在命令执行后运行的代码。

In this code, you collect the first argument to add and use it to set the Text property of a new Task instance while assigning the appropriate defaults for the other properties. The new task is subsequently passed on to createTask, which inserts the task into the database and returns nil if all goes well causing the command to exit.

在此代码中,您收集了第一个参数以add并使用它来设置新Task实例的Text属性,同时为其他属性分配适当的默认值。 新任务随后传递给createTask ,它将所有任务插入数据库,如果一切顺利,则返回nil ,导致命令退出。

Save and exit your file.

保存并退出文件。

Test it out by adding a few tasks using the add command. If successful, you’ll see no errors printed to your screen:

通过使用add命令添加一些任务来对其进行测试。 如果成功,您将不会在屏幕上看到任何错误:

  • go run main.go add "Learn Go"

    运行main.go添加“ Learn Go”
  • go run main.go add "Read a book"

    去运行main.go添加“读一本书”

Now that you can add tasks successfully, let’s implement a way to display all the tasks that you’ve added to the database.

现在您可以成功添加任务,让我们实现一种显示已添加到数据库中的所有任务的方法。

步骤4 —列出所有任务 (Step 4 — Listing all Tasks)

Listing the documents in a collection can be done using the collection.Find() method, which expects a filter as well as a pointer to a value into which the result can be decoded. Its return value is a Cursor, which provides a stream of documents that can be iterated over and decoded one at a time. The Cursor is then closed once it has been exhausted.

可以使用collection.Find()方法完成将文档列出在集合中的操作,该方法需要一个过滤器以及一个指向可以将结果解码为该值的指针。 它的返回值是Cursor ,它提供了可以一次迭代和解码一次的文档流。 一旦游标耗尽,则将其关闭。

Open your main.go file:

打开您的main.go文件:

  • nano main.go

    纳米main.go

Make sure to import the bson package:

确保导入bson包:

main.go
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"
)
. . .

Then create the following functions immediately after createTask:

然后在createTask之后立即创建以下函数:

main.go
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) is how documents are represented in a MongoDB database and the bson package is what helps us work with BSON objects in Go. The bson.D type used in the getAll() function represents a BSON document and it’s used where the order of the properties matter. By passing bson.D{{}} as your filter to filterTasks(), you’re indicating that you want to match all the documents in the collection.

BSON(二进制编码的JSON)是在MongoDB数据库中表示文档的方式,而bson包是帮助我们在Go中处理BSON对象的方式。 getAll()函数中使用的bson.D类型表示一个BSON文档,并且在属性顺序很重要的地方使用。 通过将bson.D{{}}作为过滤器传递给filterTasks() ,表明您要匹配集合中的所有文档。

In the filterTasks() function, you iterate over the Cursor returned by the collection.Find() method and decode each document into an instance of Task. Each Task is then appended to the slice of tasks created at the start of the function. Once the Cursor is exhausted, it is closed and the tasks slice is returned.

filterTasks()函数中,对collection.Find()方法返回的Cursor进行迭代,并将每个文档解码为Task的实例。 然后,将每个Task附加到在函数开始时创建的任务片中。 游标耗尽后,将其关闭并返回tasks片。

Before you create a command for listing all tasks, let’s create a helper function that takes a slice of tasks and prints to the standard output. You’ll be using the color package to colorize the output.

在创建列出所有任务的命令之前,让我们创建一个帮助程序函数,该函数将一部分tasks并打印到标准输出。 您将使用color包对输出进行着色。

Before you can use the this package, install it with:

使用此软件包之前,请使用以下命令进行安装:

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

    去获得gopkg.in/gookit/color.v1

You’ll see the following output:

您将看到以下输出:


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

And import it into your main.go file along with the fmt package:

并将其与fmt包一起导入到main.go文件中:

main.go
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"
)
. . .

Next, create a new printTasks function following your main function:

接下来,在您的main功能之后创建一个新的printTasks函数:

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

This printTasks function takes a slice of tasks, iterates over each one, and prints it out to the standard output using the green color to indicate completed tasks, and yellow for incomplete tasks.

printTasks函数采用tasks ,对每个tasks迭代,然后将其打印为标准输出,其中绿色表示已完成的任务,黄色表示未完成的任务。

Go ahead and add the following highlighted lines to create a new all command to the Commands slice. This command will print all added tasks to the standard output:

继续并添加以下突出显示的行,以在Commands切片中创建一个新的all命令。 此命令会将所有添加的任务打印到标准输出:

main.go
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)
    }
}

. . .

The all command retrieves all the tasks present in the database and prints them to the standard output. If no tasks are present, a prompt to add a new task is printed instead.

all命令检索数据库中存在的所有任务,并将它们打印到标准输出。 如果没有任务,则会提示添加新任务。

Save and exit your file.

保存并退出文件。

Build and run your program with the all command:

使用all命令生成并运行程序:

  • go run main.go all

    去运行main.go all

It will list all the tasks that you’ve added so far:

它将列出您到目前为止添加的所有任务:


   
   
Output
1: Learn Go 2: Read a book

Now that you can view all the tasks in the database, let’s add the ability to mark a task as completed in the next step.

现在您可以查看数据库中的所有任务,让我们在下一步中添加将任务标记为已完成的功能。

第5步-完成任务 (Step 5 — Completing a Task)

In this step, you’ll create a new subcommand called done that will allow you to mark an existing task in the database as completed. To mark a task as completed, you can use the collection.FindOneAndUpdate() method. It allows you to locate a document in a collection and update some or all of its properties. This method requires a filter to locate the document and an update document to describe the operation. Both of these are built using bson.D types.

在此步骤中,您将创建一个名为done的新子命令,该子命令将允许您将数据库中的现有任务标记为已完成。 要将任务标记为已完成,可以使用collection.FindOneAndUpdate()方法。 它使您可以在集合中找到文档并更新其某些或全部属性。 此方法需要一个过滤器来定位文档,并需要一个更新文档来描述操作。 这两个都是使用bson.D类型构建的。

Start by opening up your main.go file:

首先打开main.go文件:

  • nano main.go

    纳米main.go

Insert the following snippet following your filterTasks function:

filterTasks函数之后插入以下代码段:

main.go
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)
}
. . .

The function matches the first document where the text property is equal to the text parameter. The update document specifies that the completed property be set to true. If there’s an error in the FindOneAndUpdate() operation, it will be returned by completeTask(). Otherwise nil is returned.

该函数与text属性等于text参数的第一个文档匹配。 update文档指定将completed属性设置为true 。 如果FindOneAndUpdate()操作中有错误,它将由completeTask()返回。 否则返回nil

Next, let’s add a new done command to your CLI program that marks a task as completed:

接下来,让我们向CLI程序添加一个新的done命令,将一个任务标记为已完成:

main.go
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)
    }
}

. . .

You use the argument passed to the done command to find the first document whose text property matches. If found, the completed property on the document is set to true.

您可以使用传递给done命令的参数来查找text属性匹配的第一个文档。 如果找到,则文档上的completed属性设置为true

Save and exit your file.

保存并退出文件。

Then run your program with the done command:

然后使用done命令运行程序:

  • go run main.go done "Learn Go"

    去运行main.go完成“学习去”

If you use the all command again, you will notice that the task that was marked as completed is now printed with green.

如果再次使用all命令,则会注意到标记为已完成的任务现在以绿色打印。

  • go run main.go all

    去运行main.go all

Sometimes, you only want to view tasks that have not yet been done. We’ll add that feature next.

有时,您只想查看尚未完成的任务。 接下来,我们将添加该功能。

步骤6 —仅显示待处理任务 (Step 6 — Displaying Pending Tasks Only)

In this step, you’ll incorporate code to retrieve pending tasks from the database using the MongoDB driver. Pending tasks are those whose completed property is set to false.

在此步骤中,您将合并代码以使用MongoDB驱动程序从数据库中检索待处理的任务。 待完成的任务是那些其completed属性设置为false

Let’s add a new function that retrieves tasks that have not been completed yet. Open your main.go file:

让我们添加一个新函数,以检索尚未完成的任务。 打开您的main.go文件:

  • nano main.go

    纳米main.go

Then add this snippet following the completeTask function:

然后在completeTask函数之后添加此代码段:

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

    return filterTasks(filter)
}
. . .

You create a filter using the bson and primitive packages from the MongoDB driver, which will match documents whose completed property is set to false. The slice of pending tasks is then returned to the caller.

您可以使用MongoDB驱动程序中的bsonprimitive包创建过滤器,该过滤器将匹配其completed属性设置为false文档。 然后,待处理的任务片将返回给调用方。

Instead of creating a new command to list pending tasks, let’s make it the default action when running the program without any commands. You can do this by adding an Action property to the program as follows:

让我们将其设置为不带任何命令运行程序时的默认操作,而不是创建一个新命令来列出未决任务。 您可以通过将Action属性添加到程序中来做到这一点,如下所示:

main.go
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)
                },
            },
. . .

The Action property performs a default action when the program is executed without any subcommands. This is where logic for listing pending tasks is placed. The getPending() function is called and the resulting tasks are printed to the standard output using printTasks(). If there are no pending tasks, a prompt is displayed instead, encouraging the user to add a new task using the add command.

在没有任何子命令的情况下执行程序时, Action属性将执行默认操作。 这是列出挂起的任务的逻辑所在。 getPending()函数,并使用printTasks()将生成的任务打印到标准输出。 如果没有待处理的任务,则会显示提示,鼓励用户使用add命令添加新任务。

Save and exit your file.

保存并退出文件。

Running the program now without adding any commands will list all pending tasks in the database:

现在运行该程序而不添加任何命令将列出数据库中所有待处理的任务:

  • go run main.go

    去运行main.go

You’ll see the following output:

您将看到以下输出:


   
   
Output
1: Read a book

Now that you can list incomplete tasks, let’s add another command that allows you to view completed tasks only.

现在您可以列出未完成的任务,让我们添加另一个命令,该命令仅允许您查看已完成的任务。

步骤7 —显示完成的任务 (Step 7 — Displaying Finished Tasks)

In this step, you’ll add a new finished subcommand that fetches completed tasks from the database and displays them on the screen. This involves filtering and returning tasks whose completed property is set to true.

在此步骤中,您将添加一个新的finished子命令,该命令将从数据库中获取已完成的任务并将其显示在屏幕上。 这涉及过滤和返回其completed属性设置为true任务。

Open your main.go file:

打开您的main.go文件:

  • nano main.go

    纳米main.go

Then add in the following code at the end of your file:

然后在文件末尾添加以下代码:

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

    return filterTasks(filter)
}
. . .

Similar to the getPending() function, you’ve added a getFinished() function that returns a slice of completed tasks. In this case, the filter has the completed property set to true so only the documents that match this condition will be returned.

getPending()函数类似,您添加了一个getFinished()函数,该函数返回一部分已完成的任务。 在这种情况下,过滤器会将completed属性设置为true因此仅返回与此条件匹配的文档。

Next, create a finished command that prints all completed tasks:

接下来,创建一个finished命令,打印所有完成的任务:

main.go
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)
    }
}
. . .

The finished command retrieves tasks whose completed property is set to true via the getFinished() function created here. It then passes it to the printTasks function so that they are printed to the standard output.

finished命令检索任务,其completed属性设置为true通过getFinished()在这里创建功能。 然后将其传递给printTasks函数,以便将它们打印到标准输出。

Save and exit your file.

保存并退出文件。

Run the following command:

运行以下命令:

  • go run main.go finished

    去运行main.go完成

You’ll see the following output:

您将看到以下输出:


   
   
Output
1: Learn Go

In the final step, you’ll give users the option to delete tasks from the database.

在最后一步,您将为用户提供从数据库中删除任务的选项。

第8步-删除任务 (Step 8 — Deleting a Task)

In this step, you’ll add a new delete subcommand to allow users to delete a task from the database. To delete a single task, you’ll use the collection.DeleteOne() method from the MongoDB driver. It also relies on a filter to match the document to delete.

在此步骤中,您将添加一个新的delete子命令,以允许用户从数据库中删除任务。 要删除单个任务,您将使用MongoDB驱动程序中的collection.DeleteOne()方法。 它还依赖于筛选器来匹配要删除的文档。

Open your main.go file once more:

再次打开您的main.go文件:

  • nano main.go

    纳米main.go

Add this deleteTask function to delete tasks from the database straight after your getFinished function:

添加以下deleteTask函数可在getFinished函数之后getFinished从数据库中删除任务:

main.go
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
}
. . .

This deleteTask method takes a string argument that represents the task item to be deleted. A filter is constructed to match the task item whose text property is set to the string argument. You pass the filter to the DeleteOne() method that matches the item in the collection and deletes it.

deleteTask方法采用一个字符串参数,该参数表示要删除的任务项。 构造一个过滤器以匹配其text属性设置为string参数的任务项。 您将筛选器传递DeleteOne()集合中的项目匹配的DeleteOne()方法,然后将其删除。

You can check the DeletedCount property on the result from the DeleteOne method to confirm if any documents were deleted. If the filter is unable to match a document to be deleted, the DeletedCount will be zero and you can return an error in that case.

您可以在DeleteOne方法的结果上检查DeletedCount属性,以确认是否删除了任何文档。 如果过滤器无法匹配要删除的文档,则DeletedCount将为零,并且在这种情况下可以返回错误。

Now add a new rm command as highlighted:

现在添加一个新的rm命令,突出显示:

main.go
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)
    }
}
. . .

Just as with all the other subcommands added previously, the rm command uses its first argument to match a task in the database and deletes it.

就像先前添加的所有其他子命令一样, rm命令使用其第一个参数来匹配数据库中的任务并将其删除。

Save and exit your file.

保存并退出文件。

You can list pending tasks by running your program without passing any subcommands:

您可以通过运行程序列出挂起的任务,而无需传递任何子命令:

  • go run main.go

    去运行main.go

   
   
Output
1: Read a book

Running the rm subcommand on the "Read a book" task will delete it from the database:

"Read a book"任务上运行rm子命令会将其从数据库中删除:

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

    去运行main.go rm“读一本书”

If you list all pending tasks again, you’ll notice that the "Read a book" task does not appear anymore and a prompt to add a new task is shown instead:

如果再次列出所有待处理的任务,您会注意到"Read a book"任务不再出现,而是显示添加新任务的提示:

  • go run main.go

    去运行main.go

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

In this step you added a function to delete tasks from the database.

在此步骤中,您添加了一个从数据库中删除任务的功能。

结论 (Conclusion)

You’ve successfully created a task manager command line program and learned the fundamentals of using the MongoDB Go driver in the process.

您已经成功创建了任务管理器命令行程序,并了解了在此过程中使用MongoDB Go驱动程序的基础知识。

Be sure to check out the full documentation for the MongoDB Go Driver at GoDoc to learn more about the features that using the driver provides. The documentation that describes using aggregations or transactions may be of particular interest to you.

请确保在GoDoc上查看MongoDB Go驱动程序的完整文档,以了解有关使用该驱动程序提供的功能的更多信息。 您可能会对描述使用聚合事务的文档特别感兴趣。

The final code for this tutorial can be viewed in this GitHub repo.

可以在此GitHub存储库中查看本教程的最终代码。

翻译自: https://www.digitalocean.com/community/tutorials/how-to-use-go-with-mongodb-using-the-mongodb-go-driver

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值