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