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  }