9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

MongoDBの Offical Go言語 Driverを使ってみる(2)FindOne

Posted at

前回投稿した「MongoDBの Offical Go言語 Driverを使ってみる(1)準備・Insert」の続きでFindOne編です。

FindOne

FindOneは検索条件にマッチした最初の1件目のドキュメントを取得します

FindOne書式

FindOne(context.Context, 検索条件, 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"
)

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")
	var doc bson.Raw
	findOptions := options.FindOne()
	err = col.FindOne(context.Background(), bson.D{}, findOptions).Decode(&doc)
	if err == mongo.ErrNoDocuments {
		log.Println("Documents not found")
		return nil
	} else if err != nil {
		return err
	}
	fmt.Println(doc.String())
	return nil
}

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

  • bson.D{}
    • これは空ドキュメントで検索条件なしです
  • findOptions
    • オプションですが、ここでは何も設定していないので省略可能です
    • projection,sort,skipなどが設定できます
  • Decode(&doc)
    • 取得したドキュメントをデコードします。この例ではdoc変数はbson.RawなのでBSONのまま受け取ります
  • エラーmongo.ErrNoDocuments
    • 検索条件にマッチしたドキュメントが1件もないときにこのエラーを返します。

(注意) **doc.String()**でBSONをJSON化して表示していますが、リリースされたv1.0.0ではバグがありObjectIdの表示でコードが""で囲まれていません。そのため、このJSONをBSON化しようとするとエラーになります。最新では修正されていますが、v1.0.0を使っている方は注意してください。修正箇所は以下のURLの通りです。
https://github.com/mongodb/mongo-go-driver/commit/c72645a64800adf5455dd9ad9cb811c0bf7d34c1

検索条件の設定の仕方

紹介するのは次の3つタイプです。

  • bson.D
  • 構造体(struct)
  • JSON文字列

前回(1)で投稿したときに使ったデータと同じものを想定しています。

{"str1": "abc", "num1":1, "str2": "xyz", "num2": [2,3,4], "subdoc": {"str": "subdoc", "num": 987},"date": 現在の日付}

bson.Dを使った検索条件例

//{str1: "abc"}
filter := bson.D{{"str1","abc"}}
//{str1: "abc", num1: 1}
filter := bson.D{{"str1","abc"},{"num1",1}} 
//{num2: {$gt: 3}}
filter := bson.D{{"num2", bson.D{{"$gt", 3}}}}

//------------------------------------------------------------------------
err = col.FindOne(context.Background(), filter, findOptions).Decode(&doc)

構造体を使った検索条件例

//{str1: "abc"}
filter := struct {
    Str1 string
}{"abc"}

//{str1: "abc", num1: 1}
filter := struct {
    Str1 string
    Num1 int 
}{"abc", 1}

//{num2: {$gt: 3}}
filter := struct {
	Num2 struct {
		Gt int "$gt"
	}
}{}
filter.Num2.Gt = 3
//------------------------------------------------------------------------
err = col.FindOne(context.Background(), filter, findOptions).Decode(&doc)

$gtは変数名にできないのでタグで指定します。

JSON文字列を使った検索条件例

//{str1: "abc"}
jsonFilter := `{"str1": "abc"}`
//{str1: "abc", num1: 1}
jsonFilter := `{"str1": "abc", "num1": 1}` 
//{num2: {$gt: 3}}
jsonFilter := `{"num2": {"$gt": 3}}`

//------------------------------------------------------------------------

var filter bson.Raw
err = bson.UnmarshalExtJSON([]byte(jsonFilter), false, &filter)
if err != nil {
	return err
}
err = col.FindOne(context.Background(), filter, findOptions).Decode(&doc)

FindOneオプションのProjectionの例

検索条件同様に設定できるタイプはbson.D,構造体,JSON文字列などがあります。ここではbson.Dの例だけ示します。

findOptions := options.FindOne()
findOptions.SetProjection(bson.D{{"_id", 0},{"str1",1},{"num1",1}})

検索したドキュメントを受け取るデータタイプ

  • bson.D
  • bson.M
  • 構造体(struct)
  • bson.Raw

bson.D

	var doc bson.D
	err = col.FindOne(context.Background(), filter, findOptions).Decode(&doc)	
	//エラー処理 
	for _, item := range doc {
		val := item.Value
		typ := reflect.TypeOf(val)
		fmt.Printf("type:%s, key:%s, val:%v\n", typ, item.Key, val)
	}
	//num1
	num1 := doc[2]
	num1Val := num1.Value.(int32)
	fmt.Printf("num1: %d\n", num1Val)

bson.Dmapではないのでnum1をキーにして値を取得できない。上記の例ではnum1を**doc[2]しているが実際においては必ずしもdoc[2]**とは限らない。

bson.M

	var doc bson.M
	err = col.FindOne(context.Background(), filter, findOptions).Decode(&doc)
	//エラー処理 
	for key, val := range doc {
		typ := reflect.TypeOf(val)
		fmt.Printf("type:%s, key:%s, val:%v\n", typ, key, val)
	}
	//num1
	num1Val := doc["num1"].(int32)
	fmt.Printf("num1: %d\n", num1Val)

bson.Mmapなので便利ですが、このdocの一部の値を変更して再ストアすると項目の順序が変わってしまうので注意が必要です。

構造体(struct)

	var doc struct {
		Str1   string
		Num1   int
		Str2   string
		Num2   []int
		Subdoc struct {
			Str string
			Num int
		}
		Date time.Time
	}
	err = col.FindOne(context.Background(), filter, findOptions).Decode(&doc)
	//エラー処理 
	fmt.Printf("%+v\n", doc)
	fmt.Printf("num1: %d\n", doc.Num1)

前回(1)の投稿でも述べましたが、num1の項目が無いときは0になってしまいますので、区別するためにはnum1をポインタまたは**interface{}**として定義します。

var doc struct {

    Num1 *int
    //あるいは
    Num1 interface{}
}

doc.Num1 == nilのときはnum1の項目が無いか値がnullです。 項目の型が限定できない場合も**interface{}**とすれば大丈夫です。

bson.Raw

bson.Rawのままでは使い勝手が悪いので結局bson.D,bson.M,構造体に変換して使います。
次の例はbson.Rawbson.Dに変換した例です。

	var doc bson.Raw
	err = col.FindOne(context.Background(), filter, findOptions).Decode(&doc)	
	//エラー処理 
	fmt.Println(doc.String())
	var docBsonD bson.D
	err = bson.Unmarshal(doc, &docBsonD)
	if err != nil {
		return err
	}
	fmt.Println(docBsonD)

bson.Rawの良いところは、後でbson.Unmarshalを使っていろいろなデータタイプに変換して使えることです。**String()**でJSONにもできます。
また、bson.Rawにはいろいろなメソッドが用意されていて、bson.RawElement,bson.RawValueのメソッドも使えます。
https://godoc.org/go.mongodb.org/mongo-driver/bson#Raw

例としてsubdoc.numの値を取り出してみます。

	var doc bson.Raw
	err = col.FindOne(context.Background(), filter, findOptions).Decode(&doc)
	//エラー処理 
	fmt.Println(doc.String())
	val := doc.Lookup("subdoc", "num")
	numVal, ok := val.Int32OK()
	if ok {
		fmt.Printf("subdoc.num: %d\n", numVal)
	}

次回はFindの予定です

9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?