如何使用MongoDBGo驱动程序使用Go和MongoDB
作为 Write for DOnations 计划的一部分,作者选择了 自由软件基金会 来接受捐赠。
介绍
在多年依赖社区开发的解决方案后,MongoDB 宣布 正在为 Go 开发官方驱动程序。 2019 年 3 月,随着 v1.0.0 的发布,这个新的驱动程序达到了生产就绪状态 ,并从那时起不断更新。
与其他官方 MongoDB 驱动程序一样,Go 驱动程序 是 Go 编程语言的惯用语,并提供了一种简单的方法来使用 MongoDB 作为 Go 程序的数据库解决方案。 它与 MongoDB API 完全集成,并公开了 API 的所有查询、索引和聚合功能,以及其他高级功能。 与第三方库不同的是,它将由 MongoDB 工程师全力支持,因此您可以放心它的持续开发和维护。
在本教程中,您将开始使用官方的 MongoDB Go 驱动程序。 您将安装驱动程序、连接到 MongoDB 数据库并执行多个 CRUD 操作。 在此过程中,您将创建一个任务管理器程序,用于通过命令行管理任务。
先决条件
对于本教程,您将需要以下内容:
- 在您的机器上安装 Go,并按照 如何安装 Go 和设置本地编程环境 配置 Go 工作区。 在本教程中,项目将命名为
tasker
。 您需要在启用 Go Modules 的机器上安装 Go v1.11 或更高版本。
- 按照 如何安装 MongoDB 为您的操作系统安装 MongoDB。 MongoDB 2.6 或更高版本是 MongoDB Go 驱动程序支持的最低版本。
如果您使用的是 Go v1.11 或 1.12,请确保通过将 GO111MODULE
环境变量设置为 on
来启用 Go Modules,如下所示:
export GO111MODULE="on"
有关实现环境变量的更多信息,请阅读有关 如何读取和设置环境变量和 Shell 变量 的教程。
本指南中显示的命令和代码已使用 Go v1.14.1 和 MongoDB v3.6.3 进行了测试。
第 1 步 — 安装 MongoDB Go 驱动程序
在这一步中,您将为 MongoDB 安装 Go Driver 包并将其导入您的项目。 您还将连接到 MongoDB 数据库并检查连接状态。
继续在您的文件系统中为本教程创建一个新目录:
mkdir tasker
设置项目目录后,使用以下命令更改为该目录:
cd tasker
接下来,使用 go.mod
文件初始化 Go 项目。 此文件定义项目要求并将依赖项锁定到其正确版本:
go mod init
如果您的项目目录在 $GOPATH
之外,则需要为您的模块指定导入路径,如下所示:
go mod init github.com/<your_username>/tasker
此时,您的 go.mod
文件将如下所示:
去.mod
module github.com/<your_username>/tasker go 1.14
使用以下命令将 MongoDB Go 驱动程序添加为项目的依赖项:
go get go.mongodb.org/mongo-driver
您将看到如下输出:
Outputgo: downloading go.mongodb.org/mongo-driver v1.3.2 go: go.mongodb.org/mongo-driver upgrade => v1.3.2
此时,您的 go.mod
文件将如下所示:
去.mod
module github.com/<your_username>/tasker go 1.14 require go.mongodb.org/mongo-driver v1.3.1 // indirect
接下来,在项目根目录中创建一个 main.go
文件并在文本编辑器中打开它:
nano main.go
要开始使用驱动程序,请将以下包导入您的 main.go
文件:
main.go
package main import ( "context" "log" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" )
在这里,您添加了 MongoDB Go 驱动程序提供的 mongo 和 options 包。
接下来,在导入之后,创建一个新的 MongoDB 客户端并连接到正在运行的 MongoDB 服务器:
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()
接受 Context
和 options.ClientOptions
对象,用于设置连接字符串和其他驱动程序设置。 您可以访问 选项包文档 以查看可用的配置选项。
Context 类似于超时或截止日期,指示操作何时应停止运行并返回。 当特定操作运行缓慢时,它有助于防止生产系统的性能下降。 在此代码中,您传递 context.TODO()
表示您不确定现在要使用什么上下文,但您计划在未来添加一个。
接下来,让我们确保使用 Ping
方法找到并成功连接到您的 MongoDB 服务器。 在 init
函数中添加以下代码:
main.go
. . . log.Fatal(err) } err = client.Ping(ctx, nil) if err != nil { log.Fatal(err) } }
如果在连接到数据库时出现任何错误,那么在您尝试修复问题时程序应该会崩溃,因为如果没有活动的数据库连接,保持程序运行是没有意义的。
添加以下代码以创建数据库:
main.go
. . . err = client.Ping(ctx, nil) if err != nil { log.Fatal(err) } collection = client.Database("tasker").Collection("tasks") }
您创建一个 tasker
数据库和一个 task
集合来存储您将要创建的任务。 您还将 collection
设置为包级变量,以便您可以在整个包中重用数据库连接。
保存并退出文件。
此时完整的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") }
您已将程序设置为使用 Go 驱动程序连接到 MongoDB 服务器。 在下一步中,您将继续创建任务管理器程序。
第 2 步 — 创建 CLI 程序
在此步骤中,您将安装众所周知的 cli 包以帮助开发任务管理器程序。 它提供了一个界面,您可以利用该界面快速创建现代命令行工具。 例如,这个包提供了为你的程序定义子命令的能力,以获得更像 git 的命令行体验。
运行以下命令将包添加为依赖项:
go get github.com/urfave/cli/v2
接下来,再次打开您的 main.go
文件:
nano 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" ) . . .
如前所述,您导入 cli
包。 您还导入了 os
包,您将使用它来将命令行参数传递给您的程序:
在 init
函数之后添加以下代码以创建 CLI 程序并编译代码:
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) } }
该片段创建了一个名为 tasker
的 CLI 程序,并添加了一个简短的使用说明,当您运行该程序时将打印出来。 Commands
切片是您为程序添加命令的地方。 Run
命令将参数切片解析为适当的命令。
保存并退出您的文件。
这是构建和运行程序所需的命令:
go run main.go
您将看到以下输出:
OutputNAME: 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)
该程序运行并显示帮助文本,这有助于了解该程序可以做什么以及如何使用它。
在接下来的步骤中,您将通过添加子命令来帮助管理 MongoDB 中的任务,从而提高程序的实用性。
第 3 步 — 创建任务
在这一步中,您将使用 cli
包向 CLI 程序添加一个子命令。 在本节结束时,您将能够通过在 CLI 程序中使用新的 add
命令将新任务添加到 MongoDB 数据库。
首先打开您的 main.go
文件:
nano main.go
接下来,导入 go.mongodb.org/mongo-driver/bson/primitive、time 和 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" ) . . .
然后创建一个新结构来表示数据库中的单个任务,并将其插入到 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"` } . . .
您使用 primitive
包来设置每个任务的 ID 的类型,因为 MongoDB 默认使用 ObjectID
s 作为 _id
字段。 MongoDB 的另一个默认行为是,小写的字段名称在被序列化时用作每个导出字段的键,但这可以使用 bson
结构标签进行更改。
接下来,创建一个接收 Task
实例并将其保存在数据库中的函数。 在 main
函数之后添加此代码段:
main.go
. . . func createTask(task *Task) error { _, err := collection.InsertOne(ctx, task) return err } . . .
collection.InsertOne()
方法将提供的任务插入到数据库集合中,并返回插入的文档的 ID。 由于您不需要此 ID,因此您可以通过分配给下划线运算符来丢弃它。
下一步是向您的任务管理器程序添加一个新命令以创建新任务。 我们称它为 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) } }
添加到 CLI 程序中的每个新命令都放置在 Commands
切片中。 每一个都由名称、使用说明和操作组成。 这是将在命令执行时运行的代码。
在此代码中,您收集 add
的第一个参数并使用它来设置新 Task
实例的 Text
属性,同时为其他属性分配适当的默认值。 新任务随后被传递给 createTask
,它将任务插入数据库并返回 nil
如果一切顺利导致命令退出。
保存并退出您的文件。
通过使用 add
命令添加一些任务来测试它。 如果成功,您将不会看到任何错误打印到您的屏幕上:
go run main.go add "Learn Go" go run main.go add "Read a book"
现在您可以成功添加任务,让我们实现一种方法来显示您已添加到数据库中的所有任务。
第 4 步 — 列出所有任务
可以使用 collection.Find()
方法在集合中列出文档,该方法需要一个过滤器以及一个指向可以解码结果的值的指针。 它的返回值是 Cursor,它提供了一个文档流,可以一次迭代和解码一个。 游标在用完后关闭。
打开您的 main.go
文件:
nano main.go
确保导入 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" ) . . .
然后在 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(二进制编码 JSON) 是文档在 MongoDB 数据库中的表示方式,bson
包帮助我们在 Go 中使用 BSON 对象。 getAll()
函数中使用的 bson.D
类型表示 BSON 文档,它用于属性顺序重要的地方。 通过将 bson.D{{}}
作为过滤器传递给 filterTasks()
,您表示要匹配集合中的所有文档。
在 filterTasks()
函数中,您遍历 collection.Find()
方法返回的 Cursor 并将每个文档解码为 Task
的实例。 然后将每个 Task
附加到函数开始时创建的任务切片。 一旦游标用尽,它就会关闭并返回 tasks
切片。
在创建列出所有任务的命令之前,让我们创建一个辅助函数,该函数获取 tasks
的切片并打印到标准输出。 您将使用 color 包对输出进行着色。
在你可以使用这个包之前,安装它:
go get gopkg.in/gookit/color.v1
您将看到以下输出:
Outputgo: downloading gopkg.in/gookit/color.v1 v1.1.6 go: gopkg.in/gookit/color.v1 upgrade => v1.1.6
并将其与 fmt
包一起导入到您的 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" ) . . .
接下来,在 main
函数之后创建一个新的 printTasks
函数:
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) } } } . . .
这个 printTasks
函数获取 tasks
的切片,对每个切片进行迭代,并将其打印到标准输出,使用绿色表示已完成的任务,黄色表示未完成的任务。
继续添加以下突出显示的行,为 Commands
切片创建一个新的 all
命令。 此命令会将所有添加的任务打印到标准输出:
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) } } . . .
all
命令检索数据库中存在的所有任务并将它们打印到标准输出。 如果不存在任何任务,则会打印添加新任务的提示。
保存并退出您的文件。
使用 all
命令构建并运行您的程序:
go run main.go all
它将列出您迄今为止添加的所有任务:
Output1: Learn Go 2: Read a book
现在您可以查看数据库中的所有任务,让我们在下一步中添加将任务标记为已完成的功能。
第 5 步 — 完成任务
在此步骤中,您将创建一个名为 done
的新子命令,它允许您将数据库中的现有任务标记为已完成。 要将任务标记为已完成,您可以使用 collection.FindOneAndUpdate()
方法。 它允许您在集合中定位文档并更新其部分或全部属性。 这种方法需要一个过滤器来定位文档和一个更新文档来描述操作。 这两个都是使用 bson.D
类型构建的。
首先打开您的 main.go
文件:
nano main.go
在 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) } . . .
该函数匹配文本属性等于 text
参数的第一个文档。 update
文档指定将 completed
属性设置为 true
。 如果 FindOneAndUpdate()
操作出错,将由 completeTask()
返回。 否则返回 nil
。
接下来,让我们在 CLI 程序中添加一个新的 done
命令,将任务标记为已完成:
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) } } . . .
您使用传递给 done
命令的参数来查找其 text
属性匹配的第一个文档。 如果找到,文档上的 completed
属性设置为 true
。
保存并退出您的文件。
然后使用 done
命令运行您的程序:
go run main.go done "Learn Go"
如果再次使用 all
命令,您会注意到标记为已完成的任务现在打印为绿色。
go run main.go all
有时,您只想查看尚未完成的任务。 接下来我们将添加该功能。
第 6 步 — 仅显示待处理任务
在此步骤中,您将合并代码以使用 MongoDB 驱动程序从数据库中检索挂起的任务。 待处理任务是那些 completed
属性设置为 false
的任务。
让我们添加一个新函数来检索尚未完成的任务。 打开您的 main.go
文件:
nano main.go
然后在 completeTask
函数之后添加此代码段:
main.go
. . . func getPending() ([]*Task, error) { filter := bson.D{ primitive.E{Key: "completed", Value: false}, } return filterTasks(filter) } . . .
您使用 MongoDB 驱动程序中的 bson
和 primitive
包创建过滤器,它将匹配 completed
属性设置为 false
的文档。 然后将待处理任务的切片返回给调用者。
与其创建一个新的命令来列出待处理的任务,不如让它成为在没有任何命令的情况下运行程序时的默认操作。 您可以通过向程序中添加 Action
属性来执行此操作,如下所示:
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) }, }, . . .
当程序在没有任何子命令的情况下执行时,Action
属性执行默认操作。 这是列出待处理任务的逻辑的地方。 调用 getPending()
函数并使用 printTasks()
将生成的任务打印到标准输出。 如果没有待处理的任务,则会显示提示,鼓励用户使用 add
命令添加新任务。
保存并退出您的文件。
现在运行程序而不添加任何命令将列出数据库中所有待处理的任务:
go run main.go
您将看到以下输出:
Output1: Read a book
现在您可以列出未完成的任务,让我们添加另一个允许您仅查看已完成任务的命令。
第 7 步 - 显示已完成的任务
在这一步中,您将添加一个新的 finished
子命令,它从数据库中获取已完成的任务并将它们显示在屏幕上。 这涉及过滤和返回 completed
属性设置为 true
的任务。
打开您的 main.go
文件:
nano main.go
然后在文件末尾添加以下代码:
main.go
. . . func getFinished() ([]*Task, error) { filter := bson.D{ primitive.E{Key: "completed", Value: true}, } return filterTasks(filter) } . . .
与 getPending()
函数类似,您添加了一个 getFinished()
函数,该函数返回已完成任务的切片。 在这种情况下,过滤器将 completed
属性设置为 true
,因此只会返回符合此条件的文档。
接下来,创建一个打印所有已完成任务的 finished
命令:
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) } } . . .
finished
命令通过此处创建的 getFinished()
函数检索 completed
属性设置为 true
的任务。 然后它将它传递给 printTasks
函数,以便将它们打印到标准输出。
保存并退出您的文件。
运行以下命令:
go run main.go finished
您将看到以下输出:
Output1: Learn Go
在最后一步,您将为用户提供从数据库中删除任务的选项。
第 8 步 — 删除任务
在此步骤中,您将添加一个新的 delete
子命令,以允许用户从数据库中删除任务。 要删除单个任务,您将使用 MongoDB 驱动程序中的 collection.DeleteOne()
方法。 它还依赖过滤器来匹配要删除的文档。
再次打开 main.go
文件:
nano main.go
添加此 deleteTask
函数以在 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 } . . .
此 deleteTask
方法采用一个字符串参数,该参数表示要删除的任务项。 构造一个过滤器以匹配其 text
属性设置为字符串参数的任务项。 您将过滤器传递给与集合中的项目匹配的 DeleteOne()
方法并将其删除。
您可以检查 DeleteOne
方法的结果中的 DeletedCount
属性,以确认是否删除了任何文档。 如果过滤器无法匹配要删除的文档,则 DeletedCount
将为零,在这种情况下您可以返回错误。
现在添加一个新的 rm
命令,突出显示:
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) } } . . .
就像之前添加的所有其他子命令一样,rm
命令使用它的第一个参数来匹配数据库中的任务并将其删除。
保存并退出您的文件。
您可以通过运行程序来列出待处理的任务,而无需传递任何子命令:
go run main.go
Output1: Read a book
在 "Read a book"
任务上运行 rm
子命令会将其从数据库中删除:
go run main.go rm "Read a book"
如果再次列出所有待处理的任务,您会注意到 "Read a book"
任务不再出现,而是显示添加新任务的提示:
go run main.go
OutputNothing to see here Run `add 'task'` to add a task
在此步骤中,您添加了从数据库中删除任务的功能。
结论
您已成功创建任务管理器命令行程序,并在此过程中学习了使用 MongoDB Go 驱动程序的基础知识。
请务必在 GoDoc 上查看 MongoDB Go 驱动程序的完整文档,以了解有关使用驱动程序提供的功能的更多信息。 描述使用 aggregations 或 transactions 的文档可能会让您特别感兴趣。
本教程的最终代码可以在这个 GitHub repo 中查看。