作者选择了自由软件基金会作为Write for DOnations计划的一部分接受捐赠。
介绍
在依赖社区开发的解决方案多年后,MongoDB 宣布他们正在开发 Go 的官方驱动程序。2019 年 3 月,随着 v1.0.0 的发布,这个新驱动程序达到了生产就绪状态,并从那时起不断更新。
与其他官方 MongoDB 驱动程序一样,Go 驱动程序是 Go 编程语言的惯用方法,并提供了一种使用 MongoDB 作为 Go 程序的数据库解决方案的简单方法。它与 MongoDB API 完全集成,并公开了 API 的所有查询、索引和聚合功能以及其他高级功能。与第三方库不同,它将得到 MongoDB 工程师的全面支持,因此您可以放心地继续开发和维护。
在本教程中,您将开始使用官方的 MongoDB Go Driver。您将安装驱动程序、连接到 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 驱动程序包并将其导入到您的项目中。您还将连接到 MongoDB 数据库并检查连接状态。
继续并在您的文件系统中为本教程创建一个新目录:
- mkdir tasker
设置项目目录后,使用以下命令切换到该目录:
- cd tasker
接下来,使用go.mod
文件初始化 Go 项目。此文件定义项目要求并将依赖关系锁定到正确的版本:
- go mod init
如果您的项目目录在 之外$GOPATH
,则需要为您的模块指定导入路径,如下所示:
- go mod init github.com/<your_username>/tasker
此时,您的go.mod
文件将如下所示:
module github.com/<your_username>/tasker
go 1.14
使用以下命令将 MongoDB Go Driver 添加为项目的依赖项:
- 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
文件将如下所示:
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
文件中:
package main
import (
"context"
"log"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
在这里添加mongo
和options
包,由 MongoDB Go 驱动程序提供。
接下来,在您的导入之后,创建一个新的 MongoDB 客户端并连接到您正在运行的 MongoDB 服务器:
. . .
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.TODO()
表示您不确定现在要使用什么上下文,但您计划在将来添加一个上下文。
接下来,让我们确保使用该Ping
方法找到并成功连接到您的 MongoDB 服务器。在init
函数内添加以下代码:
. . .
log.Fatal(err)
}
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal(err)
}
}
如果在连接到数据库时出现任何错误,那么在您尝试解决问题时程序应该会崩溃,因为在没有活动数据库连接的情况下保持程序运行是没有意义的。
添加以下代码以创建数据库:
. . .
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal(err)
}
collection = client.Database("tasker").Collection("tasks")
}
您创建一个tasker
数据库和一个task
集合来存储您将创建的任务。您还可以设置collection
为包级变量,以便可以在整个包中重复使用数据库连接。
保存并退出文件。
此时的全文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
文件中:
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 程序并使您的代码编译:
. . .
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)
}
}
此代码段创建了一个名为的 CLI 程序,tasker
并添加了一个简短的使用说明,该说明将在您运行该程序时打印出来。该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 程序添加子命令。在本节结束时,您将能够通过add
在 CLI 程序中使用新命令向 MongoDB 数据库添加新任务。
首先打开你的main.go
文件:
- nano main.go
其次,进口go.mongodb.org/mongo-driver/bson/primitive
,time
和errors
包:
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
函数之前:
. . .
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
函数后面添加此代码段:
. . .
func createTask(task *Task) error {
_, err := collection.InsertOne(ctx, task)
return err
}
. . .
该collection.InsertOne()
方法将提供的任务插入到数据库集合中,并返回插入的文档的 ID。由于您不需要此 ID,您可以通过分配给下划线运算符来丢弃它。
下一步是向您的任务管理器程序添加一个新命令以创建新任务。让我们称之为add
:
. . .
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
并使用它来设置Text
新Task
实例的属性,同时为其他属性分配适当的默认值。新任务随后被传递到createTask
,它将任务插入到数据库中,nil
如果一切顺利导致命令退出,则返回。
保存并退出您的文件。
通过使用add
命令添加一些任务来测试它。如果成功,您将不会在屏幕上看到任何错误:
- go run main.go add "Learn Go"
- go run main.go add "Read a book"
现在您可以成功添加任务,让我们实现一种方法来显示您添加到数据库中的所有任务。
第 4 步 — 列出所有任务
可以使用该collection.Find()
方法列出集合中的文档,该方法需要一个过滤器以及一个指向可以解码结果的值的指针。它的返回值是一个Cursor,它提供了一个文档流,可以一次迭代和解码一个。Cursor 用完后就会关闭。
打开你的main.go
文件:
- nano main.go
确保导入bson
包:
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
:
. . .
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 对象。函数中bson.D
使用的类型getAll()
表示 BSON 文档,并且在属性顺序很重要的地方使用它。通过将bson.D{{}}
过滤器传递给filterTasks()
,您表示要匹配集合中的所有文档。
在该filterTasks()
函数中,您遍历该collection.Find()
方法返回的 Cursor并将每个文档解码为 的实例Task
。Task
然后将每个附加到在函数开始时创建的任务切片。一旦 Cursor 用完,它就会关闭并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
并将其main.go
与fmt
包一起导入到您的文件中:
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"
)
. . .
接下来,printTasks
在您的main
函数之后创建一个新函数:
. . .
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
,迭代每个切片,并使用绿色表示已完成的任务和黄色表示未完成的任务将其打印到标准输出。
继续并添加以下突出显示的行以all
向Commands
切片创建新命令。此命令会将所有添加的任务打印到标准输出:
. . .
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
函数后面插入以下代码段:
. . .
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
返回。
接下来,让我们done
向 CLI 程序添加一个新命令,将任务标记为已完成:
. . .
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
函数后面添加这个片段:
. . .
func getPending() ([]*Task, error) {
filter := bson.D{
primitive.E{Key: "completed", Value: false},
}
return filterTasks(filter)
}
. . .
您使用MongoDB 驱动程序中的bson
和primitive
包创建过滤器,它将匹配completed
属性设置为 的文档false
。然后将挂起任务的切片返回给调用者。
与其创建一个新命令来列出挂起的任务,不如让它成为没有任何命令运行程序时的默认操作。您可以通过向Action
程序添加一个属性来做到这一点,如下所示:
. . .
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
然后在文件末尾添加以下代码:
. . .
func getFinished() ([]*Task, error) {
filter := bson.D{
primitive.E{Key: "completed", Value: true},
}
return filterTasks(filter)
}
. . .
与该getPending()
函数类似,您添加了一个getFinished()
返回已完成任务片段的函数。在这种情况下,过滤器的completed
属性设置为,true
因此只会返回与此条件匹配的文档。
接下来,创建一个finished
打印所有已完成任务的命令:
. . .
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
命令通过此处创建的函数检索其completed
属性设置为 的任务。然后它将它传递给函数,以便将它们打印到标准输出。true
getFinished()
printTasks
保存并退出您的文件。
运行以下命令:
- go run main.go finished
您将看到以下输出:
Output1: Learn Go
在最后一步,您将为用户提供从数据库中删除任务的选项。
步骤 8 — 删除任务
在此步骤中,您将添加一个新delete
子命令以允许用户从数据库中删除任务。要删除单个任务,您将使用collection.DeleteOne()
MongoDB 驱动程序中的方法。它还依赖过滤器来匹配要删除的文档。
main.go
再次打开您的文件:
- nano main.go
添加此deleteTask
函数以在您的getFinished
函数之后直接从数据库中删除任务:
. . .
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()
给与集合中的项目匹配的方法并将其删除。
您可以检查方法DeletedCount
结果的属性,DeleteOne
以确认是否删除了任何文档。如果过滤器无法匹配要删除的文档,则DeletedCount
将为零,在这种情况下您可以返回错误。
现在添加一个rm
高亮显示的新命令:
. . .
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
命令使用其第一个参数来匹配数据库中的任务并将其删除。
保存并退出您的文件。
You can list pending tasks by running your program without passing any subcommands:
- go run main.go
Output1: Read a book
Running the rm
subcommand on the "Read a book"
task will delete it from the database:
- go run main.go rm "Read a book"
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:
- go run main.go
OutputNothing 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.
请务必在GoDoc上查看 MongoDB Go 驱动程序的完整文档,以了解有关使用该驱动程序提供的功能的更多信息。您可能对描述使用聚合或交易的文档特别感兴趣。
本教程的最终代码可以在这个GitHub repo 中查看。