作者选择了自由软件基金会作为 写给捐款计划的一部分接受捐款。
介绍
在多年来依赖社区开发的解决方案之后,MongoDB宣布(https://engineering.mongodb.com/post/considering-the-community-effects-of-introducing-an-official-golang-mongodb-driver)正在为Go开发一个官方驱动程序,2019年3月,这个新驱动程序达到生产准备状态(随着v1.0.0的发布)(https://github.com/mongodb/mongo-go-driver/releases/tag/v1.0.0)并自那时以来一直在不断更新。
与其他官方 MongoDB 驱动程序一样,Go 驱动程序(https://github.com/mongodb/mongo-go-driver)与 Go 编程语言相同,提供了一个简单的方法来使用 MongoDB 作为 Go 程序的数据库解决方案。它与 MongoDB API 完全集成,并暴露了 API 的所有查询、索引和汇总功能,以及其他高级功能。
在本教程中,您将开始使用官方 MongoDB Go 驱动程序. 您将安装驱动程序,连接到 MongoDB 数据库,并执行多个 CRUD 操作。
前提条件
对于本教程,您将需要以下内容:
Go 安装在您的机器上,并根据 如何安装 Go 和设置本地编程环境配置一个 Go 工作区。 在本教程中,该项目将被命名为
tasker
. 你需要 Go v1.11 或更高版本安装在您的机器上,有 Go 模块启用。MongoDB 安装在您的操作系统上,如下 如何安装 MongoDB。
如果您正在使用 Go v1.11 或 1.12,请确保通过将GO111MODULE
环境变量设置为启用
,如下所示:
1export GO111MODULE="on"
有关实施环境变量的更多信息,请阅读本教程中的 如何阅读和设置环境和壳变量。
本指南中显示的命令和代码已在 Go v1.14.1 和 MongoDB v3.6.3 中测试。
步骤1:安装 MongoDB Go 驱动程序
在此步骤中,您将安装 MongoDB 的 Go Driver 包,并将其导入您的项目,您还将连接到您的 MongoDB 数据库并检查连接的状态。
继续,并在您的文件系统中创建此教程的新目录:
1mkdir tasker
一旦您的项目目录已设置,请使用以下命令更改它:
1cd tasker
接下来,用 go.mod 文件初始化 Go 项目,该文件定义了项目的要求,并将依赖性锁定为正确的版本:
1go mod init
如果您的项目目录不在$GOPATH
之外,则需要如下指定模块的导入路径:
1go mod init github.com/<your_username>/tasker
在此时,你的 go.mod 文件将看起来像这样:
1[label go.mod]
2module github.com/<your_username>/tasker
3
4go 1.14
使用以下命令为您的项目添加 MongoDB Go 驱动程序作为依赖:
1go get go.mongodb.org/mongo-driver
你会看到输出如下:
1[secondary_label Output]
2go: downloading go.mongodb.org/mongo-driver v1.3.2
3go: go.mongodb.org/mongo-driver upgrade => v1.3.2
在此时,你的 go.mod 文件将看起来像这样:
1[label go.mod]
2module github.com/<your_username>/tasker
3
4go 1.14
5
6require go.mongodb.org/mongo-driver v1.3.1 // indirect
接下来,在项目根中创建一个main.go
文件,并在文本编辑器中打开它:
1nano main.go
要开始使用驱动程序,请将下列包导入到您的 main.go 文件中:
1[label main.go]
2package main
3
4import (
5 "context"
6 "log"
7
8 "go.mongodb.org/mongo-driver/mongo"
9 "go.mongodb.org/mongo-driver/mongo/options"
10)
在这里,您添加了 MongoDB Go 驱动程序提供的 mongo
和 options
包。
接下来,按照您的导入,创建一个新的 MongoDB 客户端,并连接到正在运行的 MongoDB 服务器:
1[label main.go]
2. . .
3var collection *mongo.Collection
4var ctx = context.TODO()
5
6func init() {
7 clientOptions := options.Client().ApplyURI("mongodb://localhost:27017/")
8 client, err := mongo.Connect(ctx, clientOptions)
9 if err != nil {
10 log.Fatal(err)
11 }
12}
mongo.Connect()
接受一个 Context
和一个 options.ClientOptions
对象,用于设置连接字符串和其他驱动程序设置。
Context就像一个时间或截止日期,指示一个操作何时停止运行并返回。它有助于防止特定操作运行缓慢时的生产系统的性能下降。
接下来,让我们确保您的 MongoDB 服务器被找到并成功连接到使用Ping
方法。
1[label main.go]
2. . .
3 log.Fatal(err)
4 }
5
6 err = client.Ping(ctx, nil)
7 if err != nil {
8 log.Fatal(err)
9 }
10}
如果连接到数据库时出现任何错误,程序应该在尝试修复问题时崩溃,因为没有活跃数据库连接的情况下保持程序运行没有意义。
添加以下代码来创建数据库:
1[label main.go]
2. . .
3 err = client.Ping(ctx, nil)
4 if err != nil {
5 log.Fatal(err)
6 }
7
8 collection = client.Database("tasker").Collection("tasks")
9}
您创建一个任务
数据库和一个任务
集合来存储您正在创建的任务,您还将收藏
设置为包级变量,以便在整个包中重复使用数据库连接。
保存和退出文件。
此时的main.go
全文如下:
1[label main.go]
2package main
3
4import (
5 "context"
6 "log"
7
8 "go.mongodb.org/mongo-driver/mongo"
9 "go.mongodb.org/mongo-driver/mongo/options"
10)
11
12var collection *mongo.Collection
13var ctx = context.TODO()
14
15func init() {
16 clientOptions := options.Client().ApplyURI("mongodb://localhost:27017/")
17 client, err := mongo.Connect(ctx, clientOptions)
18 if err != nil {
19 log.Fatal(err)
20 }
21
22 err = client.Ping(ctx, nil)
23 if err != nil {
24 log.Fatal(err)
25 }
26
27 collection = client.Database("tasker").Collection("tasks")
28}
你已经设置了你的程序连接到你的 MongoDB 服务器使用 Go 驱动程序. 在下一步,你将继续创建你的任务管理程序。
步骤 2:创建一个 CLI 计划
在此步骤中,您将安装已知的 cli
包,以帮助开发您的任务管理程序. 它提供了您可以快速创建现代命令行工具的接口。
运行以下命令以将包添加为依赖:
1go get github.com/urfave/cli/v2
接下来,重新打开您的 main.go 文件:
1nano main.go
将以下突出代码添加到您的 main.go 文件中:
1[label main.go]
2package main
3
4import (
5 "context"
6 "log"
7 "os"
8
9 "github.com/urfave/cli/v2"
10 "go.mongodb.org/mongo-driver/mongo"
11 "go.mongodb.org/mongo-driver/mongo/options"
12)
13. . .
您还将导入os
包,您将使用该包将命令行参数传递给您的程序:
在init
函数后添加以下代码来创建您的 CLI 程序,并使您的代码编译:
1[label main.go]
2. . .
3func main() {
4 app := &cli.App{
5 Name: "tasker",
6 Usage: "A simple CLI program to manage your tasks",
7 Commands: []*cli.Command{},
8 }
9
10 err := app.Run(os.Args)
11 if err != nil {
12 log.Fatal(err)
13 }
14}
此片段创建了一个名为tasker
的 CLI 程序,并添加一个简短的使用描述,在运行该程序时将被打印出来。
保存和退出您的文件。
以下是您需要构建和运行该程序的命令:
1go run main.go
您将看到以下输出:
1[secondary_label Output]
2NAME:
3 tasker - A simple CLI program to manage your tasks
4
5USAGE:
6 main [global options] command [command options] [arguments...]
7
8COMMANDS:
9 help, h Shows a list of commands or help for one command
10
11GLOBAL OPTIONS:
12 --help, -h show help (default: false)
该程序运行并显示帮助文本,这是方便的学习该程序可以做什么,以及如何使用它。
在接下来的步骤中,您将通过添加子命令来改善程序的实用性,以帮助管理 MongoDB 中的任务。
步骤三:创建一个任务
在此步骤中,您将使用cli
包添加一个子命令到您的 CLI 程序. 在本节的末尾,您将能够通过在您的 CLI 程序中使用一个新的add
命令添加一个新的任务到您的 MongoDB 数据库。
首先,打开你的 main.go 文件:
1nano main.go
接下来,输入 go.mongodb.org/mongo-driver/bson/primitive
, time
和 errors
的包裹:
1[label main.go]
2package main
3
4import (
5 "context"
6 "errors"
7 "log"
8 "os"
9 "time"
10
11 "github.com/urfave/cli/v2"
12 "go.mongodb.org/mongo-driver/bson/primitive"
13 "go.mongodb.org/mongo-driver/mongo"
14 "go.mongodb.org/mongo-driver/mongo/options"
15)
16. . .
然后创建一个新的结构,以代表数据库中的单个任务,并将其插入在主要
函数之前:
1[label main.go]
2. . .
3type Task struct {
4 ID primitive.ObjectID `bson:"_id"`
5 CreatedAt time.Time `bson:"created_at"`
6 UpdatedAt time.Time `bson:"updated_at"`
7 Text string `bson:"text"`
8 Completed bool `bson:"completed"`
9}
10. . .
您使用原始
包来设置每个任务的ID类型,因为MongoDB默认情况下使用ObjectID
用于_id
字段。MongoDB的另一个默认行为是,下级字段名称在进行序列化时被用作每个导出字段的密钥,但可以使用bson
结构标签进行更改。
接下来,创建一个函数,该函数接收一个任务
的实例并将其保存到数据库中。
1[label main.go]
2. . .
3func createTask(task *Task) error {
4 _, err := collection.InsertOne(ctx, task)
5 return err
6}
7. . .
collection.InsertOne()
方法将所提供的任务插入数据库集合,并返回已插入的文档的 ID。
下一步是将新命令添加到你的任务管理程序中,以创建新任务。
1[label main.go]
2. . .
3func main() {
4 app := &cli.App{
5 Name: "tasker",
6 Usage: "A simple CLI program to manage your tasks",
7 Commands: []*cli.Command{
8 {
9 Name: "add",
10 Aliases: []string{"a"},
11 Usage: "add a task to the list",
12 Action: func(c *cli.Context) error {
13 str := c.Args().First()
14 if str == "" {
15 return errors.New("Cannot add an empty task")
16 }
17
18 task := &Task{
19 ID: primitive.NewObjectID(),
20 CreatedAt: time.Now(),
21 UpdatedAt: time.Now(),
22 Text: str,
23 Completed: false,
24 }
25
26 return createTask(task)
27 },
28 },
29 },
30 }
31
32 err := app.Run(os.Args)
33 if err != nil {
34 log.Fatal(err)
35 }
36}
添加到您的 CLI 程序的每个新命令都放置在命令
片段中,每个命令都包含一个名称、使用描述和操作。
在此代码中,您收集添加
的第一个参数,并使用它来设置一个新的任务
实例的文本
属性,同时为其他属性分配适当的默认值。
保存和退出您的文件。
通过使用添加
命令添加几个任务来测试它,如果成功,您将看到没有任何错误打印到您的屏幕上:
1go run main.go add "Learn Go"
2go run main.go add "Read a book"
现在您可以成功地添加任务,让我们实施一种方式来显示您已添加到数据库的所有任务。
步骤4:列出所有任务
可以使用collection.Find()
方法列出集合中的文档,该方法预期将一个过滤器以及一个指针列为可以解码结果的值。 其返回值是 Cursor,该值提供了可以重复和一次解码的文档流。
打开您的 main.go 文件:
1nano main.go
请确保导入 bson
包:
1[label main.go]
2package main
3
4import (
5 "context"
6 "errors"
7 "log"
8 "os"
9 "time"
10
11 "github.com/urfave/cli/v2"
12 "go.mongodb.org/mongo-driver/bson"
13 "go.mongodb.org/mongo-driver/bson/primitive"
14 "go.mongodb.org/mongo-driver/mongo"
15 "go.mongodb.org/mongo-driver/mongo/options"
16)
17. . .
然后在createTask
后立即创建以下函数:
1[label main.go]
2. . .
3func getAll() ([]*Task, error) {
4 // passing bson.D{{}} matches all documents in the collection
5 filter := bson.D{{}}
6 return filterTasks(filter)
7}
8
9func filterTasks(filter interface{}) ([]*Task, error) {
10 // A slice of tasks for storing the decoded documents
11 var tasks []*Task
12
13 cur, err := collection.Find(ctx, filter)
14 if err != nil {
15 return tasks, err
16 }
17
18 for cur.Next(ctx) {
19 var t Task
20 err := cur.Decode(&t)
21 if err != nil {
22 return tasks, err
23 }
24
25 tasks = append(tasks, &t)
26 }
27
28 if err := cur.Err(); err != nil {
29 return tasks, err
30 }
31
32 // once exhausted, close the cursor
33 cur.Close(ctx)
34
35 if len(tasks) == 0 {
36 return tasks, mongo.ErrNoDocuments
37 }
38
39 return tasks, nil
40}
BSON(二进制编码的JSON)是如何在MongoDB数据库中表示文档的,而bson
包是帮助我们在Go中使用BSON对象的。
在FilterTasks()
函数中,您重复通过collection.Find()
方法返回的标记,并将每个文档解码为一个Task
实例。
在创建列出所有任务的命令之前,让我们创建一个帮助函数,该函数将一小块任务
和打印到标准输出中。
在您可以使用此包之前,请将其安装在:
1go get gopkg.in/gookit/color.v1
您将看到以下输出:
1[secondary_label Output]
2go: downloading gopkg.in/gookit/color.v1 v1.1.6
3go: gopkg.in/gookit/color.v1 upgrade => v1.1.6
然后将其导入到您的 main.go 文件中,并将其导入到 fmt 包中:
1[label main.go]
2package main
3
4import (
5 "context"
6 "errors"
7 "fmt"
8 "log"
9 "os"
10 "time"
11
12 "github.com/urfave/cli/v2"
13 "go.mongodb.org/mongo-driver/bson"
14 "go.mongodb.org/mongo-driver/bson/primitive"
15 "go.mongodb.org/mongo-driver/mongo"
16 "go.mongodb.org/mongo-driver/mongo/options"
17 "gopkg.in/gookit/color.v1"
18)
19. . .
接下来,创建一个新的printTasks
函数,按照你的main
函数:
1[label main.go]
2. . .
3func printTasks(tasks []*Task) {
4 for i, v := range tasks {
5 if v.Completed {
6 color.Green.Printf("%d: %s\n", i+1, v.Text)
7 } else {
8 color.Yellow.Printf("%d: %s\n", i+1, v.Text)
9 }
10 }
11}
12. . .
此打印任务
函数采用一个任务
片段,重复每个任务,并将其打印到标准输出中,使用绿色表示已完成的任务,黄色表示未完成的任务。
继续,添加以下突出的行,以在命令
片段中创建一个新的所有
命令。
1[label main.go]
2. . .
3func main() {
4 app := &cli.App{
5 Name: "tasker",
6 Usage: "A simple CLI program to manage your tasks",
7 Commands: []*cli.Command{
8 {
9 Name: "add",
10 Aliases: []string{"a"},
11 Usage: "add a task to the list",
12 Action: func(c *cli.Context) error {
13 str := c.Args().First()
14 if str == "" {
15 return errors.New("Cannot add an empty task")
16 }
17
18 task := &Task{
19 ID: primitive.NewObjectID(),
20 CreatedAt: time.Now(),
21 UpdatedAt: time.Now(),
22 Text: str,
23 Completed: false,
24 }
25
26 return createTask(task)
27 },
28 },
29 {
30 Name: "all",
31 Aliases: []string{"l"},
32 Usage: "list all tasks",
33 Action: func(c *cli.Context) error {
34 tasks, err := getAll()
35 if err != nil {
36 if err == mongo.ErrNoDocuments {
37 fmt.Print("Nothing to see here.\nRun `add 'task'` to add a task")
38 return nil
39 }
40
41 return err
42 }
43
44 printTasks(tasks)
45 return nil
46 },
47 },
48 },
49 }
50
51 err := app.Run(os.Args)
52 if err != nil {
53 log.Fatal(err)
54 }
55}
56
57. . .
所有
命令会检索数据库中的所有任务并将其打印到标准输出中,如果没有任务,则会打印添加新任务的提示。
保存和退出您的文件。
用全部
命令构建和运行您的程序:
1go run main.go all
它会列出您迄今为止添加的所有任务:
1[secondary_label Output]
21: Learn Go
32: Read a book
现在您可以查看数据库中的所有任务,让我们在下一步中添加标记任务完成的能力。
步骤五:完成任务
在此步骤中,您将创建一个名为完成
的新子命令,允许您将数据库中的现有任务标记为完成。 要将任务标记为完成,您可以使用collection.FindOneAndUpdate()
方法。 它允许您在集合中定位文档并更新其部分或全部属性。 此方法需要一个过滤器来定位文档和更新文档来描述操作。
首先,打开你的 main.go 文件:
1nano main.go
插入以下片段,按照您的filterTasks
函数:
1[label main.go]
2. . .
3func completeTask(text string) error {
4 filter := bson.D{primitive.E{Key: "text", Value: text}}
5
6 update := bson.D{primitive.E{Key: "$set", Value: bson.D{
7 primitive.E{Key: "completed", Value: true},
8 }}}
9
10 t := &Task{}
11 return collection.FindOneAndUpdate(ctx, filter, update).Decode(t)
12}
13. . .
该函数与第一个文本属性等于文本
参数的文档相匹配。更新
文档指定完成
属性设置为真
。如果在FindOneAndUpdate()
操作中出现错误,则将返回completeTask()
。
接下来,让我们将一个新的完成
命令添加到您的 CLI 程序中,该命令将任务标记为完成:
1[label main.go]
2. . .
3func main() {
4 app := &cli.App{
5 Name: "tasker",
6 Usage: "A simple CLI program to manage your tasks",
7 Commands: []*cli.Command{
8 {
9 Name: "add",
10 Aliases: []string{"a"},
11 Usage: "add a task to the list",
12 Action: func(c *cli.Context) error {
13 str := c.Args().First()
14 if str == "" {
15 return errors.New("Cannot add an empty task")
16 }
17
18 task := &Task{
19 ID: primitive.NewObjectID(),
20 CreatedAt: time.Now(),
21 UpdatedAt: time.Now(),
22 Text: str,
23 Completed: false,
24 }
25
26 return createTask(task)
27 },
28 },
29 {
30 Name: "all",
31 Aliases: []string{"l"},
32 Usage: "list all tasks",
33 Action: func(c *cli.Context) error {
34 tasks, err := getAll()
35 if err != nil {
36 if err == mongo.ErrNoDocuments {
37 fmt.Print("Nothing to see here.\nRun `add 'task'` to add a task")
38 return nil
39 }
40
41 return err
42 }
43
44 printTasks(tasks)
45 return nil
46 },
47 },
48 {
49 Name: "done",
50 Aliases: []string{"d"},
51 Usage: "complete a task on the list",
52 Action: func(c *cli.Context) error {
53 text := c.Args().First()
54 return completeTask(text)
55 },
56 },
57 },
58 }
59
60 err := app.Run(os.Args)
61 if err != nil {
62 log.Fatal(err)
63 }
64}
65
66. . .
使用完成
命令传递的参数来查找第一个与文本
属性匹配的文档。
保存和退出您的文件。
然后用完成
命令运行您的程序:
1go run main.go done "Learn Go"
如果您再次使用所有
命令,您会注意到已标记为完成的任务现在打印为绿色。
1go run main.go all
有时,您只需要查看尚未完成的任务,我们将下次添加该功能。
步骤 6 – 显示只待执行的任务
在此步骤中,您将嵌入代码,以使用 MongoDB 驱动程序从数据库中获取等待任务。
让我们添加一个新的函数来检索尚未完成的任务。
1nano main.go
然后在completeTask
函数下添加此片段:
1[label main.go]
2. . .
3func getPending() ([]*Task, error) {
4 filter := bson.D{
5 primitive.E{Key: "completed", Value: false},
6 }
7
8 return filterTasks(filter)
9}
10. . .
您使用 MongoDB 驱动程序的bson
和primitive
包创建过滤器,将匹配其完成
属性设置为false
的文档。
不要创建一个新的命令来列出等待任务,让我们在没有任何命令的情况下运行程序时将其作为默认操作。
1[label main.go]
2. . .
3func main() {
4 app := &cli.App{
5 Name: "tasker",
6 Usage: "A simple CLI program to manage your tasks",
7 Action: func(c *cli.Context) error {
8 tasks, err := getPending()
9 if err != nil {
10 if err == mongo.ErrNoDocuments {
11 fmt.Print("Nothing to see here.\nRun `add 'task'` to add a task")
12 return nil
13 }
14
15 return err
16 }
17
18 printTasks(tasks)
19 return nil
20 },
21 Commands: []*cli.Command{
22 {
23 Name: "add",
24 Aliases: []string{"a"},
25 Usage: "add a task to the list",
26 Action: func(c *cli.Context) error {
27 str := c.Args().First()
28 if str == "" {
29 return errors.New("Cannot add an empty task")
30 }
31
32 task := &Task{
33 ID: primitive.NewObjectID(),
34 CreatedAt: time.Now(),
35 UpdatedAt: time.Now(),
36 Text: str,
37 Completed: false,
38 }
39
40 return createTask(task)
41 },
42 },
43. . .
当程序在没有任何子命令的情况下执行默认操作时,该Action
属性会执行默认操作. 此处放置了列出等待任务的逻辑。 呼叫了getPending()
函数,并使用printTasks()
打印到结果的标准输出中。
保存和退出您的文件。
现在运行该程序而不添加任何命令将列出数据库中的所有等待任务:
1go run main.go
您将看到以下输出:
1[secondary_label Output]
21: Read a book
现在您可以列出不完整的任务,让我们添加另一个命令,允许您只查看已完成的任务。
步骤 7 – 显示完成的任务
在此步骤中,您将添加一个新的完成
子命令,从数据库中获取已完成的任务并将其显示在屏幕上。
打开您的 main.go 文件:
1nano main.go
然后在文件的末尾添加以下代码:
1[label main.go]
2. . .
3func getFinished() ([]*Task, error) {
4 filter := bson.D{
5 primitive.E{Key: "completed", Value: true},
6 }
7
8 return filterTasks(filter)
9}
10. . .
与getPending()
函数类似,您已添加一个getFinished()
函数,该函数返回了一小部分完成的任务,在这种情况下,过滤器将完成
属性设置为真实
,因此只返回符合此条件的文档。
接下来,创建一个完成
命令,打印所有完成的任务:
1[label main.go]
2. . .
3func main() {
4 app := &cli.App{
5 Name: "tasker",
6 Usage: "A simple CLI program to manage your tasks",
7 Action: func(c *cli.Context) error {
8 tasks, err := getPending()
9 if err != nil {
10 if err == mongo.ErrNoDocuments {
11 fmt.Print("Nothing to see here.\nRun `add 'task'` to add a task")
12 return nil
13 }
14
15 return err
16 }
17
18 printTasks(tasks)
19 return nil
20 },
21 Commands: []*cli.Command{
22 {
23 Name: "add",
24 Aliases: []string{"a"},
25 Usage: "add a task to the list",
26 Action: func(c *cli.Context) error {
27 str := c.Args().First()
28 if str == "" {
29 return errors.New("Cannot add an empty task")
30 }
31
32 task := &Task{
33 ID: primitive.NewObjectID(),
34 CreatedAt: time.Now(),
35 UpdatedAt: time.Now(),
36 Text: str,
37 Completed: false,
38 }
39
40 return createTask(task)
41 },
42 },
43 {
44 Name: "all",
45 Aliases: []string{"l"},
46 Usage: "list all tasks",
47 Action: func(c *cli.Context) error {
48 tasks, err := getAll()
49 if err != nil {
50 if err == mongo.ErrNoDocuments {
51 fmt.Print("Nothing to see here.\nRun `add 'task'` to add a task")
52 return nil
53 }
54
55 return err
56 }
57
58 printTasks(tasks)
59 return nil
60 },
61 },
62 {
63 Name: "done",
64 Aliases: []string{"d"},
65 Usage: "complete a task on the list",
66 Action: func(c *cli.Context) error {
67 text := c.Args().First()
68 return completeTask(text)
69 },
70 },
71 {
72 Name: "finished",
73 Aliases: []string{"f"},
74 Usage: "list completed tasks",
75 Action: func(c *cli.Context) error {
76 tasks, err := getFinished()
77 if err != nil {
78 if err == mongo.ErrNoDocuments {
79 fmt.Print("Nothing to see here.\nRun `done 'task'` to complete a task")
80 return nil
81 }
82
83 return err
84 }
85
86 printTasks(tasks)
87 return nil
88 },
89 },
90 }
91 }
92
93 err := app.Run(os.Args)
94 if err != nil {
95 log.Fatal(err)
96 }
97}
98. . .
该完成
命令通过在这里创建的getFinished()
函数获取具有完成
属性的任务,然后将其传输到printTasks
函数,以便将其打印到标准输出中。
保存和退出您的文件。
运行以下命令:
1go run main.go finished
您将看到以下输出:
1[secondary_label Output]
21: Learn Go
在最后一步中,您将为用户提供从数据库中删除任务的选项。
步骤 8 - 删除任务
在此步骤中,您将添加一个新的删除
子命令,以允许用户从数据库中删除任务. 要删除单个任务,您将使用MongoDB驱动程序的collection.DeleteOne()
方法。
再次打开你的main.go
文件:
1nano main.go
添加此删除任务
函数以在完成
函数后立即从数据库中删除任务:
1[label main.go]
2. . .
3func deleteTask(text string) error {
4 filter := bson.D{primitive.E{Key: "text", Value: text}}
5
6 res, err := collection.DeleteOne(ctx, filter)
7 if err != nil {
8 return err
9 }
10
11 if res.DeletedCount == 0 {
12 return errors.New("No tasks were deleted")
13 }
14
15 return nil
16}
17. . .
此删除任务
方法采用一个字符串参数,代表要删除的任务项目. 一个过滤器被构建以匹配任务项目,其文本
属性被设置为字符串参数. 您将过滤器传输到删除One()
方法,该方法匹配集合中的项目并删除它。
您可以通过DeleteOne
方法检查结果中的DeletedCount
属性,以确认是否已删除任何文档. 如果过滤器无法匹配要删除的文档,则DeletedCount
将为零,您可以在这种情况下返回错误。
现在添加一个新的rm
命令,如下所示:
1[label main.go]
2. . .
3func main() {
4 app := &cli.App{
5 Name: "tasker",
6 Usage: "A simple CLI program to manage your tasks",
7 Action: func(c *cli.Context) error {
8 tasks, err := getPending()
9 if err != nil {
10 if err == mongo.ErrNoDocuments {
11 fmt.Print("Nothing to see here.\nRun `add 'task'` to add a task")
12 return nil
13 }
14
15 return err
16 }
17
18 printTasks(tasks)
19 return nil
20 },
21 Commands: []*cli.Command{
22 {
23 Name: "add",
24 Aliases: []string{"a"},
25 Usage: "add a task to the list",
26 Action: func(c *cli.Context) error {
27 str := c.Args().First()
28 if str == "" {
29 return errors.New("Cannot add an empty task")
30 }
31
32 task := &Task{
33 ID: primitive.NewObjectID(),
34 CreatedAt: time.Now(),
35 UpdatedAt: time.Now(),
36 Text: str,
37 Completed: false,
38 }
39
40 return createTask(task)
41 },
42 },
43 {
44 Name: "all",
45 Aliases: []string{"l"},
46 Usage: "list all tasks",
47 Action: func(c *cli.Context) error {
48 tasks, err := getAll()
49 if err != nil {
50 if err == mongo.ErrNoDocuments {
51 fmt.Print("Nothing to see here.\nRun `add 'task'` to add a task")
52 return nil
53 }
54
55 return err
56 }
57
58 printTasks(tasks)
59 return nil
60 },
61 },
62 {
63 Name: "done",
64 Aliases: []string{"d"},
65 Usage: "complete a task on the list",
66 Action: func(c *cli.Context) error {
67 text := c.Args().First()
68 return completeTask(text)
69 },
70 },
71 {
72 Name: "finished",
73 Aliases: []string{"f"},
74 Usage: "list completed tasks",
75 Action: func(c *cli.Context) error {
76 tasks, err := getFinished()
77 if err != nil {
78 if err == mongo.ErrNoDocuments {
79 fmt.Print("Nothing to see here.\nRun `done 'task'` to complete a task")
80 return nil
81 }
82
83 return err
84 }
85
86 printTasks(tasks)
87 return nil
88 },
89 },
90 {
91 Name: "rm",
92 Usage: "deletes a task on the list",
93 Action: func(c *cli.Context) error {
94 text := c.Args().First()
95 err := deleteTask(text)
96 if err != nil {
97 return err
98 }
99
100 return nil
101 },
102 },
103 }
104 }
105
106 err := app.Run(os.Args)
107 if err != nil {
108 log.Fatal(err)
109 }
110}
111. . .
与之前添加的所有其他子命令一样,rm
命令使用其第一个参数来匹配数据库中的任务并删除它。
保存和退出您的文件。
您可以通过运行程序列出未完成的任务,而无需通过任何子命令:
1go run main.go
1[secondary_label Output]
21: Read a book
在阅读一本书
任务上运行rm
子命令将从数据库中删除它:
1go run main.go rm "Read a book"
如果您再次列出所有尚未完成的任务,您会注意到阅读一本书
任务不再显示,而显示了添加新任务的提示:
1go run main.go
1[secondary_label Output]
2Nothing to see here
3Run `add 'task'` to add a task
在此步骤中,您添加了一个函数来从数据库中删除任务。
结论
您已经成功创建了一个任务管理器命令行程序,并了解了使用MongoDB Go驱动程序的基本知识。
请确保在 GoDoc查看 MongoDB Go 驱动程序的完整文档,以了解使用驱动程序提供的功能。
此教程的最终代码可以在此 GitHub repo中查看。