Thực hiện truy vấn tổng hợp trong Go với MongoDB: Nhóm theo trường và tính toán giá trị

Giả sử bạn có một collection sales trong MongoDB với các tài liệu như sau:

{ "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-03-01T08:00:00Z") }
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1, "date" : ISODate("2014-03-01T09:00:00Z") }
{ "_id" : 3, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-03-15T09:00:00Z") }
{ "_id" : 4, "item" : "xyz", "price" : 5, "quantity" : 20, "date" : ISODate("2014-04-04T11:21:39.736Z") }
{ "_id" : 5, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-04-04T21:23:13.331Z") }

Truy vấn tổng hợp (Aggregation) trong MongoDB

Để nhóm dữ liệu theo ngày (tháng/ngày/năm) và tính tổng doanh thu, trung bình số lượng và số bản ghi, bạn có thể dùng pipeline aggregation như sau:

db.sales.aggregate([
  {
    $group: {
      _id: {
        year: { $year: "$date" },
        month: { $month: "$date" },
        day: { $dayOfMonth: "$date" }
      },
      totalRevenue: { $sum: { $multiply: ["$price", "$quantity"] } },
      avgQty: { $avg: "$quantity" },
      totalCount: { $sum: 1 }
    }
  }
])

Kết quả trả về sẽ giống như:

{ "_id": { "year": 2014, "month": 3, "day": 1 }, "totalRevenue": 40, "avgQty": 1.5, "totalCount": 2 }
{ "_id": { "year": 2014, "month": 3, "day": 15 }, "totalRevenue": 50, "avgQty": 10, "totalCount": 1 }
{ "_id": { "year": 2014, "month": 4, "day": 4 }, "totalRevenue": 200, "avgQty": 15, "totalCount": 2 }

Triển khai trong Go với thư viện mongo-go-driver

Sử dụng driver chính thức của MongoDB cho Go (go.mongodb.org/mongo-driver), bạn có thể viết đoạn mã sau để thực thi truy vấn trên:

package main

import (
	"context"
	"fmt"
	"log"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type GroupResult struct {
	ID          bson.M `bson:"_id"`
	TotalRevenue float64 `bson:"totalRevenue"`
	AvgQty       float64 `bson:"avgQty"`
	TotalCount   int32   `bson:"totalCount"`
}

func main() {
	client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://localhost:27017"))
	if err != nil {
		log.Fatal(err)
	}
	defer client.Disconnect(context.TODO())

	coll := client.Database("testdb").Collection("sales")

	pipeline := []bson.M{
		{
			"$group": bson.M{
				"_id": bson.M{
					"year":  bson.M{"$year": "$date"},
					"month": bson.M{"$month": "$date"},
					"day":   bson.M{"$dayOfMonth": "$date"},
				},
				"totalRevenue": bson.M{"$sum": bson.M{"$multiply": []string{"$price", "$quantity"}}},
				"avgQty":       bson.M{"$avg": "$quantity"},
				"totalCount":   bson.M{"$sum": 1},
			},
		},
	}

	cursor, err := coll.Aggregate(context.TODO(), pipeline)
	if err != nil {
		log.Fatal(err)
	}
	defer cursor.Close(context.TODO())

	var results []GroupResult
	if err = cursor.All(context.TODO(), &results); err != nil {
		log.Fatal(err)
	}

	for _, res := range results {
		fmt.Printf("%+v\n", res)
	}
}

Một số lưu ý quan trọng

  • Trường _id trong stage $group là bắt buộc. Nếu đặt _id: null, toàn bộ collection sẽ được gom thành một nhóm duy nhất.
  • $sum: 1 dùng để đếm số bản ghi. Có thể thay bằng giá trị khác nếu cần nhân hệ số.
  • Các biểu thức như $year, $month, $dayOfMonth trích xuất thành phần từ trường kiểu Date.

Thẻ: golang MongoDB Aggregation

Đăng vào ngày 12 tháng 9 lúc 00:15