尝试使用 MongoDB 官方提供的 Go 语言驱动(三)进行查找

这是《尝试使用MongoDB官方的Go语言驱动程序》系列的第三篇帖子,名为“Find编”。

查找插入文档

下载东京都的平成31年地价公示的个别地点数据(CSV文件)。

请从以下URL下载用于MongoDB插入的CSV数据。

将地价公示数据登记到MongoDB中

    • ダウンロードしたデータはSJISなのでgolang.org/x/text/encoding/japaneseを使って文字コード変換します

 

    • 英文字の項目名を考えるのが面倒なので日本語にする

項目名の先頭文字が大文字の英字でないとMongoDBにInsertされないので、Fを付けて、MongoDB上の項目名はタグに指定します

MongoDBにInsertする項目は都道府県市区町村コードから地積までとします
価格などは数値項目ですが”,”が使われているので”,”を取り除く処理をしています
InsertはUseSessionを使ってトランザクション処理としました

insertDataのタイプは []地価公示としないこと
Insert件数は2603件でした

package main

import (
    "context"
    "encoding/csv"
    "io"
    "log"
    "os"
    "strconv"
    "strings"

    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "golang.org/x/text/encoding/japanese"
    "golang.org/x/text/transform"
)

type 地価公示 struct {
    Fコード     int     "コード"
    F対象年     int     "対象年"
    F標準地名    string  "標準地名"
    F標準地番号用途 int     "標準地番号用途"
    F標準地番号連番 int     "標準地番号連番"
    F区市町村名   string  "区市町村名"
    F地番      string  "地番"
    F住居表示    string  "住居表示"
    F当年価格    int     "当年価格"
    F前年価格    int     "前年価格"
    F変動率     float64 "変動率"
    F地積      int     "地積"
}

func toInt(data string) (int, error) {
    intData := strings.Trim(strings.ReplaceAll(data, ",", ""), " ")
    return strconv.Atoi(intData)
}

func toFloat(data string) (float64, error) {
    floatData := strings.Trim(strings.ReplaceAll(data, ",", ""), " ")
    return strconv.ParseFloat(floatData, 64)
}

func mainMain() error {
    client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        return err
    }
    if err = client.Connect(context.Background()); err != nil {
        return err
    }
    defer client.Disconnect(context.Background())
    col := client.Database("test").Collection("col")
    csvFile, err := os.Open("./31kouji_chiten_data.csv")
    if err != nil {
        log.Fatal(err)
    }
    defer csvFile.Close()
    reader := csv.NewReader(transform.NewReader(csvFile, japanese.ShiftJIS.NewDecoder()))
    //ヘッダー部の読み飛ばし
    reader.Read()
    reader.Read()
    var insertData []interface{}
    for {
        rec, err := reader.Read()
        if err == io.EOF {
            break
        } else if err != nil {
            return err
        }
        var csvData 地価公示
        csvData.Fコード, _ = toInt(rec[1])
        csvData.F対象年, _ = toInt(rec[2])
        csvData.F標準地名 = rec[3]
        csvData.F標準地番号用途, _ = toInt(rec[4])
        csvData.F標準地番号連番, _ = toInt(rec[5])
        csvData.F区市町村名 = rec[6]
        csvData.F地番 = rec[7]
        csvData.F住居表示 = rec[8]
        csvData.F当年価格, _ = toInt(rec[9])
        csvData.F前年価格, _ = toInt(rec[10])
        csvData.F変動率, _ = toFloat(rec[11])
        csvData.F地積, _ = toInt(rec[12])
        insertData = append(insertData, csvData)
    }
    err = client.UseSession(context.Background(),
        func(ctx mongo.SessionContext) error {
            err := ctx.StartTransaction()
            if err != nil {
                return err
            }
            _, err = col.InsertMany(ctx, insertData)
            if err != nil {
                return err
            }
            return ctx.CommitTransaction(ctx)
        })
    return err
}
func main() {
    if err := mainMain(); err != nil {
        log.Fatal(err)
    }
    log.Println("normal end.")
}

找到地价公示数据

找到書的格式

Find函数会获取与搜索条件匹配的所有文档。

Find(context.Context, 検索条件, Find オプション)

在这个选项里,options.Find()并不是上次使用的options.FindOne()。

找到一个程序的示例

以下是搜索条件的例子,要求是找到当年价格高于1000万元且波动率较高的5个记录。

db.col.find({"当年価格":{$gte:10000000}},{_id:0}).sort({"変動率":-1}).limit(5)

cur.Next()は次のドキュメントの読み込み、ドキュメントが無いときはfalseを返す

cur.Decode()は読み込んだドキュメントを取得します。Decodeできるタイプはいろいろありますので前回の投稿のFindOne編をご覧ください

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 地価公示 struct {
    Fコード     int     "コード"
    F対象年     int     "対象年"
    F標準地名    string  "標準地名"
    F標準地番号用途 int     "標準地番号用途"
    F標準地番号連番 int     "標準地番号連番"
    F区市町村名   string  "区市町村名"
    F地番      string  "地番"
    F住居表示    string  "住居表示"
    F当年価格    int     "当年価格"
    F前年価格    int     "前年価格"
    F変動率     float64 "変動率"
    F地積      int     "地積"
}

func mainMain() error {
    client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        return err
    }
    if err = client.Connect(context.Background()); err != nil {
        return err
    }
    defer client.Disconnect(context.Background())
    col := client.Database("test").Collection("col")
    findOptions := options.Find().SetProjection(bson.D{{"_id", 0}})
    findOptions.SetLimit(5).SetSort(bson.D{{"変動率", -1}})
    filter := bson.D{{"当年価格", bson.D{{"$gte", 1000 * 10000}}}}
    cur, err := col.Find(context.Background(), filter, findOptions)
    if err != nil {
        return err
    }

    for cur.Next(context.Background()) {
        var doc 地価公示
        if err = cur.Decode(&doc); err != nil {
            return err
        }
        //fmt.Printf("%+v\n", doc)
        fmt.Printf("変動率:%g%%, 当年価格:%d万円, 地番:%s\n", doc.F変動率, doc.F当年価格/10000, doc.F地番)
    }
    return nil
}

func main() {
    if err := mainMain(); err != nil {
        log.Fatal(err)
    }
    log.Println("normal end.")
}

用相同的条件下,示例使用了Aggregate进行编程。

db.col.aggregate([
  {$match:{"当年価格":{$gte:10000000}}},
  {$sort:{"変動率":-1}},
  {$limit: 5},
  {$project:{_id:0}}
])
    filter := bson.D{{"当年価格", bson.D{{"$gte", 1000 * 10000}}}}
    pipeline := bson.A{
        bson.D{{"$match", filter}},
        bson.D{{"$sort", bson.D{{"変動率", -1}}}},
        bson.D{{"$limit", 5}},
        bson.D{{"$project", bson.D{{"_id", 0}}}},
    }
    cur, err := col.Aggregate(context.Background(), pipeline)