@@ -16,6 +16,10 @@ import (
16
16
"github.com/mongodb/mongo-go-driver/core/wiremessage"
17
17
)
18
18
19
+ // this is the amount of reserved buffer space in a message that the
20
+ // driver reserves for command overhead.
21
+ const reservedCommandBufferBytes = 16 * 10 * 10 * 10
22
+
19
23
// Insert represents the insert command.
20
24
//
21
25
// The insert command inserts a set of documents into the database.
@@ -27,24 +31,76 @@ type Insert struct {
27
31
Docs []* bson.Document
28
32
Opts []option.InsertOptioner
29
33
30
- result result.Insert
31
- err error
34
+ result result.Insert
35
+ err error
36
+ continueOnError bool
32
37
}
33
38
34
- // Encode will encode this command into a wire message for the given server description.
35
- func (i * Insert ) Encode (desc description.SelectedServer ) (wiremessage.WireMessage , error ) {
39
+ func (i * Insert ) split (maxCount , targetBatchSize int ) ([][]* bson.Document , error ) {
40
+ batches := [][]* bson.Document {}
41
+
42
+ if targetBatchSize > reservedCommandBufferBytes {
43
+ targetBatchSize -= reservedCommandBufferBytes
44
+ }
45
+
46
+ if maxCount <= 0 {
47
+ maxCount = 1
48
+ }
49
+
50
+ startAt := 0
51
+ splitInserts:
52
+ for {
53
+ size := 0
54
+ batch := []* bson.Document {}
55
+ assembleBatch:
56
+ for idx := startAt ; idx < len (i .Docs ); idx ++ {
57
+ itsize , err := i .Docs [idx ].Validate ()
58
+ if err != nil {
59
+ return nil , err
60
+ }
61
+
62
+ if size + int (itsize ) > targetBatchSize {
63
+ break assembleBatch
64
+ }
65
+
66
+ size += int (itsize )
67
+ batch = append (batch , i .Docs [idx ])
68
+ startAt ++
69
+ if len (batch ) == maxCount {
70
+ break assembleBatch
71
+ }
72
+ }
73
+ batches = append (batches , batch )
74
+ if startAt == len (i .Docs ) {
75
+ break splitInserts
76
+ }
77
+ }
78
+
79
+ return batches , nil
80
+ }
81
+
82
+ func (i * Insert ) encodeBatch (docs []* bson.Document , desc description.SelectedServer ) (wiremessage.WireMessage , error ) {
83
+
36
84
command := bson .NewDocument (bson .EC .String ("insert" , i .NS .Collection ))
37
- vals := make ([]* bson.Value , 0 , len (i .Docs ))
38
- for _ , doc := range i .Docs {
85
+
86
+ vals := make ([]* bson.Value , 0 , len (docs ))
87
+ for _ , doc := range docs {
39
88
vals = append (vals , bson .VC .Document (doc ))
40
89
}
41
90
command .Append (bson .EC .ArrayFromElements ("documents" , vals ... ))
42
91
43
- for _ , option := range i .Opts {
44
- if option == nil {
92
+ for _ , opt := range i .Opts {
93
+ if opt == nil {
45
94
continue
46
95
}
47
- err := option .Option (command )
96
+
97
+ if ordered , ok := opt .(option.OptOrdered ); ok {
98
+ if ! ordered {
99
+ i .continueOnError = true
100
+ }
101
+ }
102
+
103
+ err := opt .Option (command )
48
104
if err != nil {
49
105
return nil , err
50
106
}
@@ -53,6 +109,26 @@ func (i *Insert) Encode(desc description.SelectedServer) (wiremessage.WireMessag
53
109
return (& Command {DB : i .NS .DB , Command : command , isWrite : true }).Encode (desc )
54
110
}
55
111
112
+ // Encode will encode this command into a wire message for the given server description.
113
+ func (i * Insert ) Encode (desc description.SelectedServer ) ([]wiremessage.WireMessage , error ) {
114
+ out := []wiremessage.WireMessage {}
115
+ batches , err := i .split (int (desc .MaxBatchCount ), int (desc .MaxDocumentSize ))
116
+ if err != nil {
117
+ return nil , err
118
+ }
119
+
120
+ for _ , docs := range batches {
121
+ cmd , err := i .encodeBatch (docs , desc )
122
+ if err != nil {
123
+ return nil , err
124
+ }
125
+
126
+ out = append (out , cmd )
127
+ }
128
+
129
+ return out , nil
130
+ }
131
+
56
132
// Decode will decode the wire message using the provided server description. Errors during decoding
57
133
// are deferred until either the Result or Err methods are called.
58
134
func (i * Insert ) Decode (desc description.SelectedServer , wm wiremessage.WireMessage ) * Insert {
@@ -79,18 +155,40 @@ func (i *Insert) Err() error { return i.err }
79
155
80
156
// RoundTrip handles the execution of this command using the provided wiremessage.ReadWriter.
81
157
func (i * Insert ) RoundTrip (ctx context.Context , desc description.SelectedServer , rw wiremessage.ReadWriter ) (result.Insert , error ) {
82
- wm , err := i .Encode (desc )
83
- if err != nil {
84
- return result.Insert {}, err
85
- }
158
+ res := result.Insert {}
86
159
87
- err = rw . WriteWireMessage ( ctx , wm )
160
+ wms , err := i . Encode ( desc )
88
161
if err != nil {
89
- return result. Insert {} , err
162
+ return res , err
90
163
}
91
- wm , err = rw .ReadWireMessage (ctx )
92
- if err != nil {
93
- return result.Insert {}, err
164
+
165
+ for _ , wm := range wms {
166
+ err = rw .WriteWireMessage (ctx , wm )
167
+ if err != nil {
168
+ return res , err
169
+ }
170
+ wm , err = rw .ReadWireMessage (ctx )
171
+ if err != nil {
172
+ return res , err
173
+ }
174
+
175
+ r , err := i .Decode (desc , wm ).Result ()
176
+ if err != nil {
177
+ return res , err
178
+ }
179
+
180
+ res .WriteErrors = append (res .WriteErrors , r .WriteErrors ... )
181
+
182
+ if r .WriteConcernError != nil {
183
+ res .WriteConcernError = r .WriteConcernError
184
+ }
185
+
186
+ if ! i .continueOnError && len (res .WriteErrors ) > 0 {
187
+ return res , nil
188
+ }
189
+
190
+ res .N += r .N
94
191
}
95
- return i .Decode (desc , wm ).Result ()
192
+
193
+ return res , nil
96
194
}
0 commit comments