Cách sử dụng Go với MongoDB bằng Trình điều khiển MongoDB Go
Sau khi dựa vào các giải pháp do cộng đồng phát triển trong nhiều năm, MongoDB thông báo rằng họ đang làm việc trên một trình điều khiển chính thức cho Go. Vào tháng 3 năm 2019, trình điều khiển mới này đã đạt đến trạng thái sẵn sàng production với việc phát hành v1.0.0 và đã được cập nhật liên tục kể từ đó.Giống như các trình điều khiển MongoDB chính thức khác, trình điều khiển Go là thành ngữ đối với ngôn ngữ lập trình Go và cung cấp một cách dễ dàng để sử dụng MongoDB làm giải pháp database cho chương trình Go. Nó được tích hợp hoàn toàn với API MongoDB và hiển thị tất cả các tính năng truy vấn, lập index và tổng hợp của API, cùng với các tính năng nâng cao khác. Không giống như các thư viện của bên thứ ba, nó sẽ được hỗ trợ đầy đủ bởi các kỹ sư MongoDB nên bạn có thể yên tâm về sự phát triển và bảo trì liên tục của nó.
Trong hướng dẫn này, bạn sẽ bắt đầu sử dụng Trình điều khiển MongoDB Go chính thức. Bạn sẽ cài đặt trình điều khiển, kết nối với database MongoDB và thực hiện một số thao tác CRUD. Trong quá trình này, bạn sẽ tạo một chương trình quản lý tác vụ để quản lý các việc thông qua dòng lệnh.
Yêu cầu
Đối với hướng dẫn này, bạn cần những thứ sau:
-  Cài đặt Go trên máy của bạn và một không gian làm việc Go được  cấu hình  sau Cách cài đặt Go và  Cài đặt  Môi trường Lập trình Cục bộ . Trong hướng dẫn này, dự án sẽ được đặt tên là tasker. Bạn cần cài đặt Go v1.11 trở lên trên máy của bạn khi đã bật Mô-đun Go.
- MongoDB được cài đặt cho hệ điều hành của bạn sau Cách cài đặt MongoDB . MongoDB 2.6 trở lên là version tối thiểu được hỗ trợ bởi trình điều khiển MongoDB Go.
 Nếu bạn đang sử dụng Go v1.11 hoặc 1.12, hãy đảm bảo Mô-đun Go được bật bằng cách đặt biến môi trường GO111MODULE thành on như hình sau:
- export GO111MODULE="on" 
Để biết thêm thông tin về cách triển khai các biến môi trường, hãy đọc hướng dẫn này về Cách đọc và Đặt các biến Môi trường và Hệ vỏ .
Các lệnh và mã hiển thị trong hướng dẫn này đã được thử nghiệm với Go v1.14.1 và MongoDB v3.6.3.
Bước 1 - Cài đặt trình điều khiển MongoDB Go
Trong bước này, bạn sẽ cài đặt gói Go Driver cho MongoDB và nhập nó vào dự án của bạn . Bạn cũng sẽ kết nối với database MongoDB của bạn và kiểm tra trạng thái của kết nối.
Hãy tiếp tục và tạo một folder mới cho hướng dẫn này trong hệ thống file của bạn:
- mkdir tasker 
Sau khi folder dự án của bạn được cài đặt , hãy thay đổi folder đó bằng lệnh sau:
- cd tasker 
Tiếp theo, khởi tạo dự án Go bằng file  go.mod . Tệp này xác định các yêu cầu của dự án và khóa các phần phụ thuộc vào các version  chính xác của chúng:
- go mod init 
Nếu folder  dự án của bạn nằm ngoài $GOPATH , bạn cần chỉ định đường dẫn nhập cho module   của bạn  như sau:
- go mod init github.com/<your_username>/tasker 
 Đến đây,  file  go.mod của bạn sẽ giống như sau:
module github.com/<your_username>/tasker  go 1.14 Thêm Trình điều khiển MongoDB Go làm phụ thuộc cho dự án của bạn bằng lệnh sau:
- go get go.mongodb.org/mongo-driver 
Bạn sẽ thấy kết quả như sau:
Outputgo: downloading go.mongodb.org/mongo-driver v1.3.2 go: go.mongodb.org/mongo-driver upgrade => v1.3.2  Đến đây,  file  go.mod của bạn sẽ giống như sau:
module github.com/<your_username>/tasker  go 1.14  require go.mongodb.org/mongo-driver v1.3.1 // indirect Tiếp theo, tạo file  main.go trong root  dự án của bạn và mở file  đó trong editor  của bạn:
- nano main.go 
Để bắt đầu với trình điều khiển, hãy nhập các gói sau vào file  main.go của bạn:
package main  import (     "context"     "log"      "go.mongodb.org/mongo-driver/mongo"     "go.mongodb.org/mongo-driver/mongo/options" ) Tại đây bạn thêm các gói options và mongo mà trình điều khiển MongoDB Go cung cấp.
Tiếp theo, sau quá trình nhập của bạn, hãy tạo một ứng dụng client MongoDB mới và kết nối với server MongoDB đang chạy của bạn:
. . . 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() chấp nhận một đối tượng Context và một options.ClientOptions , được sử dụng để đặt chuỗi kết nối và các cài đặt trình điều khiển khác. Bạn có thể truy cập tài liệu gói tùy chọn để xem những tùy chọn cấu hình nào có sẵn.
 Bối cảnh giống như thời gian chờ hoặc thời hạn cho biết khi nào một hoạt động sẽ ngừng chạy và quay trở lại. Nó giúp ngăn chặn sự suy giảm hiệu suất trên hệ thống production  khi các hoạt động cụ thể đang chạy chậm. Trong đoạn mã này, bạn đang chuyển ngữ context.TODO() để cho biết rằng bạn không chắc chắn nên sử dụng ngữ cảnh nào ngay bây giờ, nhưng bạn dự định thêm một ngữ cảnh trong tương lai.
 Tiếp theo, hãy  đảm bảo   server  MongoDB của bạn đã được tìm thấy và kết nối thành công bằng phương pháp Ping . Thêm mã sau vào trong hàm init :
. . .     log.Fatal(err)   }    err = client.Ping(ctx, nil)   if err != nil {     log.Fatal(err)   } } Nếu có bất kỳ lỗi nào trong khi kết nối với database , chương trình sẽ gặp sự cố trong khi bạn cố gắng khắc phục sự cố vì không có lý do gì để giữ chương trình chạy mà không có kết nối database đang hoạt động.
Thêm mã sau để tạo database :
. . .   err = client.Ping(ctx, nil)   if err != nil {     log.Fatal(err)   }    collection = client.Database("tasker").Collection("tasks") } Bạn tạo một tasker database  và một task thu thập để lưu trữ các nhiệm vụ bạn sẽ được tạo ra. Bạn cũng  cài đặt  collection dưới dạng biến mức gói để bạn có thể sử dụng lại kết nối database  trong toàn bộ gói.
Lưu và thoát khỏi file .
 main.go đầy đủ tại thời điểm này như sau:
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") } Bạn đã cài đặt chương trình của bạn để kết nối với server MongoDB bằng trình điều khiển Go. Trong bước tiếp theo, bạn sẽ tiến hành tạo chương trình quản lý tác vụ của bạn .
Bước 2 - Tạo chương trình CLI
 Trong bước này, bạn sẽ cài đặt gói cli nổi tiếng để hỗ trợ phát triển chương trình quản lý tác vụ  của bạn . Nó cung cấp một giao diện mà bạn có thể tận dụng để tạo nhanh các công cụ dòng lệnh hiện đại. Ví dụ: gói này cung cấp khả năng xác định các lệnh con cho chương trình của bạn để có trải nghiệm dòng lệnh giống git hơn.
Chạy lệnh sau để thêm gói làm phụ thuộc:
- go get github.com/urfave/cli/v2 
Tiếp theo, mở lại file  main.go của bạn:
- nano main.go 
Thêm mã được đánh dấu sau vào file  main.go của bạn:
package main  import (     "context"     "log"     "os"      "github.com/urfave/cli/v2"     "go.mongodb.org/mongo-driver/mongo"     "go.mongodb.org/mongo-driver/mongo/options" ) . . . Bạn nhập gói cli như đã nói. Bạn cũng import os gói, trong đó bạn sẽ sử dụng để vượt qua đối số dòng lệnh để chương trình của bạn:
 Thêm mã sau vào sau hàm init của bạn để tạo chương trình CLI và khiến mã của bạn được biên dịch:
. . . 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)     } } Đoạn mã này tạo một chương trình CLI có tên là tasker và thêm một mô tả sử dụng ngắn sẽ được in ra khi bạn chạy chương trình. Đoạn Commands là nơi bạn sẽ thêm các lệnh cho chương trình  của bạn . Lệnh Run phân tích cú pháp lát cắt đối số thành lệnh thích hợp.
Lưu và thoát khỏi file của bạn.
Đây là lệnh bạn cần để xây dựng và chạy chương trình:
- go run main.go 
Bạn sẽ thấy kết quả sau:
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) Chương trình chạy và hiển thị văn bản trợ giúp, rất hữu ích cho việc tìm hiểu về những gì chương trình có thể làm và cách sử dụng nó.
Trong các bước tiếp theo, bạn sẽ cải thiện tiện ích của chương trình bằng cách thêm các lệnh con để giúp quản lý các việc của bạn trong MongoDB.
Bước 3 - Tạo công việc
 Trong bước này, bạn sẽ thêm một lệnh con vào chương trình CLI  của bạn  bằng cách sử dụng gói cli . Ở cuối phần này,  bạn có thể  thêm một nhiệm vụ mới vào database  MongoDB  của bạn  bằng cách sử dụng lệnh add mới trong chương trình CLI của bạn.
 Bắt đầu bằng cách mở file  main.go của bạn:
- nano main.go 
Tiếp theo, nhập các go.mongodb.org/mongo-driver/bson/primitive , time và 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" ) . . . Sau đó, tạo một cấu trúc mới để đại diện cho một tác vụ duy nhất trong database  và chèn nó ngay trước chức năng 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"` } . . . Bạn sử dụng gói primitive để đặt loại ID của mỗi tác vụ vì MongoDB sử dụng ObjectID cho trường _id theo mặc định. Một hành vi mặc định khác của MongoDB là tên trường viết thường được sử dụng làm khóa cho mỗi trường được xuất khi nó đang được tuần tự hóa, nhưng điều này có thể được thay đổi bằng cách sử dụng thẻ struct bson .
 Tiếp theo, tạo một hàm nhận một thể hiện của Task và lưu nó vào database . Thêm đoạn mã này sau chức năng main :
. . . func createTask(task *Task) error {     _, err := collection.InsertOne(ctx, task)   return err } . . . Phương thức collection.InsertOne() sẽ chèn tác vụ đã cung cấp vào bộ sưu tập database  và trả về ID của tài liệu đã được chèn. Vì bạn không cần ID này, bạn loại bỏ nó bằng cách gán cho toán tử gạch dưới.
 Bước tiếp theo là thêm một lệnh mới vào chương trình quản lý tác vụ của bạn để tạo  các việc  mới. Hãy gọi nó là 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)     } } Mọi lệnh mới được thêm vào chương trình CLI của bạn đều được đặt bên trong lát Commands . Mỗi cái bao gồm tên, mô tả cách sử dụng và hành động. Đây là mã sẽ chạy khi thực thi lệnh.
 Trong mã này, bạn thu thập đối số đầu tiên để add và sử dụng đối số đó để đặt thuộc tính Text của version  Task mới trong khi gán các giá trị mặc định thích hợp cho các thuộc tính khác. Tác vụ mới sau đó được chuyển cho createTask , tác vụ này sẽ chèn tác vụ vào database  và trả về nil nếu mọi việc suôn sẻ khiến lệnh thoát.
Lưu và thoát khỏi file của bạn.
 Kiểm tra nó bằng cách thêm một vài tác vụ bằng lệnh add . Nếu thành công, bạn sẽ không thấy lỗi nào được in ra màn hình  của bạn :
- go run main.go add "Learn Go" 
- go run main.go add "Read a book" 
Đến đây bạn có thể thêm các việc thành công, hãy triển khai một cách để hiển thị tất cả các việc mà bạn đã thêm vào database .
Bước 4 - Liệt kê tất cả các Nhiệm vụ
 Việc liệt kê các tài liệu trong một bộ sưu tập có thể được thực hiện bằng cách sử dụng phương thức collection.Find() , phương thức này yêu cầu một bộ lọc cũng như một con trỏ tới một giá trị mà kết quả có thể được giải mã. Giá trị trả về của nó là Con trỏ , cung cấp một stream  tài liệu có thể được lặp lại và giải mã từng cái một. Con trỏ sau đó sẽ bị đóng khi nó đã được sử dụng hết.
 Mở file  main.go của bạn:
- nano main.go 
Đảm bảo nhập gói 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" ) . . . Sau đó tạo các hàm sau ngay sau khi 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 được mã hóa binary ) là cách tài liệu được biểu diễn trong database  MongoDB và gói bson là thứ giúp  ta  làm việc với các đối tượng BSON trong Go. Kiểu bson.D được sử dụng trong hàm getAll() đại diện cho một tài liệu BSON và nó được sử dụng khi thứ tự của các thuộc tính quan trọng. Bằng cách chuyển bson.D{{}} làm bộ lọc của bạn cho filterTasks() , bạn cho biết rằng bạn muốn đối sánh tất cả các tài liệu trong bộ sưu tập.
 Trong hàm filterTasks() , bạn lặp lại Con trỏ được trả về bởi phương thức collection.Find() và giải mã từng tài liệu thành một thể hiện của Task . Mỗi Task sau đó được thêm vào phần nhiệm vụ được tạo khi bắt đầu chức năng. Khi con trỏ hết, nó được đóng lại và phần tasks được trả về.
 Trước khi bạn tạo một lệnh để liệt kê tất cả các nhiệm vụ, hãy tạo một hàm trợ giúp lấy một phần tasks và in ra  kết quả  tiêu chuẩn. Bạn sẽ sử dụng gói color để tạo màu cho  kết quả .
Trước khi bạn có thể sử dụng gói này, hãy cài đặt nó bằng:
- go get gopkg.in/gookit/color.v1 
Bạn sẽ thấy kết quả sau:
Outputgo: downloading gopkg.in/gookit/color.v1 v1.1.6 go: gopkg.in/gookit/color.v1 upgrade => v1.1.6 Và nhập nó vào file  main.go của bạn cùng với gói 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" ) . . . Tiếp theo, tạo một chức năng printTasks mới sau chức năng main của bạn:
. . . 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)         }     } } . . . Hàm printTasks này nhận một phần tasks , lặp lại từng phần và in nó ra  kết quả  tiêu chuẩn bằng cách sử dụng màu xanh lá cây để biểu thị các nhiệm vụ đã hoàn thành và màu vàng cho các nhiệm vụ chưa hoàn thành.
 Hãy tiếp tục và thêm các dòng được đánh dấu sau để tạo một lệnh all mới cho lát Commands . Lệnh này sẽ in tất cả  các việc  đã thêm vào  kết quả  tiêu chuẩn:
. . . 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)     } }  . . . Lệnh all truy xuất tất cả  các việc  có trong database  và in chúng ra  kết quả  chuẩn. Nếu không có nhiệm vụ nào,  dấu nhắc  thêm nhiệm vụ mới sẽ được in thay thế.
Lưu và thoát khỏi file của bạn.
 Xây dựng và chạy chương trình của bạn bằng lệnh all :
- go run main.go all 
Nó sẽ liệt kê tất cả các nhiệm vụ mà bạn đã thêm cho đến nay:
Output1: Learn Go 2: Read a book Đến đây bạn có thể xem tất cả các nhiệm vụ trong database , hãy thêm khả năng đánh dấu một nhiệm vụ là đã hoàn thành trong bước tiếp theo.
Bước 5 - Hoàn thành công việc
 Trong bước này, bạn sẽ tạo một lệnh con mới được gọi là done cho phép bạn đánh dấu một nhiệm vụ hiện có trong database  là đã hoàn thành. Để đánh dấu một công việc là đã hoàn thành, bạn có thể sử dụng phương thức collection.FindOneAndUpdate() . Nó cho phép bạn định vị một tài liệu trong một bộ sưu tập và cập nhật một số hoặc tất cả các thuộc tính của nó. Phương pháp này yêu cầu một bộ lọc để xác định vị trí tài liệu và một tài liệu cập nhật để mô tả hoạt động. Cả hai đều được xây dựng bằng các loại bson.D
 Bắt đầu bằng cách mở file  main.go của bạn:
- nano main.go 
Chèn đoạn mã sau vào sau hàm filterTasks của bạn:
. . . 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) } . . . Hàm  trùng với  tài liệu đầu tiên có thuộc tính văn bản bằng tham số text . Tài liệu update chỉ định rằng thuộc tính completed được đặt thành true . Nếu có lỗi trong thao tác FindOneAndUpdate() , nó sẽ được trả về bởi completeTask() . Nếu không, nil được trả về.
 Tiếp theo, hãy thêm một lệnh done mới vào chương trình CLI của bạn để đánh dấu một nhiệm vụ là đã hoàn thành:
. . . 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)     } }  . . . Bạn sử dụng đối số được truyền cho lệnh done để tìm tài liệu đầu tiên có thuộc tính text khớp. Nếu được tìm thấy, thuộc tính completed trên tài liệu được đặt thành true .
Lưu và thoát khỏi file của bạn.
 Sau đó chạy chương trình của bạn bằng lệnh done :
- go run main.go done "Learn Go" 
Nếu bạn sử dụng lại lệnh all , bạn sẽ nhận thấy rằng tác vụ được đánh dấu là đã hoàn thành hiện được in bằng màu xanh lục.
- go run main.go all 
Đôi khi, bạn chỉ muốn xem các nhiệm vụ chưa được thực hiện. Ta sẽ thêm tính năng đó tiếp theo.
Bước 6 - Chỉ hiển thị các nhiệm vụ đang chờ xử lý
 Trong bước này, bạn sẽ kết hợp mã để truy xuất  các việc  đang chờ xử lý từ database  bằng trình điều khiển MongoDB. Nhiệm vụ đang chờ xử lý là những công việc có thuộc tính completed được đặt thành false .
 Hãy thêm một chức năng mới để truy xuất các nhiệm vụ chưa được hoàn thành. Mở file  main.go của bạn:
- nano main.go 
Sau đó, thêm đoạn mã này sau hàm completeTask :
. . . func getPending() ([]*Task, error) {     filter := bson.D{         primitive.E{Key: "completed", Value: false},     }      return filterTasks(filter) } . . . Bạn tạo bộ lọc bằng cách sử dụng các bson và primitive từ trình điều khiển MongoDB, bộ lọc này sẽ  trùng với  các tài liệu có thuộc tính completed được đặt thành false . Phần các nhiệm vụ đang chờ xử lý sau đó được trả lại cho người gọi.
 Thay vì tạo một lệnh mới để liệt kê  các việc  đang chờ xử lý, hãy đặt nó làm hành động mặc định khi chạy chương trình mà không có lệnh nào. Bạn có thể thực hiện việc này bằng cách thêm thuộc tính Action vào chương trình như sau:
. . . 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)                 },             }, . . . Thuộc tính Action thực hiện một hành động mặc định khi chương trình được thực thi mà không có bất kỳ lệnh con nào. Đây là nơi đặt logic để liệt kê các nhiệm vụ đang chờ xử lý. Hàm getPending() được gọi và  các việc  kết quả được in ra  kết quả  chuẩn bằng printTasks() . Nếu không có nhiệm vụ nào đang chờ xử lý, thay vào đó, một  dấu nhắc  sẽ hiển thị, khuyến khích  user  thêm một nhiệm vụ mới bằng lệnh add .
Lưu và thoát khỏi file của bạn.
Chạy chương trình ngay bây giờ mà không thêm bất kỳ lệnh nào sẽ liệt kê tất cả các việc đang chờ xử lý trong database :
- go run main.go 
Bạn sẽ thấy kết quả sau:
Output1: Read a book Đến đây bạn có thể liệt kê các nhiệm vụ chưa hoàn thành, hãy thêm một lệnh khác cho phép bạn chỉ xem các nhiệm vụ đã hoàn thành.
Bước 7 - Hiển thị các nhiệm vụ đã hoàn thành
 Trong bước này, bạn sẽ thêm một lệnh con finished mới tìm nạp  các việc  đã hoàn thành từ database  và hiển thị chúng trên màn hình. Điều này liên quan đến việc lọc và trả về  các việc  có thuộc tính completed được đặt thành true .
 Mở file  main.go của bạn:
- nano main.go 
Sau đó, thêm mã sau vào cuối file của bạn:
. . . func getFinished() ([]*Task, error) {     filter := bson.D{         primitive.E{Key: "completed", Value: true},     }      return filterTasks(filter) } . . . Tương tự như hàm getPending() , bạn đã thêm một getFinished() trả về một phần các nhiệm vụ đã hoàn thành. Trong trường hợp này, bộ lọc có thuộc tính completed được đặt thành true nên chỉ những tài liệu phù hợp với điều kiện này mới được trả về.
 Tiếp theo, tạo một lệnh finished in tất cả  các việc  đã hoàn thành:
. . . 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)     } } . . . Lệnh finished truy xuất  các việc  có thuộc tính completed được đặt thành true thông qua hàm getFinished() được tạo ở đây. Sau đó, nó chuyển nó đến hàm printTasks để chúng được in ra  kết quả  tiêu chuẩn.
Lưu và thoát khỏi file của bạn.
Chạy lệnh sau:
- go run main.go finished 
Bạn sẽ thấy kết quả sau:
Output1: Learn Go Trong bước cuối cùng, bạn sẽ cung cấp cho user tùy chọn xóa các việc khỏi database .
Bước 8 - Xóa công việc
 Trong bước này, bạn sẽ thêm một lệnh con delete mới để cho phép  user  xóa một tác vụ khỏi database . Để xóa một tác vụ duy nhất, bạn sẽ sử dụng phương thức collection.DeleteOne() từ trình điều khiển MongoDB. Nó cũng dựa vào một bộ lọc để  trùng với  tài liệu cần xóa.
 Mở file  main.go của bạn   :
- nano main.go 
Thêm chức năng deleteTask này để xóa  các việc  khỏi database  ngay sau chức năng getFinished của bạn:
. . . 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 } . . . Phương thức deleteTask này nhận một đối số chuỗi đại diện cho mục tác vụ sẽ bị xóa. Bộ lọc được tạo để  trùng với  mục tác vụ có thuộc tính text được đặt thành đối số chuỗi. Bạn chuyển bộ lọc đến phương thức DeleteOne() phù hợp với mục trong bộ sưu tập và xóa nó.
 Bạn có thể kiểm tra thuộc tính DeletedCount trên kết quả từ phương thức DeleteOne để xác nhận xem có tài liệu nào bị xóa hay không. Nếu bộ lọc không thể phù hợp với một tài liệu bị xóa, các DeletedCount sẽ không và bạn có thể trả về một lỗi trong trường hợp đó.
 Bây giờ thêm một lệnh rm mới như được đánh dấu:
. . . 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)     } } . . . Cũng giống như với tất cả các lệnh con khác đã được thêm trước đó, lệnh rm sử dụng đối số đầu tiên của nó để  trùng với  một tác vụ trong database  và xóa nó.
Lưu và thoát khỏi file của bạn.
Bạn có thể liệt kê các nhiệm vụ đang chờ xử lý bằng cách chạy chương trình của bạn mà không cần chuyển bất kỳ lệnh con nào:
- go run main.go 
Output1: Read a book Chạy lệnh con rm trên tác vụ "Read a book" sẽ xóa nó khỏi database :
- go run main.go rm "Read a book" 
Nếu bạn liệt kê lại tất cả các nhiệm vụ đang chờ xử lý, bạn sẽ nhận thấy rằng tác vụ "Read a book" không xuất hiện nữa và  dấu nhắc  thêm nhiệm vụ mới được hiển thị thay thế:
- go run main.go 
OutputNothing to see here Run `add 'task'` to add a task Trong bước này, bạn đã thêm một chức năng để xóa các việc khỏi database .
Kết luận
Bạn đã tạo thành công chương trình dòng lệnh của trình quản lý tác vụ và đã tìm hiểu các nguyên tắc cơ bản của việc sử dụng trình điều khiển MongoDB Go trong quá trình này.
Hãy nhớ xem toàn bộ tài liệu cho Trình điều khiển MongoDB Go tại GoDoc để tìm hiểu thêm về các tính năng sử dụng trình điều khiển cung cấp. Tài liệu mô tả việc sử dụng tổng hợp hoặc giao dịch có thể được bạn quan tâm đặc biệt.
Có thể xem mã cuối cùng cho hướng dẫn này trong repo GitHub này.
Các tin liên quan
 

