1// Copyright (C) MongoDB, Inc. 2019-present. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7// NOTE: This file is maintained by hand because operationgen cannot generate it. 8 9package operation 10 11import ( 12 "context" 13 "errors" 14 "fmt" 15 16 "go.mongodb.org/mongo-driver/bson" 17 "go.mongodb.org/mongo-driver/event" 18 "go.mongodb.org/mongo-driver/mongo/description" 19 "go.mongodb.org/mongo-driver/mongo/writeconcern" 20 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" 21 "go.mongodb.org/mongo-driver/x/mongo/driver" 22 "go.mongodb.org/mongo-driver/x/mongo/driver/session" 23) 24 25// Update performs an update operation. 26type Update struct { 27 bypassDocumentValidation *bool 28 ordered *bool 29 updates []bsoncore.Document 30 session *session.Client 31 clock *session.ClusterClock 32 collection string 33 monitor *event.CommandMonitor 34 database string 35 deployment driver.Deployment 36 hint *bool 37 arrayFilters *bool 38 selector description.ServerSelector 39 writeConcern *writeconcern.WriteConcern 40 retry *driver.RetryMode 41 result UpdateResult 42 crypt *driver.Crypt 43} 44 45// Upsert contains the information for an upsert in an Update operation. 46type Upsert struct { 47 Index int64 48 ID interface{} `bson:"_id"` 49} 50 51// UpdateResult contains information for the result of an Update operation. 52type UpdateResult struct { 53 // Number of documents matched. 54 N int32 55 // Number of documents modified. 56 NModified int32 57 // Information about upserted documents. 58 Upserted []Upsert 59} 60 61func buildUpdateResult(response bsoncore.Document, srvr driver.Server) (UpdateResult, error) { 62 elements, err := response.Elements() 63 if err != nil { 64 return UpdateResult{}, err 65 } 66 ur := UpdateResult{} 67 for _, element := range elements { 68 switch element.Key() { 69 70 case "nModified": 71 var ok bool 72 ur.NModified, ok = element.Value().Int32OK() 73 if !ok { 74 err = fmt.Errorf("response field 'nModified' is type int32, but received BSON type %s", element.Value().Type) 75 } 76 77 case "n": 78 var ok bool 79 ur.N, ok = element.Value().Int32OK() 80 if !ok { 81 err = fmt.Errorf("response field 'n' is type int32, but received BSON type %s", element.Value().Type) 82 } 83 84 case "upserted": 85 arr, ok := element.Value().ArrayOK() 86 if !ok { 87 err = fmt.Errorf("response field 'upserted' is type array, but received BSON type %s", element.Value().Type) 88 break 89 } 90 91 var values []bsoncore.Value 92 values, err = arr.Values() 93 if err != nil { 94 break 95 } 96 97 for _, val := range values { 98 valDoc, ok := val.DocumentOK() 99 if !ok { 100 err = fmt.Errorf("upserted value is type document, but received BSON type %s", val.Type) 101 break 102 } 103 var upsert Upsert 104 if err = bson.Unmarshal(valDoc, &upsert); err != nil { 105 break 106 } 107 ur.Upserted = append(ur.Upserted, upsert) 108 } 109 } 110 } 111 return ur, nil 112} 113 114// NewUpdate constructs and returns a new Update. 115func NewUpdate(updates ...bsoncore.Document) *Update { 116 return &Update{ 117 updates: updates, 118 } 119} 120 121// Result returns the result of executing this operation. 122func (u *Update) Result() UpdateResult { return u.result } 123 124func (u *Update) processResponse(response bsoncore.Document, srvr driver.Server, desc description.Server, currIndex int) error { 125 ur, err := buildUpdateResult(response, srvr) 126 127 u.result.N += ur.N 128 u.result.NModified += ur.NModified 129 if currIndex > 0 { 130 for ind := range ur.Upserted { 131 ur.Upserted[ind].Index += int64(currIndex) 132 } 133 } 134 u.result.Upserted = append(u.result.Upserted, ur.Upserted...) 135 return err 136 137} 138 139// Execute runs this operations and returns an error if the operaiton did not execute successfully. 140func (u *Update) Execute(ctx context.Context) error { 141 if u.deployment == nil { 142 return errors.New("the Update operation must have a Deployment set before Execute can be called") 143 } 144 batches := &driver.Batches{ 145 Identifier: "updates", 146 Documents: u.updates, 147 Ordered: u.ordered, 148 } 149 150 return driver.Operation{ 151 CommandFn: u.command, 152 ProcessResponseFn: u.processResponse, 153 Batches: batches, 154 RetryMode: u.retry, 155 Type: driver.Write, 156 Client: u.session, 157 Clock: u.clock, 158 CommandMonitor: u.monitor, 159 Database: u.database, 160 Deployment: u.deployment, 161 Selector: u.selector, 162 WriteConcern: u.writeConcern, 163 Crypt: u.crypt, 164 }.Execute(ctx, nil) 165 166} 167 168func (u *Update) command(dst []byte, desc description.SelectedServer) ([]byte, error) { 169 dst = bsoncore.AppendStringElement(dst, "update", u.collection) 170 if u.bypassDocumentValidation != nil && 171 (desc.WireVersion != nil && desc.WireVersion.Includes(4)) { 172 173 dst = bsoncore.AppendBooleanElement(dst, "bypassDocumentValidation", *u.bypassDocumentValidation) 174 } 175 if u.ordered != nil { 176 177 dst = bsoncore.AppendBooleanElement(dst, "ordered", *u.ordered) 178 } 179 if u.hint != nil && *u.hint { 180 181 if desc.WireVersion == nil || !desc.WireVersion.Includes(5) { 182 return nil, errors.New("the 'hint' command parameter requires a minimum server wire version of 5") 183 } 184 if !u.writeConcern.Acknowledged() { 185 return nil, errUnacknowledgedHint 186 } 187 } 188 if u.arrayFilters != nil && *u.arrayFilters { 189 if desc.WireVersion == nil || !desc.WireVersion.Includes(6) { 190 return nil, errors.New("the 'arrayFilters' command parameter requires a minimum server wire version of 6") 191 } 192 } 193 194 return dst, nil 195} 196 197// BypassDocumentValidation allows the operation to opt-out of document level validation. Valid 198// for server versions >= 3.2. For servers < 3.2, this setting is ignored. 199func (u *Update) BypassDocumentValidation(bypassDocumentValidation bool) *Update { 200 if u == nil { 201 u = new(Update) 202 } 203 204 u.bypassDocumentValidation = &bypassDocumentValidation 205 return u 206} 207 208// Hint is a flag to indicate that the update document contains a hint. Hint is only supported by 209// servers >= 4.2. Older servers >= 3.4 will report an error for using the hint option. For servers < 210// 3.4, the driver will return an error if the hint option is used. 211func (u *Update) Hint(hint bool) *Update { 212 if u == nil { 213 u = new(Update) 214 } 215 216 u.hint = &hint 217 return u 218} 219 220// ArrayFilters is a flag to indicate that the update document contains an arrayFilters field. This option is only 221// supported on server versions 3.6 and higher. For servers < 3.6, the driver will return an error. 222func (u *Update) ArrayFilters(arrayFilters bool) *Update { 223 if u == nil { 224 u = new(Update) 225 } 226 227 u.arrayFilters = &arrayFilters 228 return u 229} 230 231// Ordered sets ordered. If true, when a write fails, the operation will return the error, when 232// false write failures do not stop execution of the operation. 233func (u *Update) Ordered(ordered bool) *Update { 234 if u == nil { 235 u = new(Update) 236 } 237 238 u.ordered = &ordered 239 return u 240} 241 242// Updates specifies an array of update statements to perform when this operation is executed. 243// Each update document must have the following structure: 244// {q: <query>, u: <update>, multi: <boolean>, collation: Optional<Document>, arrayFitlers: Optional<Array>, hint: Optional<string/Document>}. 245func (u *Update) Updates(updates ...bsoncore.Document) *Update { 246 if u == nil { 247 u = new(Update) 248 } 249 250 u.updates = updates 251 return u 252} 253 254// Session sets the session for this operation. 255func (u *Update) Session(session *session.Client) *Update { 256 if u == nil { 257 u = new(Update) 258 } 259 260 u.session = session 261 return u 262} 263 264// ClusterClock sets the cluster clock for this operation. 265func (u *Update) ClusterClock(clock *session.ClusterClock) *Update { 266 if u == nil { 267 u = new(Update) 268 } 269 270 u.clock = clock 271 return u 272} 273 274// Collection sets the collection that this command will run against. 275func (u *Update) Collection(collection string) *Update { 276 if u == nil { 277 u = new(Update) 278 } 279 280 u.collection = collection 281 return u 282} 283 284// CommandMonitor sets the monitor to use for APM events. 285func (u *Update) CommandMonitor(monitor *event.CommandMonitor) *Update { 286 if u == nil { 287 u = new(Update) 288 } 289 290 u.monitor = monitor 291 return u 292} 293 294// Database sets the database to run this operation against. 295func (u *Update) Database(database string) *Update { 296 if u == nil { 297 u = new(Update) 298 } 299 300 u.database = database 301 return u 302} 303 304// Deployment sets the deployment to use for this operation. 305func (u *Update) Deployment(deployment driver.Deployment) *Update { 306 if u == nil { 307 u = new(Update) 308 } 309 310 u.deployment = deployment 311 return u 312} 313 314// ServerSelector sets the selector used to retrieve a server. 315func (u *Update) ServerSelector(selector description.ServerSelector) *Update { 316 if u == nil { 317 u = new(Update) 318 } 319 320 u.selector = selector 321 return u 322} 323 324// WriteConcern sets the write concern for this operation. 325func (u *Update) WriteConcern(writeConcern *writeconcern.WriteConcern) *Update { 326 if u == nil { 327 u = new(Update) 328 } 329 330 u.writeConcern = writeConcern 331 return u 332} 333 334// Retry enables retryable writes for this operation. Retries are not handled automatically, 335// instead a boolean is returned from Execute and SelectAndExecute that indicates if the 336// operation can be retried. Retrying is handled by calling RetryExecute. 337func (u *Update) Retry(retry driver.RetryMode) *Update { 338 if u == nil { 339 u = new(Update) 340 } 341 342 u.retry = &retry 343 return u 344} 345 346// Crypt sets the Crypt object to use for automatic encryption and decryption. 347func (u *Update) Crypt(crypt *driver.Crypt) *Update { 348 if u == nil { 349 u = new(Update) 350 } 351 352 u.crypt = crypt 353 return u 354} 355