Thứ ba, 28/04/2020 | 00:00 GMT+7

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:

go.mod
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:

Output
go: 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:

go.mod
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:

main.go
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 optionsmongo 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:

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() 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 :

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

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

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

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:

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

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:

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)     } } 

Đ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:

Output
NAME: tasker - A simple CLI program to manage your tasks USAGE: main [global options] command [command options] [arguments...] COMMANDS: help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help (default: false)

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 , timeerrors :

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

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 :

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

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 :

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

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)     } } 

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 :

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

Sau đó tạo các hàm sau ngay sau khi 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 đượ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:

Output
go: 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 :

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

Tiếp theo, tạo một chức năng printTasks mới sau chức năng main của bạn:

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

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:

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

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:

Output
1: 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:

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

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:

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

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

Ảnh chụp màn hình  kết quả  của terminal  sau khi hoàn thành tác vụ

Đô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 :

main.go
. . . 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 bsonprimitive 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:

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

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:

Output
1: 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:

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

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

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:

Output
1: 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:

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

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:

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

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
Output
1: 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
Output
Nothing 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.


Tags:

Các tin liên quan