0%

所有的 API 可以在 Go MongoDB Driver 找到。

在下面的例子中,都使用下面的这个 db() 函数来获取 mongo.Database 的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import (
"context"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)

func db() *mongo.Database {
clientOptions := options.Client().ApplyURL("mongodb://localhost:27017")
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}

err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
return client.Database("test")
}

实例一 插入单条记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)

func main() {
db := db()
coll := db.Collection("inventory_test")
coll.Drop(context.TODO())

// E represents a BSON element for a D. It is usually used inside a D.
// type E struct {
// Key string
// Value interface{}
// }
result, err := coll.InsertOne(
context.TODO(),
// bson.D 是一个 []bson.E 类型
bson.D{
{"item", "canvas"}, // 这是一个 bson.E 类型元素
{"qty", 100},
{"tags", bson.A{"cotton"}},
{"size", bson.D{
{"h", 28},
{"w", 35.5},
{"uom", "cm"},
}},
},
)
if err != nil {
panic(err)
}
fmt.Printf("Inserted ID is %+v\n", result.InsertedID)

// 我们会在 MongoDB 中看到如下内容:
// {
// "_id": ObjectId("5e64e556478fb77672d8b5b5"),
// "item": "canvas",
// "qty": NumberInt("100"),
// "tags": [
// "cotton"
// ],
// "size": {
// "h": NumberInt("28"),
// "w": 35.5,
// "uom": "cm"
// }
// }
}

实例二 查询单条记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)

type Size struct {
H int
W float64
Uom string
}

type InventoryTest struct {
ID string `bson:"_id"`
Item string
Qty int
Tags []string
Size Size
}

func main() {
db := db()
coll := db.Collection("inventory_test")

cursor, err := coll.Find(
context.TODO(),
bson.D{
{"item", "canvas"},
},
)
if err != nil {
panic(err)
}

var inventoryTest InventoryTest
// 获取游标的下一条记录
cursor.Next(context.TODO())
err = cursor.Decode(&inventoryTest)
if err != nil {
panic(err)
}
fmt.Printf("inventoryTest is %+v\n", inventoryTest)

// inventoryTest is {ID:5e64ea4919abb0178c2787f8 Item:canvas Qty:100 Tags:[cotton] Size:{H:28 W:35.5 Uom:cm}}
}

实例三 插入多条记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)

func main() {
db := db()
coll := db.Collection("inventory_test")

result, err := coll.InsertMany(
context.TODO(),
[]interface{}{
bson.D{
{"item", "journal"},
{"qty", int32(25)},
{"tags", bson.A{"blank", "red"}},
{"size", bson.D{
{"h", 14},
{"w", 21},
{"uom", "cm"},
}},
},
bson.D{
{"item", "mat"},
{"qty", int32(25)},
{"tags", bson.A{"gray"}},
{"size", bson.D{
{"h", 27.9},
{"w", 35.5},
{"uom", "cm"},
}},
},
bson.D{
{"item", "mousepad"},
{"qty", 25},
{"tags", bson.A{"gel", "blue"}},
{"size", bson.D{
{"h", 19},
{"w", 22.85},
{"uom", "cm"},
}},
},
},
)
if err != nil {
panic(err)
}
fmt.Printf("InsertedIDs: %+v\n", result.InsertedIDs)

// InsertedIDs: [ObjectID("5e64ee5bacce2998dc112460") ObjectID("5e64ee5bacce2998dc112461") ObjectID("5e64ee5bacce2998dc112462")]
}

实例四 查询顶层字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
package main

import (
"context"
"fmt"
"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"
"log"
)

func db() *mongo.Database {
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}
err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
return client.Database("test")
}

type Size struct {
H int
W float64
Uom string
}

type InventoryTest struct {
ID string `bson:"_id"`
Item string
Qty int
Tags []string
Size Size
}

func main() {
db := db()
coll := db.Collection("inventory_query_top")

err := coll.Drop(context.TODO())
if err != nil {
panic(err)
}

{
// Start Example 6
docs := []interface{}{
bson.D{
{"item", "journal"},
{"qty", 25},
{"size", bson.D{
{"h", 14},
{"w", 21},
{"uom", "cm"},
}},
{"status", "A"},
},
bson.D{
{"item", "notebook"},
{"qty", 50},
{"size", bson.D{
{"h", 8.5},
{"w", 11},
{"uom", "in"},
}},
{"status", "A"},
},
bson.D{
{"item", "paper"},
{"qty", 100},
{"size", bson.D{
{"h", 8.5},
{"w", 11},
{"uom", "in"},
}},
{"status", "D"},
},
bson.D{
{"item", "planner"},
{"qty", 75},
{"size", bson.D{
{"h", 22.85},
{"w", 30},
{"uom", "cm"},
}},
{"status", "D"},
},
bson.D{
{"item", "postcard"},
{"qty", 45},
{"size", bson.D{
{"h", 10},
{"w", 15.25},
{"uom", "cm"},
}},
{"status", "A"},
},
}

result, err := coll.InsertMany(context.TODO(), docs)
if err != nil {
panic(err)
}
fmt.Printf("InsertedIDs: %+v\n", result.InsertedIDs)
}

{
// Start Example 7
cursor, _ := coll.Find(
context.TODO(),
bson.D{},
)
length := 0
for cursor.Next(context.TODO()) {
length++
}
// 5
fmt.Printf("length: %v\n", length)
}

{
// Start Example 8
cursor, _ := coll.Find(
context.TODO(),
bson.D{
{"status", "D"},
},
)
length := 0
for cursor.Next(context.TODO()) {
length++
}
// 2
fmt.Printf("length: %+v\n", length)
}

{
// Start Example 9
cursor, _ := coll.Find(
context.TODO(),
bson.D{
{"status", bson.D{{"$in", bson.A{"A", "D"}}}},
},
)
length := 0
for cursor.Next(context.TODO()) {
length++
}
// 5
fmt.Printf("length: %+v\n", length)
}

{
// Start Example 10
cursor, _ := coll.Find(
context.TODO(),
bson.D{
{"status", "A"},
{"qty", bson.D{{"$lt", 30}}},
},
)
length := 0
for cursor.Next(context.TODO()) {
length++
}
// 1
fmt.Printf("length: %+v\n", length)
}

{
// Start Example 11
cursor, _ := coll.Find(
context.TODO(),
bson.D{
{"$or", bson.A{
bson.D{{"status", "A"}},
bson.D{{"qty", bson.D{{"$lt", 30}}}},
}},
},
)
length := 0
for cursor.Next(context.TODO()) {
length++
}
// 3
fmt.Printf("length: %+v\n", length)
}

{
// Start Example 12
cursor, _ := coll.Find(
context.TODO(),
bson.D{
{"status", "A"},
{"$or", bson.A{
bson.D{{"qty", bson.D{{"$lt", 30}}}},
bson.D{{"item", primitive.Regex{Pattern: "^p", Options: ""}}},
}},
},
)
length := 0
for cursor.Next(context.TODO()) {
length++
}
// 2
fmt.Printf("length: %+v\n", length)
}
}

实例五 查询嵌套的字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
func main() {
db := db()
coll := db.Collection("inventory_query_embedded")

err := coll.Drop(context.TODO())
if err != nil {
panic(err)
}

{
// Start Example 14
docs := []interface{}{
bson.D{
{"item", "journal"},
{"qty", 25},
{"size", bson.D{
{"h", 14},
{"w", 21},
{"uom", "cm"},
}},
{"status", "A"},
},
bson.D{
{"item", "notebook"},
{"qty", 50},
{"size", bson.D{
{"h", 8.5},
{"w", 11},
{"uom", "in"},
}},
{"status", "A"},
},
bson.D{
{"item", "paper"},
{"qty", 100},
{"size", bson.D{
{"h", 8.5},
{"w", 11},
{"uom", "in"},
}},
{"status", "D"},
},
bson.D{
{"item", "planner"},
{"qty", 75},
{"size", bson.D{
{"h", 22.85},
{"w", 30},
{"uom", "cm"},
}},
{"status", "D"},
},
bson.D{
{"item", "postcard"},
{"qty", 45},
{"size", bson.D{
{"h", 10},
{"w", 15.25},
{"uom", "cm"},
}},
},
}

result, _ := coll.InsertMany(context.Background(), docs)
fmt.Printf("InsertedIDs: %+v\n", result.InsertedIDs)
}

{
// Start Example 15
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"size", bson.D{
{"h", 14},
{"w", 21},
{"uom", "cm"},
}},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 16
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"size", bson.D{
{"w", 21},
{"h", 14},
{"uom", "cm"},
}},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 17
cursor, _ := coll.Find(
context.Background(),
bson.D{{"size.uom", "in"}},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 18
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"size.h", bson.D{
{"$lt", 15},
}},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 19
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"size.h", bson.D{
{"$lt", 15},
}},
{"size.uom", "in"},
{"status", "D"},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}
}

实例六 查询数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
func main() {
db := db()
coll := db.Collection("inventory_query_array")

err := coll.Drop(context.TODO())
if err != nil {
panic(err)
}

{
// Start Example 20
docs := []interface{}{
bson.D{
{"item", "journal"},
{"qty", 25},
{"tags", bson.A{"blank", "red"}},
{"dim_cm", bson.A{14, 21}},
},
bson.D{
{"item", "notebook"},
{"qty", 50},
{"tags", bson.A{"red", "blank"}},
{"dim_cm", bson.A{14, 21}},
},
bson.D{
{"item", "paper"},
{"qty", 100},
{"tags", bson.A{"red", "blank", "plain"}},
{"dim_cm", bson.A{14, 21}},
},
bson.D{
{"item", "planner"},
{"qty", 75},
{"tags", bson.A{"blank", "red"}},
{"dim_cm", bson.A{22.85, 30}},
},
bson.D{
{"item", "postcard"},
{"qty", 45},
{"tags", bson.A{"blue"}},
{"dim_cm", bson.A{10, 15.25}},
},
}

result, _ := coll.InsertMany(context.Background(), docs)
fmt.Printf("InsertedIDs: %+v\n", result.InsertedIDs)
}

{
// Start Example 21
cursor, _ := coll.Find(
context.Background(),
bson.D{{"tags", bson.A{"red", "blank"}}},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 22
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"tags", bson.D{{"$all", bson.A{"red", "blank"}}}},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 23
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"tags", "red"},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 24
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"dim_cm", bson.D{
{"$gt", 25},
}},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 25
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"dim_cm", bson.D{
{"$gt", 15},
{"$lt", 20},
}},
},
)

length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 26
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"dim_cm", bson.D{
{"$elemMatch", bson.D{
{"$gt", 22},
{"$lt", 30},
}},
}},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 27
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"dim_cm.1", bson.D{
{"$gt", 25},
}},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 28
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"tags", bson.D{
{"$size", 3},
}},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}
}

实例七 查询嵌套文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
func main() {
db := db()
coll := db.Collection("inventory_query_embedded")

err := coll.Drop(context.TODO())
if err != nil {
panic(err)
}

{
// Start Example 29
docs := []interface{}{
bson.D{
{"item", "journal"},
{"instock", bson.A{
bson.D{
{"warehouse", "A"},
{"qty", 5},
},
bson.D{
{"warehourse", "C"},
{"qty", 15},
},
}},
},
bson.D{
{"item", "notebook"},
{"instock", bson.A{
bson.D{
{"warehouse", "C"},
{"qty", 5},
},
}},
},
bson.D{
{"item", "paper"},
{"instock", bson.A{
bson.D{
{"warehouse", "A"},
{"qty", 60},
},
bson.D{
{"warehouse", "B"},
{"qty", 15},
},
}},
},
bson.D{
{"item", "planner"},
{"instock", bson.A{
bson.D{
{"warehouse", "A"},
{"qty", 40},
},
bson.D{
{"warehouse", "B"},
{"qty", 5},
},
}},
},
bson.D{
{"item", "postcard"},
{"instock", bson.A{
bson.D{
{"warehouse", "B"},
{"qty", 15},
},
bson.D{
{"warehouse", "C"},
{"qty", 35},
},
}},
},
}

result, _ := coll.InsertMany(context.Background(), docs)
fmt.Printf("InsertedIDs: %+v\n", result.InsertedIDs)
}

{
// Start Example 30
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"instock", bson.D{
{"warehouse", "A"},
{"qty", 5},
}},
})
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length) // 1
}

{
// Start Example 31
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"instock", bson.D{
{"qty", 5},
{"warehouse", "A"},
}},
})
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length) // 0
}

{
// Start Example 32
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"instock.0.qty", bson.D{
{"$lte", 20},
}},
})
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length) // 3
}

{
// Start Example 33
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"instock.qty", bson.D{
{"$lte", 20},
}},
})
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length) // 5
}

{
// Start Example 34
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"instock", bson.D{
{"$elemMatch", bson.D{
{"qty", 5},
{"warehouse", "A"},
}},
}},
})
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length) // 1
}

{
// Start Example 35
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"instock", bson.D{
{"$elemMatch", bson.D{
{"qty", bson.D{
{"$gt", 10},
{"$lte", 20},
}},
}},
}},
})
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length) // 3
}

{
// Start Example 36
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"instock.qty", bson.D{
{"$gt", 10},
{"$lte", 20},
}},
})
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length) // 4
}

{
// Start Example 37
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"instock.qty", 5},
{"instock.warehouse", "A"},
})
type Stock struct {
Warehouse string
Qty int
}
type Data struct {
Item string
Instock []Stock
}
length := 0
for cursor.Next(context.Background()) {
length++
var d Data
cursor.Decode(&d)
// {Item:journal Instock:[{Warehouse:A Qty:5} {Warehouse: Qty:15}]}
// {Item:planner Instock:[{Warehouse:A Qty:40} {Warehouse:B Qty:5}]}
fmt.Printf("%+v\n", d)
}
fmt.Printf("length: %v\n", length) // 2
}
}

实例八 查询为 null 或缺失的字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
func main() {
db := db()
coll := db.Collection("inventory_query_null_missing")

err := coll.Drop(context.Background())
if err != nil {
panic(err)
}

{
// Start Example 38
docs := []interface{}{
bson.D{
{"_id", 1},
{"item", nil},
},
bson.D{
{"_id", 2},
},
}
result, _ := coll.InsertMany(context.Background(), docs)
fmt.Printf("InsertedIDs: %v\n", result.InsertedIDs)
}

{
// Start Example 39
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"item", nil},
})
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 40
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"item", bson.D{
{"$type", 10}, // 10 等同于 "null", https://www.runoob.com/mongodb/mongodb-operators-type.html
}},
},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}

{
// Start Example 41
cursor, _ := coll.Find(
context.Background(),
bson.D{
{"item", bson.D{
{"$exists", false},
}},
})
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %v\n", length)
}
}

实例九 Projection

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
func containsKey(doc bson.Raw, key ...string) bool {
_, err := doc.LookupErr(key...)
if err != nil {
return false
}
return true
}

func main() {
db := db()
coll := db.Collection("inventory_project")

err := coll.Drop(context.Background())
if err != nil {
panic(err)
}

{
// Start Example 42
docs := []interface{}{
bson.D{
{"item", "journal"},
{"status", "A"},
{"size", bson.D{
{"h", 14},
{"w", 21},
{"uom", "cm"},
}},
{"instock", bson.A{
bson.D{
{"warehouse", "A"},
{"qty", 5},
},
}},
},
bson.D{
{"item", "notebook"},
{"status", "A"},
{"size", bson.D{
{"h", 8.5},
{"w", 11},
{"uom", "in"},
}},
{"instock", bson.A{
bson.D{
{"warehouse", "EC"},
{"qty", 5},
},
}},
},
bson.D{
{"item", "paper"},
{"status", "D"},
{"size", bson.D{
{"h", 8.5},
{"w", 11},
{"uom", "in"},
}},
{"instock", bson.A{
bson.D{
{"warehouse", "A"},
{"qty", 60},
},
}},
},
bson.D{
{"item", "postcard"},
{"status", "A"},
{"size", bson.D{
{"h", 10},
{"w", 15.25},
{"uom", "cm"},
}},
{"instock", bson.A{
bson.D{
{"warehouse", "B"},
{"qty", 15},
},
bson.D{
{"warehouse", "EC"},
{"qty", 35},
},
}},
},
}
result, _ := coll.InsertMany(context.Background(), docs)
fmt.Printf("InsertedIDs: %+v\n", result.InsertedIDs)
}

{
// Start Example 43
cursor, _ := coll.Find(
context.Background(),
bson.D{{"status", "A"}},
)
length := 0
for cursor.Next(context.Background()) {
length++
}
fmt.Printf("length: %+v\n", length)
}

{
// Start Example 44
projection := bson.D{
{"item", 1},
{"status", 1},
}

cursor, _ := coll.Find(
context.Background(),
bson.D{
{"status", "A"},
},
options.Find().SetProjection(projection),
)

for cursor.Next(context.Background()) {
doc := cursor.Current

fmt.Printf("contain _id: %v\n", containsKey(doc, "_id"))
fmt.Printf("contain item: %v\n", containsKey(doc, "item"))
fmt.Printf("contain status: %v\n", containsKey(doc, "status"))
fmt.Printf("contain size: %v\n", containsKey(doc, "size"))
fmt.Printf("contain instock: %v\n", containsKey(doc, "instock"))

break
}
}

{
// Start Example 45
projection := bson.D{
{"item", 1},
{"status", 1},
{"_id", 0},
}

cursor, _ := coll.Find(
context.Background(),
bson.D{
{"status", "A"},
},
options.Find().SetProjection(projection),
)

for cursor.Next(context.Background()) {
doc := cursor.Current

fmt.Printf("contain _id: %v\n", containsKey(doc, "_id"))
fmt.Printf("contain item: %v\n", containsKey(doc, "item"))
fmt.Printf("contain status: %v\n", containsKey(doc, "status"))
fmt.Printf("contain size: %v\n", containsKey(doc, "size"))
fmt.Printf("contain instock: %v\n", containsKey(doc, "instock"))

break
}
}

{
// Start Example 46
projection := bson.D{
{"status", 0},
{"instock", 0},
}

cursor, _ := coll.Find(
context.Background(),
bson.D{
{"status", "A"},
},
options.Find().SetProjection(projection),
)

for cursor.Next(context.Background()) {
doc := cursor.Current

fmt.Printf("contain _id: %v\n", containsKey(doc, "_id"))
fmt.Printf("contain item: %v\n", containsKey(doc, "item"))
fmt.Printf("contain status: %v\n", containsKey(doc, "status"))
fmt.Printf("contain size: %v\n", containsKey(doc, "size"))
fmt.Printf("contain instock: %v\n", containsKey(doc, "instock"))

break
}
}

{
// Start Example 47
projection := bson.D{
{"item", 1},
{"status", 1},
{"size.uom", 1},
}

cursor, _ := coll.Find(
context.Background(),
bson.D{
{"status", "A"},
},
options.Find().SetProjection(projection),
)

for cursor.Next(context.Background()) {
doc := cursor.Current

fmt.Printf("contain _id: %v\n", containsKey(doc, "_id"))
fmt.Printf("contain item: %v\n", containsKey(doc, "item"))
fmt.Printf("contain status: %v\n", containsKey(doc, "status"))
fmt.Printf("contain size: %v\n", containsKey(doc, "size"))
fmt.Printf("contain instock: %v\n", containsKey(doc, "instock"))

fmt.Printf("contain size.uom: %v\n", containsKey(doc, "size", "uom"))
fmt.Printf("contain size.h: %v\n", containsKey(doc, "size", "h"))
fmt.Printf("contain size.w: %v\n", containsKey(doc, "size", "w"))

break
}
}

{
// Start Example 48
projection := bson.D{
{"size.uom", 0},
}

cursor, _ := coll.Find(
context.Background(),
bson.D{
{"status", "A"},
},
options.Find().SetProjection(projection),
)

for cursor.Next(context.Background()) {
doc := cursor.Current

fmt.Printf("contain _id: %v\n", containsKey(doc, "_id"))
fmt.Printf("contain item: %v\n", containsKey(doc, "item"))
fmt.Printf("contain status: %v\n", containsKey(doc, "status"))
fmt.Printf("contain size: %v\n", containsKey(doc, "size"))
fmt.Printf("contain instock: %v\n", containsKey(doc, "instock"))

fmt.Printf("contain size.uom: %v\n", containsKey(doc, "size", "uom"))
fmt.Printf("contain size.h: %v\n", containsKey(doc, "size", "h"))
fmt.Printf("contain size.w: %v\n", containsKey(doc, "size", "w"))

break
}
}

{
// Start Example 49
projection := bson.D{
{"item", 1},
{"status", 1},
{"instock.qty", 1},
}

cursor, _ := coll.Find(
context.Background(),
bson.D{
{"status", "A"},
},
options.Find().SetProjection(projection),
)

for cursor.Next(context.Background()) {
doc := cursor.Current

fmt.Printf("contain _id: %v\n", containsKey(doc, "_id"))
fmt.Printf("contain item: %v\n", containsKey(doc, "item"))
fmt.Printf("contain status: %v\n", containsKey(doc, "status"))
fmt.Printf("contain size: %v\n", containsKey(doc, "size"))
fmt.Printf("contain instock: %v\n", containsKey(doc, "instock"))

instock, _ := doc.LookupErr("instock")
vals, _ := instock.Array().Values()

for _, val := range vals {
subdoc := val.Document()
elems, _ := subdoc.Elements()
fmt.Printf("length of elems: %+v\n", len(elems))
}

break
}
}

{
// Start Example 50
projection := bson.D{
{"item", 1},
{"status", 1},
{"instock", bson.D{
{"$slice", -1},
}},
}

cursor, _ := coll.Find(
context.Background(),
bson.D{
{"status", "A"},
},
options.Find().SetProjection(projection),
)

for cursor.Next(context.Background()) {
doc := cursor.Current

fmt.Printf("contain _id: %v\n", containsKey(doc, "_id"))
fmt.Printf("contain item: %v\n", containsKey(doc, "item"))
fmt.Printf("contain status: %v\n", containsKey(doc, "status"))
fmt.Printf("contain size: %v\n", containsKey(doc, "size"))
fmt.Printf("contain instock: %v\n", containsKey(doc, "instock"))

break
}
}
}

实例十 更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
func main() {
db := db()
coll := db.Collection("inventory_update")

err := coll.Drop(context.Background())
if err != nil {
panic(err)
}

{
// Start Example 51
docs := []interface{}{
bson.D{
{"item", "canvas"},
{"qty", 100},
{"size", bson.D{
{"h", 28},
{"w", 35.5},
{"uom", "cm"},
}},
{"status", "A"},
},
bson.D{
{"item", "journal"},
{"qty", 25},
{"size", bson.D{
{"h", 14},
{"w", 21},
{"uom", "cm"},
}},
{"status", "A"},
},
bson.D{
{"item", "mat"},
{"qty", 85},
{"size", bson.D{
{"h", 27.9},
{"w", 35.5},
{"uom", "cm"},
}},
{"status", "A"},
},
bson.D{
{"item", "mousepad"},
{"qty", 25},
{"size", bson.D{
{"h", 19},
{"w", 22.85},
{"uom", "in"},
}},
{"status", "P"},
},
bson.D{
{"item", "notebook"},
{"qty", 50},
{"size", bson.D{
{"h", 8.5},
{"w", 11},
{"uom", "in"},
}},
{"status", "P"},
},
bson.D{
{"item", "paper"},
{"qty", 100},
{"size", bson.D{
{"h", 8.5},
{"w", 11},
{"uom", "in"},
}},
{"status", "D"},
},
bson.D{
{"item", "planner"},
{"qty", 75},
{"size", bson.D{
{"h", 22.85},
{"w", 30},
{"uom", "cm"},
}},
{"status", "D"},
},
bson.D{
{"item", "postcard"},
{"qty", 45},
{"size", bson.D{
{"h", 10},
{"w", 15.25},
{"uom", "cm"},
}},
{"status", "A"},
},
bson.D{
{"item", "sketchbook"},
{"qty", 80},
{"size", bson.D{
{"h", 14},
{"w", 21},
{"uom", "cm"},
}},
{"status", "A"},
},
bson.D{
{"item", "sketch pad"},
{"qty", 95},
{"size", bson.D{
{"h", 22.85},
{"w", 30.5},
{"uom", "cm"},
}},
{"status", "A"},
},
}

result, _ := coll.InsertMany(context.Background(), docs)
fmt.Printf("InsertedIDs: %+v\n", result.InsertedIDs)
}

{
// Start Example 52
result, _ := coll.UpdateOne(
context.Background(),
bson.D{
{"item", "paper"},
},
bson.D{
{"$set", bson.D{
{"size.uom", "cm"},
{"status", "{"},
}},
{"$currentDate", bson.D{
{"lastModified", true},
}},
},
)
fmt.Printf("result.MatchedCount: %+v\n", result.MatchedCount)
fmt.Printf("result.ModifiedCount: %+v\n", result.ModifiedCount)

cursor, _ := coll.Find(
context.Background(),
bson.D{
{"item", "paper"},
},
)

for cursor.Next(context.Background()) {
doc := cursor.Current

uom, _ := doc.LookupErr("size", "uom")
fmt.Printf("uom.StringValue() = %+v\n", uom.StringValue())

status, _ := doc.LookupErr("status")
fmt.Printf("status.StringValue() = %+v\n", status.StringValue())

fmt.Printf("contain lastModified: %+v\n", containsKey(doc, "lastModified"))
}
}

{
// Start Example 53
result, _ := coll.UpdateMany(
context.Background(),
bson.D{
{"qty", bson.D{
{"$lt", 50},
}},
},
bson.D{
{"$set", bson.D{
{"size.uom", "cm"},
{"status", "P"},
}},
{"$currentDate", bson.D{
{"lastModified", true},
}},
},
)

fmt.Printf("result.MatchedCount: %+v\n", result.MatchedCount)
fmt.Printf("result.ModifiedCount: %+v\n", result.ModifiedCount)

cursor, _ := coll.Find(
context.Background(),
bson.D{
{"qty", bson.D{
{"$lt", 50},
}},
},
)

for cursor.Next(context.Background()) {
doc := cursor.Current

uom, _ := doc.LookupErr("size", "uom")
fmt.Printf("uom.StringValue(): %+v\n", uom.StringValue())

status, _ := doc.LookupErr("status")
fmt.Printf("status.StringValue(): %+v\n", status.StringValue())

fmt.Printf("contain lastModified: %+v\n", containsKey(doc, "lastModified"))
}
}

{
// Start Example 54
result, _ := coll.ReplaceOne(
context.Background(),
bson.D{
{"item", "paper"},
},
bson.D{
{"item", "paper"},
{"instock", bson.A{
bson.D{
{"warehouse", "A"},
{"qty", 60},
},
bson.D{
{"warehouse", "B"},
{"qty", 40},
},
}},
},
)

fmt.Printf("MatchedCount: %+v\n", result.MatchedCount)
fmt.Printf("ModifiedCount: %+v\n", result.ModifiedCount)

cursor, _ := coll.Find(
context.Background(),
bson.D{
{"item", "paper"},
},
)

for cursor.Next(context.Background()) {
doc := cursor.Current

fmt.Printf("contains _id: %+v\n", containsKey(doc, "_id"))
fmt.Printf("contains item: %+v\n", containsKey(doc, "item"))
fmt.Printf("contain instock: %+v\n", containsKey(doc, "instock"))

instock, _ := doc.LookupErr("instock")
vals, _ := instock.Array().Values()
fmt.Printf("length of vals is: %+v\n", vals)
}
}
}

实例十一 删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
func main() {
db := db()
coll := db.Collection("inventory_delete")

err := coll.Drop(context.Background())
if err != nil {
panic(err)
}

{
// Start Example 55
docs := []interface{}{
bson.D{
{"item", "journal"},
{"qty", 25},
{"size", bson.D{
{"h", 14},
{"w", 21},
{"uom", "cm"},
}},
{"status", "A"},
},
bson.D{
{"item", "notebook"},
{"qty", 50},
{"size", bson.D{
{"h", 8.5},
{"w", 11},
{"uom", "in"},
}},
{"status", "P"},
},
bson.D{
{"item", "paper"},
{"qty", 100},
{"size", bson.D{
{"h", 8.5},
{"w", 11},
{"uom", "in"},
}},
{"status", "D"},
},
bson.D{
{"item", "planner"},
{"qty", 75},
{"size", bson.D{
{"h", 22.85},
{"w", 30},
{"uom", "cm"},
}},
{"status", "D"},
},
bson.D{
{"item", "postcard"},
{"qty", 45},
{"size", bson.D{
{"h", 10},
{"w", 15.25},
{"uom", "cm"},
}},
{"status", "A"},
},
}
result, _ := coll.InsertMany(context.Background(), docs)
fmt.Printf("InsertedIDs: %+v\n", result.InsertedIDs)
}

{
// Start Example 57
result, _ := coll.DeleteMany(
context.Background(),
bson.D{
{"status", "A"},
},
)
fmt.Printf("Deleted count: %+v\n", result.DeletedCount)
}

{
// Start Example 58
result, _ := coll.DeleteOne(
context.Background(),
bson.D{
{"status", "D"},
},
)
fmt.Printf("Deleted count: %+v\n", result.DeletedCount)
}

{
// Start Example 59
result, _ := coll.DeleteMany(context.Background(), bson.D{})
fmt.Printf("Deleted count: %+v\n", result.DeletedCount)
}
}

实例十二 事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
var log = logger.New(logger.DebugLevel)

func main() {
db := db()
coll := db.Collection("inventory_delete")

err := coll.Drop(context.Background())
if err != nil {
panic(err)
}

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

employees := client.Database("hr").Collection("employees")
events := client.Database("reporting").Collection("events")

err = client.UseSession(context.Background(), func(sctx mongo.SessionContext) error {
err := sctx.StartTransaction(options.Transaction().
SetReadConcern(readconcern.Snapshot()).
SetWriteConcern(writeconcern.New(writeconcern.WMajority())),
)
if err != nil {
return err
}

_, err = employees.UpdateOne(sctx, bson.D{{"employee", 3}}, bson.D{{"$set", bson.D{{"status", "Inactive"}}}})
if err != nil {
sctx.AbortTransaction(sctx)
log.Printf("caught exception during transaction, aborting.")
return err
}

_, err = events.InsertOne(sctx, bson.D{{"employee", 3}, {"status", bson.D{{"new", "Inactive"}, {"old", "active"}}}})
if err != nil {
sctx.AbortTransaction(sctx)
log.Printf("caught exception during transaction, aborting")
return err
}

for {
err = sctx.CommitTransaction(sctx)
switch e := err.(type) {
case nil:
return nil
case mongo.CommandError:
if e.HasErrorLabel("UnknownTransactionCommitResult") {
log.Printf("UnknownTransactionCommitResult, retrying commit operation...")
continue
}
log.Printf("Error during commit...")
return e
default:
log.Printf("Error during commit...")
return e
}
}
})
}

实例十三 事务 commit 重试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// RunTransactionWithRetry is an example function demonstrating transaction retry logic.
func RunTransactionWithRetry(sctx mongo.SessionContext, txnFn func(sessionContext mongo.SessionContext) error) error {
for {
err := txnFn(sctx) // Performs transaction.
if err == nil {
return nil
}

log.Printf("Transaction aborted. Caught exception during transaction.\n")

// If transient error, retry the whole transaction
if cmdErr, ok := err.(mongo.CommandError); ok && cmdErr.HasErrorLabel("TransientTransactionError") {
log.Printf("TransientTransactionError, retrying transaction....")
continue
}
return err
}
}

// CommitWithRetry is an example function demonstrating transaction commit with retry logic.
func CommitWithRetry(sctx mongo.SessionContext) error {
for {
err := sctx.CommitTransaction(sctx)
switch e := err.(type) {
case nil:
log.Printf("Transaction committed.")
return nil
case mongo.CommandError:
// Can retry commit
if e.HasErrorLabel("UnknownTransactionCommitResult") {
log.Printf("UnknownTransactionCommitResult, retrying commit operation...")
continue
}
log.Printf("Error during commit...")
return e
default:
log.Printf("Error during commit...")
return e
}
}
}

// TransactionsExamples contains examples for transaction operations.
func TransactionsExamples(ctx context.Context, client *mongo.Client) error {
_, err := client.Database("hr").Collection("employees").InsertOne(ctx, bson.D{{"pi", 3.14159}})
if err != nil {
return err
}

_, err = client.Database("hr").Collection("employees").DeleteOne(ctx, bson.D{{"pi", 3.14159}})
if err != nil {
return err
}

_, err = client.Database("reporting").Collection("events").InsertOne(ctx, bson.D{{"pi", 3.14159}})
if err != nil {
return err
}

_, err = client.Database("reporting").Collection("events").DeleteOne(ctx, bson.D{{"pi", 3.14159}})
if err != nil {
return err
}

// Start Transactions Retry Example 3
runTransactionWithRetry := func(sctx mongo.SessionContext, txnFn func(mongo.SessionContext) error) error {
for {
err := txnFn(sctx) // Performs transactions.
if err == nil {
return nil
}

log.Printf("Transaction aborted. Caught exception during transaction.")

// If transient error, retry the whole transaction
if cmdErr, ok := err.(mongo.CommandError); ok && cmdErr.HasErrorLabel("TransientTransactionError") {
log.Printf("TransientTransactionError, retrying transaction...")
continue
}
return err
}
}

commitWithRetry := func(sctx mongo.SessionContext) error {
for {
err := sctx.CommitTransaction(sctx)
switch e := err.(type) {
case nil:
log.Printf("Transaction committed.")
return nil
case mongo.CommandError:
// Can retry commit
if e.HasErrorLabel("UnknownTransactionCommitResult") {
log.Printf("UnknownTransactionCommitResult, retrying commit operation...")
continue
}
log.Printf("Error during commit...\n")
return e
default:
log.Printf("Error during commit...\n")
return e
}
}
}

// Updates two collections in a transaction.
updateEmployeeInfo := func(sctx mongo.SessionContext) error {
employees := client.Database("hr").Collection("employees")
events := client.Database("reporting").Collection("events")

err := sctx.StartTransaction(options.Transaction().
SetReadConcern(readconcern.Snapshot()).
SetWriteConcern(writeconcern.New(writeconcern.WMajority())),
)
if err != nil {
return err
}

_, err = employees.UpdateOne(sctx, bson.D{{"employee", 3}}, bson.D{{"$set", bson.D{{"status", "Inactive"}}}})
if err != nil {
sctx.AbortTransaction(sctx)
log.Printf("caught exception during transaction, aborting.")
return err
}

_, err = events.InsertOne(sctx, bson.D{{"employee", 3}, {"status", bson.D{{"new", "Inactive"}, {"old", "Active"}}}})
if err != nil {
sctx.AbortTransaction(sctx)
log.Printf("caught exception during transaction, aborting.")
return err
}

return commitWithRetry(sctx)
}

return client.UseSessionWithOptions(
ctx, options.Session().SetDefaultReadPreference(readpref.Primary()),
func(sctx mongo.SessionContext) error {
return runTransactionWithRetry(sctx, updateEmployeeInfo)
},
)
}

实例十四 ChangeStream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// ChangeStreamExamples contains examples of changestream operations.
func ChangeStreamExamples(t *testing.T, db *mongo.Database) {
ctx := context.Background()

coll := db.Collection("inventory_changestream")
coll.Drop(context.Background())

coll.InsertOne(ctx, bson.D{{"x", int32(1)}})

var stop int32
doInserts := func(coll *mongo.Collection) {
for atomic.LoadInt32(&stop) == 0 {
coll.InsertOne(ctx, bson.D{{"x", 1}})
time.Sleep(10 * time.Microsecond)
coll.DeleteOne(ctx, bson.D{{"x", 1}})
}
}
go doInserts(coll)

{
// Start Changestream Example 1
cs, err := coll.Watch(ctx, mongo.Pipeline{})
defer cs.Close(ctx)

ok := cs.Next(ctx)
next := cs.Current

fmt.Printf("%+v\n", err)
fmt.Printf("%+v\n", ok)
fmt.Printf("%+v\n", len(next))
}

{
// Start Changestream Example 2
cs, err := coll.Watch(ctx, mongo.Pipeline{}, options.ChangeStream().SetFullDocument(options.UpdateLookup))
defer cs.Close(ctx)

ok := cs.Next(ctx)
next := cs.Current

fmt.Printf("%+v\n", err)
fmt.Printf("%+v\n", ok)
fmt.Printf("%+v\n", len(next))
}

{
original, err := coll.Watch(ctx, mongo.Pipeline{})
defer original.Close(ctx)

ok := original.Next(ctx)
fmt.Printf("%+v\n", err)
fmt.Printf("%+v\n", ok)

resumeToken := original.ResumeToken()

cs, err := coll.Watch(ctx, mongo.Pipeline{}, options.ChangeStream().SetResumeAfter(resumeToken))
defer cs.Close(ctx)

ok = cs.Next(ctx)
result := cs.Current

fmt.Printf("%+v\n", err)
fmt.Printf("%+v\n", ok)
fmt.Printf("%+v\n", len(result))
}

{
pipeline := mongo.Pipeline{bson.D{{"$match", bson.D{{"$or",
bson.A{
bson.D{{"fullDocument.username", "alice"}},
bson.D{{"operationType", "delete"}}}}},
}}}
cs, err := coll.Watch(ctx, pipeline)
defer cs.Close(ctx)
fmt.Printf("%+v\n", err)

ok := cs.Next(ctx)
next := cs.Current

fmt.Printf("%+v\n", err)
fmt.Printf("%+v\n", ok)
fmt.Printf("%+v\n", len(next))
}
}

MongoDB 安装(Docker)

先装个 mongo,为了省事就用 docker 了。

docker 的 daemon.json 加一个国内的源地址:

1
2
3
"registry-mirrors": [
"http://hub-mirror.c.163.com"
]

然后拉取 mongo 镜像:

1
docker pull mongodb

启动 mongo:

1
docker run -p 27017:27017 mongo

安装 MongoDB Go 驱动

1
go get go.mongodb.org/mongo-driver

基础代码

创建 main.go 文件,并且导入 bsonmongomongo/options 包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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"
)

// 在后面的代码中将会使用这个 Trainer 结构体
type Trainer struct {
Name string
Age int
City string
}

func main() {
//
}

使用 Go Driver 连接到 MongoDB

一旦 MongoDB 的 Go 驱动被导入之后,我们就可以使用 mongo.Connect 函数来连接到 MongoDB。你必须传递一个 contextoptions.ClientOptions 对象给 mongo.Connect 函数。options.ClientOptions 被用来设置连接配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 设置连接参数
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

// 连接到 MongoDB
client, err := mongo.Connect(context.TODO(), clientOptions)

if err != nil {
log.Fatal(err)
}

// 检查连接
err = client.Ping(context.TODO(), nil)

if err != nil {
log.Fatal(err)
}

fmt.Println("Connected to MongoDB!")

一旦你连接上了 MongoDB,你现在可以获取 test 数据库的 trainers 集合。

1
collection := client.Dataabse("test").Collection("trainers")

下面的代码将会使用 collection 来查询 trainers 集合。

如果你不在需要查询 MongoDB,就可以使用 client.Disconnect() 来关闭 MongoDB 连接。

1
2
3
4
5
6
err = client.Disconnect(context.TODO())

if err != nil {
log.Fatal(err)
}
fmt.Println("Connection to MongoDB closed.")

在 Go 里面使用 BSON 对象

在发送查询请求到 MongoDB 之前,了解如何使用 BSON 对象是非常有必要的。在 MongoDB 中存储的 JSON 文档叫 BSON,是以二进制形式存储的。不像其他数据库以字符串形式存储 JSOn 数据,BSON 还包含了字段类型,这使得应用程序更容易可靠地处理、排序和比较。Go 的 MongoDB 驱动有两个表示 BSON 数据的类型:D 类型和 Raw 类型。

D 类型用于使用 Go 中的原始类型来构建 BSON 对象。这对于传递给 MongoDB 的命令特别有用。D 类型包括四种类型:

  • D:一个 BSON 文档。

  • M:无序的 map。

  • A:一个 BSON 数组。

  • E:D 里面的单个元素。

这里是一个使用 D 类型筛选文档的例子,这里会筛选出名字为 Alice 或者 Bob 的数据:

1
2
3
4
5
6
bson.D({
"name",
bson.D{
{"$in": bson.A{"Alice", "Bob"}}
}
})

CRUD 操作

一旦你连接到了数据库,我们就可以开始添加或者操作数据库中的数据了。Collection 类型有一些方法允许你发送查询到数据库中。

插入文档

首先,创建一些新的 Trainer 结构体来插入到数据库中:

1
2
3
ash := Trainer{"Ash", 10, "Pallet Town"}
misty := Trainer{"Misty", 10, "Cerulean City"}
brock := Trainer{"Brock", 15, "Pewter" City}

可以使用 collection.InsertOne() 方法来插入单个文档:

1
2
3
4
5
6
insertResult, err := collection.InsertOne(context.TODO(), ash)
if err != nil {
log.Fatal(err)
}

fmt.Println("Inserted a single document: ", insertResult.InsertedID)

如果我们想一次性插入多个文档,可以传递一个切片给 collection.InsertMany 方法:

1
2
3
4
5
6
7
8
trainers := []interface{}{misty, brock}

insertManyResult, err := collection.InsertMany(context.TODO(), trainers)
if err != nil {
log.Fatal(err)
}

fmt.Println("Inserted multiple documents: ", insertManyResult.InsertedIDs)

更新文档

collection.UpdateOne() 方法允许你更新单个文档。它需要一个 bson.D 类型的参数来筛选数据库中特定的文档,然后更新它。

1
2
3
4
5
6
7
8
9
// 筛选 name 字段的值为 Ash 的记录
filter := bson.D{{"name", "Ash"}}

// 更新 age 字段,值加 1
update := bson.D{
{"$inc", bson.D{
{"age", 1}
}}
}

这几行代码会将数据库中名字为 Ash 的文档的 age 字段的值加 1。

1
2
3
4
5
updateResult, err := collection.UpdateOne(context.TODO(), filter, update)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Matched %v documents and updated %v documents.\n", updateResult.MatchedCount, updateResult.ModifiedCount)

查询文档

如果想要查询一个文档,同样需要提供一个 filter 文档来筛选,同时需要一个接收结果的指针。为了查询单个文档,可以使用 collection.FindOne() 方法。这个方法会返回一条匹配上的记录并解析到我们指针指向的地方。你可以使用和上面相同的 filter 来查询 name 为 Ash 的记录。

1
2
3
4
5
6
7
8
// 创建一个变量用来接收解析后的结果
var result Trainer

err = collection.FindOne(context.TODO(), filter).Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found a single document: %+v\n", result)

如果想要查询多个文档,可以使用 collection.Find() 方法。这个方法返回一个 Cursor(游标)。一个 Cursor (游标)可以让我们一次获取到其中一条记录。一旦一个游标遍历完毕,你应该关闭这个游标。

下面的例子中,我们在 Find 的时候同时指定了额外的一些选项,这里我们设置最多获取的记录数为 2。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 这个选项会被传递给 Find 方法
findOptions := options.Find()
findOptions.SetLimit(2)

// 这是用来保存查询结果的数组
var results []*Trainer

// 传递 bson.D{{}} 作为 filter 来匹配 collection 中的所有文档
cur, err := collection.Find(context.TODO(), bson.D{{}}, findOptions)
if err != nil {
log.Fatal(err)
}

// 查询多个文档的时候返回一个 cursor
// 迭代这个 cursor 允许我们一次解码一个文档
for cur.Next(context.TODO()) {

// 创建一个用来接收单个文档的变量
var elem Trainer
err := cur.Decode(&elem)
if err != nil {
log.Fatal(err)
}

results = append(results, &elem)
}

if err := cur.Err(); err != nil {
log.Fatal(err)
}

// 一旦结束了获取数据的操作,我们需要关闭 cursor
cur.Close(context.TODO())

fmt.Println("Found multiple documents (array of pointers): %+v\n", results)

删除文档

最后,你可以使用 collection.DeleteOne() 或者 collection.DeleteMany() 来删除文档。这里传递了 bson.D{ {} } 作为 filter 参数,将会匹配集合中的所有文档。你也可以使用 collection.Drop 来删除整个集合。

1
2
3
4
5
deleteResult, err := collection.DeleteMany(context.TODO(), bson.D{{}})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Deleted %v documents in the trainers collection\n", deleteResult.DeletedCount)

下一步

MongoDB Go 驱动的完整文档可以在 pkg.go.dev 中查看。

参考链接:https://tecadmin.net/install-rabbitmq-on-centos/

安装 Erlang

1
2
3
wget https://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm
sudo rpm -Uvh erlang-solutions-1.0-1.noarch.rpm
sudo yum install erlang erlang-nox

安装 RabbitMQ Server

CentOS/RHEL 6

1
https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.2/rabbitmq-server-3.8.2-1.el6.noarch.rpm
1
2
sudo rpm --import https://www.rabbitmq.com/rabbitmq-release-signing-key.asc
sudo yum install rabbitmq-server-3.8.2-1.el6.noarch.rpm

疑难杂症

在内网测试机上安装的过程中,遇到了一种情况,yum install 只能安装旧的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
yum list | grep erlang

erlang-appmon.x86_64 R14B-04.3.el6 @epel
erlang-asn1.x86_64 R14B-04.3.el6 @epel
erlang-common_test.x86_64 R14B-04.3.el6 @epel
erlang-compiler.x86_64 R14B-04.3.el6 @epel
erlang-cosEvent.x86_64 R14B-04.3.el6 @epel
erlang-cosEventDomain.x86_64 R14B-04.3.el6 @epel
erlang-cosFileTransfer.x86_64 R14B-04.3.el6 @epel
erlang-cosNotification.x86_64 R14B-04.3.el6 @epel
erlang-cosProperty.x86_64 R14B-04.3.el6 @epel
erlang-cosTime.x86_64 R14B-04.3.el6 @epel
...

这个命令输出显示了一堆 R14B-04.3.el6 的 erlang 版本。但是这不是我想要安装的版本。而且因为这一个版本存在,也导致新的版本死活安装不上。

最后搜索 @epel 找到一个命令:

1
rpm -qa | grep kernel

这个命令输出显示有一个 erlang-kernel-R14B-04.3.el6.x86_64,说实话这个不知道哪里来的,估计是之前随便找了篇文章瞎搞上去的。

1
2
3
4
5
6
7
8
kernel-2.6.32-642.el6.x86_64
kernel-2.6.32-696.23.1.el6.x86_64
kernel-2.6.32-754.27.1.el6.x86_64
dracut-kernel-004-411.el6.noarch
kernel-2.6.32-754.6.3.el6.x86_64
erlang-kernel-R14B-04.3.el6.x86_64
kernel-headers-2.6.32-754.27.1.el6.x86_64
kernel-firmware-2.6.32-754.27.1.el6.noarch

把这个删除就可以装上新的版本了。

1
yum remove erlang-kernel-R14B-04.3.el6.x86_64

有时候我们在执行 composer install 的时候会遇到一些依赖不满足导致命令执行失败的情况,比如 php 版本不满足、扩展缺失等。如:

1
2
3
4
5
6
7
8
9
10
Your requirements could not be resolved to an installable set of packages.

Problem 1
- phpdocumentor/reflection-docblock 5.1.0 requires php ^7.2 -> your PHP version (7.1.14) does not satisfy that requirement.
- phpdocumentor/reflection-docblock 5.1.0 requires php ^7.2 -> your PHP version (7.1.14) does not satisfy that requirement.
- phpdocumentor/reflection-docblock 5.1.0 requires php ^7.2 -> your PHP version (7.1.14) does not satisfy that requirement.
- Installation request for phpdocumentor/reflection-docblock (locked at 5.1.0) -> satisfiable by phpdocumentor/reflection-docblock[5.1.0].


Installation failed, reverting ./composer.json to its original content.

但有时候我们可能并不需要满足那个依赖也能满足我们项目的正常运行,这个时候我们可以选择忽略掉这些依赖不满足的情况继续执行 composer install。

这个时候,我们只需要加上参数 --ignore-platform-reqs

1
composer install --ignore-platform-reqs

加上这个参数之后,我们的 composer install 命令将会忽略掉依赖不满足的情况继续执行安装。

今天在排查 stage 环境的 mongodb log 的时候发现有很多慢查询,本来想尝试去优化一波,但无奈的是实在是看不懂那些 aggregate 语句,目前只好先来补习一下 mongodb 的 aggregate 相关知识。

mongodb aggregate 做了什么?

我们在使用 mongodb 的 aggregate 操作的时候会传递一个数组参数,这个数组里面的每一项定义了这一步需要做的操作,然后把这一步的结果传递给数组下一个聚合操作,知道执行完所有操作。

MongoDB 的聚合框架以数据处理管道的概念为模型。文档进入多阶段流水线,该流水线将文档转换成汇总结果。

例子(来自官网)

假设我们有如下数据:

orders:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
cust_id: "A123",
amount: 500,
status: "A"
},
{
cust_id: "A123",
amount: 250,
status: "A"
},
{
cust_id: "B212",
amount: 200,
status: "A"
},
{
cust_id: "A123",
amount: 300,
status: "D"
},

我们对这个 collection 执行如下操作:

1
2
3
4
db.orders.aggregate([
{ $match: { status: "A" } },
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } }
])

这个操作做了什么?

首先,根据传递的条件筛选出符合条件的数据

1
{ $match: { status: "A" } },

首先,筛选出 status"A" 的记录。这个过程会得到下面的记录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
cust_id: "A123",
amount: 500,
status: "A"
},
{
cust_id: "A123",
amount: 250,
status: "A"
},
{
cust_id: "B212",
amount: 200,
status: "A"
}

然后,根据 cust_id 分组,并且统计 amount 的总数

1
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } }

$group 表明我们要对上一步的结果进行分组,_id 表明我们需要根据 "$cust_id"" 来分组,相同 cust_id 的数组分为一组,最后,$sum 表明我们需要根据后面的表达式来当前分组执行求和操作,求和的字段为 amount

这个过程会得到下面的结果:

1
2
3
4
5
6
7
8
{
_id: "A123",
total: 750
},
{
_id: "B212",
total: 200
}