github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/mongo/index_view.go (about) 1 // Copyright (C) MongoDB, Inc. 2017-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 mongo 8 9 import ( 10 "bytes" 11 "context" 12 "errors" 13 "fmt" 14 "strconv" 15 16 "go.mongodb.org/mongo-driver/bson" 17 "go.mongodb.org/mongo-driver/bson/bsontype" 18 "go.mongodb.org/mongo-driver/mongo/description" 19 "go.mongodb.org/mongo-driver/mongo/options" 20 "go.mongodb.org/mongo-driver/mongo/readpref" 21 "go.mongodb.org/mongo-driver/mongo/writeconcern" 22 "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" 23 "go.mongodb.org/mongo-driver/x/mongo/driver" 24 "go.mongodb.org/mongo-driver/x/mongo/driver/operation" 25 "go.mongodb.org/mongo-driver/x/mongo/driver/session" 26 ) 27 28 // ErrInvalidIndexValue is returned if an index is created with a keys document that has a value that is not a number 29 // or string. 30 var ErrInvalidIndexValue = errors.New("invalid index value") 31 32 // ErrNonStringIndexName is returned if an index is created with a name that is not a string. 33 var ErrNonStringIndexName = errors.New("index name must be a string") 34 35 // ErrMultipleIndexDrop is returned if multiple indexes would be dropped from a call to IndexView.DropOne. 36 var ErrMultipleIndexDrop = errors.New("multiple indexes would be dropped") 37 38 // IndexView is a type that can be used to create, drop, and list indexes on a collection. An IndexView for a collection 39 // can be created by a call to Collection.Indexes(). 40 type IndexView struct { 41 coll *Collection 42 } 43 44 // IndexModel represents a new index to be created. 45 type IndexModel struct { 46 // A document describing which keys should be used for the index. It cannot be nil. This must be an order-preserving 47 // type such as bson.D. Map types such as bson.M are not valid. See https://www.mongodb.com/docs/manual/indexes/#indexes 48 // for examples of valid documents. 49 Keys interface{} 50 51 // The options to use to create the index. 52 Options *options.IndexOptions 53 } 54 55 func isNamespaceNotFoundError(err error) bool { 56 if de, ok := err.(driver.Error); ok { 57 return de.Code == 26 58 } 59 return false 60 } 61 62 // List executes a listIndexes command and returns a cursor over the indexes in the collection. 63 // 64 // The opts parameter can be used to specify options for this operation (see the options.ListIndexesOptions 65 // documentation). 66 // 67 // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/listIndexes/. 68 func (iv IndexView) List(ctx context.Context, opts ...*options.ListIndexesOptions) (*Cursor, error) { 69 if ctx == nil { 70 ctx = context.Background() 71 } 72 73 sess := sessionFromContext(ctx) 74 if sess == nil && iv.coll.client.sessionPool != nil { 75 sess = session.NewImplicitClientSession(iv.coll.client.sessionPool, iv.coll.client.id) 76 } 77 78 err := iv.coll.client.validSession(sess) 79 if err != nil { 80 closeImplicitSession(sess) 81 return nil, err 82 } 83 84 selector := description.CompositeSelector([]description.ServerSelector{ 85 description.ReadPrefSelector(readpref.Primary()), 86 description.LatencySelector(iv.coll.client.localThreshold), 87 }) 88 selector = makeReadPrefSelector(sess, selector, iv.coll.client.localThreshold) 89 op := operation.NewListIndexes(). 90 Session(sess).CommandMonitor(iv.coll.client.monitor). 91 ServerSelector(selector).ClusterClock(iv.coll.client.clock). 92 Database(iv.coll.db.name).Collection(iv.coll.name). 93 Deployment(iv.coll.client.deployment).ServerAPI(iv.coll.client.serverAPI). 94 Timeout(iv.coll.client.timeout) 95 96 cursorOpts := iv.coll.client.createBaseCursorOptions() 97 lio := options.MergeListIndexesOptions(opts...) 98 if lio.BatchSize != nil { 99 op = op.BatchSize(*lio.BatchSize) 100 cursorOpts.BatchSize = *lio.BatchSize 101 } 102 op = op.MaxTime(lio.MaxTime) 103 retry := driver.RetryNone 104 if iv.coll.client.retryReads { 105 retry = driver.RetryOncePerCommand 106 } 107 op.Retry(retry) 108 109 err = op.Execute(ctx) 110 if err != nil { 111 // for namespaceNotFound errors, return an empty cursor and do not throw an error 112 closeImplicitSession(sess) 113 if isNamespaceNotFoundError(err) { 114 return newEmptyCursor(), nil 115 } 116 117 return nil, replaceErrors(err) 118 } 119 120 bc, err := op.Result(cursorOpts) 121 if err != nil { 122 closeImplicitSession(sess) 123 return nil, replaceErrors(err) 124 } 125 cursor, err := newCursorWithSession(bc, iv.coll.bsonOpts, iv.coll.registry, sess) 126 return cursor, replaceErrors(err) 127 } 128 129 // ListSpecifications executes a List command and returns a slice of returned IndexSpecifications 130 func (iv IndexView) ListSpecifications(ctx context.Context, opts ...*options.ListIndexesOptions) ([]*IndexSpecification, error) { 131 cursor, err := iv.List(ctx, opts...) 132 if err != nil { 133 return nil, err 134 } 135 136 var results []*IndexSpecification 137 err = cursor.All(ctx, &results) 138 if err != nil { 139 return nil, err 140 } 141 142 ns := iv.coll.db.Name() + "." + iv.coll.Name() 143 for _, res := range results { 144 // Pre-4.4 servers report a namespace in their responses, so we only set Namespace manually if it was not in 145 // the response. 146 res.Namespace = ns 147 } 148 149 return results, nil 150 } 151 152 // CreateOne executes a createIndexes command to create an index on the collection and returns the name of the new 153 // index. See the IndexView.CreateMany documentation for more information and an example. 154 func (iv IndexView) CreateOne(ctx context.Context, model IndexModel, opts ...*options.CreateIndexesOptions) (string, error) { 155 names, err := iv.CreateMany(ctx, []IndexModel{model}, opts...) 156 if err != nil { 157 return "", err 158 } 159 160 return names[0], nil 161 } 162 163 // CreateMany executes a createIndexes command to create multiple indexes on the collection and returns the names of 164 // the new indexes. 165 // 166 // For each IndexModel in the models parameter, the index name can be specified via the Options field. If a name is not 167 // given, it will be generated from the Keys document. 168 // 169 // The opts parameter can be used to specify options for this operation (see the options.CreateIndexesOptions 170 // documentation). 171 // 172 // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/createIndexes/. 173 func (iv IndexView) CreateMany(ctx context.Context, models []IndexModel, opts ...*options.CreateIndexesOptions) ([]string, error) { 174 names := make([]string, 0, len(models)) 175 176 var indexes bsoncore.Document 177 aidx, indexes := bsoncore.AppendArrayStart(indexes) 178 179 for i, model := range models { 180 if model.Keys == nil { 181 return nil, fmt.Errorf("index model keys cannot be nil") 182 } 183 184 if isUnorderedMap(model.Keys) { 185 return nil, ErrMapForOrderedArgument{"keys"} 186 } 187 188 keys, err := marshal(model.Keys, iv.coll.bsonOpts, iv.coll.registry) 189 if err != nil { 190 return nil, err 191 } 192 193 name, err := getOrGenerateIndexName(keys, model) 194 if err != nil { 195 return nil, err 196 } 197 198 names = append(names, name) 199 200 var iidx int32 201 iidx, indexes = bsoncore.AppendDocumentElementStart(indexes, strconv.Itoa(i)) 202 indexes = bsoncore.AppendDocumentElement(indexes, "key", keys) 203 204 if model.Options == nil { 205 model.Options = options.Index() 206 } 207 model.Options.SetName(name) 208 209 optsDoc, err := iv.createOptionsDoc(model.Options) 210 if err != nil { 211 return nil, err 212 } 213 214 indexes = bsoncore.AppendDocument(indexes, optsDoc) 215 216 indexes, err = bsoncore.AppendDocumentEnd(indexes, iidx) 217 if err != nil { 218 return nil, err 219 } 220 } 221 222 indexes, err := bsoncore.AppendArrayEnd(indexes, aidx) 223 if err != nil { 224 return nil, err 225 } 226 227 sess := sessionFromContext(ctx) 228 229 if sess == nil && iv.coll.client.sessionPool != nil { 230 sess = session.NewImplicitClientSession(iv.coll.client.sessionPool, iv.coll.client.id) 231 defer sess.EndSession() 232 } 233 234 err = iv.coll.client.validSession(sess) 235 if err != nil { 236 return nil, err 237 } 238 239 wc := iv.coll.writeConcern 240 if sess.TransactionRunning() { 241 wc = nil 242 } 243 if !writeconcern.AckWrite(wc) { 244 sess = nil 245 } 246 247 selector := makePinnedSelector(sess, iv.coll.writeSelector) 248 249 option := options.MergeCreateIndexesOptions(opts...) 250 251 op := operation.NewCreateIndexes(indexes). 252 Session(sess).WriteConcern(wc).ClusterClock(iv.coll.client.clock). 253 Database(iv.coll.db.name).Collection(iv.coll.name).CommandMonitor(iv.coll.client.monitor). 254 Deployment(iv.coll.client.deployment).ServerSelector(selector).ServerAPI(iv.coll.client.serverAPI). 255 Timeout(iv.coll.client.timeout).MaxTime(option.MaxTime) 256 if option.CommitQuorum != nil { 257 commitQuorum, err := marshalValue(option.CommitQuorum, iv.coll.bsonOpts, iv.coll.registry) 258 if err != nil { 259 return nil, err 260 } 261 262 op.CommitQuorum(commitQuorum) 263 } 264 265 err = op.Execute(ctx) 266 if err != nil { 267 _, err = processWriteError(err) 268 return nil, err 269 } 270 271 return names, nil 272 } 273 274 func (iv IndexView) createOptionsDoc(opts *options.IndexOptions) (bsoncore.Document, error) { 275 optsDoc := bsoncore.Document{} 276 if opts.Background != nil { 277 optsDoc = bsoncore.AppendBooleanElement(optsDoc, "background", *opts.Background) 278 } 279 if opts.ExpireAfterSeconds != nil { 280 optsDoc = bsoncore.AppendInt32Element(optsDoc, "expireAfterSeconds", *opts.ExpireAfterSeconds) 281 } 282 if opts.Name != nil { 283 optsDoc = bsoncore.AppendStringElement(optsDoc, "name", *opts.Name) 284 } 285 if opts.Sparse != nil { 286 optsDoc = bsoncore.AppendBooleanElement(optsDoc, "sparse", *opts.Sparse) 287 } 288 if opts.StorageEngine != nil { 289 doc, err := marshal(opts.StorageEngine, iv.coll.bsonOpts, iv.coll.registry) 290 if err != nil { 291 return nil, err 292 } 293 294 optsDoc = bsoncore.AppendDocumentElement(optsDoc, "storageEngine", doc) 295 } 296 if opts.Unique != nil { 297 optsDoc = bsoncore.AppendBooleanElement(optsDoc, "unique", *opts.Unique) 298 } 299 if opts.Version != nil { 300 optsDoc = bsoncore.AppendInt32Element(optsDoc, "v", *opts.Version) 301 } 302 if opts.DefaultLanguage != nil { 303 optsDoc = bsoncore.AppendStringElement(optsDoc, "default_language", *opts.DefaultLanguage) 304 } 305 if opts.LanguageOverride != nil { 306 optsDoc = bsoncore.AppendStringElement(optsDoc, "language_override", *opts.LanguageOverride) 307 } 308 if opts.TextVersion != nil { 309 optsDoc = bsoncore.AppendInt32Element(optsDoc, "textIndexVersion", *opts.TextVersion) 310 } 311 if opts.Weights != nil { 312 doc, err := marshal(opts.Weights, iv.coll.bsonOpts, iv.coll.registry) 313 if err != nil { 314 return nil, err 315 } 316 317 optsDoc = bsoncore.AppendDocumentElement(optsDoc, "weights", doc) 318 } 319 if opts.SphereVersion != nil { 320 optsDoc = bsoncore.AppendInt32Element(optsDoc, "2dsphereIndexVersion", *opts.SphereVersion) 321 } 322 if opts.Bits != nil { 323 optsDoc = bsoncore.AppendInt32Element(optsDoc, "bits", *opts.Bits) 324 } 325 if opts.Max != nil { 326 optsDoc = bsoncore.AppendDoubleElement(optsDoc, "max", *opts.Max) 327 } 328 if opts.Min != nil { 329 optsDoc = bsoncore.AppendDoubleElement(optsDoc, "min", *opts.Min) 330 } 331 if opts.BucketSize != nil { 332 optsDoc = bsoncore.AppendInt32Element(optsDoc, "bucketSize", *opts.BucketSize) 333 } 334 if opts.PartialFilterExpression != nil { 335 doc, err := marshal(opts.PartialFilterExpression, iv.coll.bsonOpts, iv.coll.registry) 336 if err != nil { 337 return nil, err 338 } 339 340 optsDoc = bsoncore.AppendDocumentElement(optsDoc, "partialFilterExpression", doc) 341 } 342 if opts.Collation != nil { 343 optsDoc = bsoncore.AppendDocumentElement(optsDoc, "collation", bsoncore.Document(opts.Collation.ToDocument())) 344 } 345 if opts.WildcardProjection != nil { 346 doc, err := marshal(opts.WildcardProjection, iv.coll.bsonOpts, iv.coll.registry) 347 if err != nil { 348 return nil, err 349 } 350 351 optsDoc = bsoncore.AppendDocumentElement(optsDoc, "wildcardProjection", doc) 352 } 353 if opts.Hidden != nil { 354 optsDoc = bsoncore.AppendBooleanElement(optsDoc, "hidden", *opts.Hidden) 355 } 356 357 return optsDoc, nil 358 } 359 360 func (iv IndexView) drop(ctx context.Context, name string, opts ...*options.DropIndexesOptions) (bson.Raw, error) { 361 if ctx == nil { 362 ctx = context.Background() 363 } 364 365 sess := sessionFromContext(ctx) 366 if sess == nil && iv.coll.client.sessionPool != nil { 367 sess = session.NewImplicitClientSession(iv.coll.client.sessionPool, iv.coll.client.id) 368 defer sess.EndSession() 369 } 370 371 err := iv.coll.client.validSession(sess) 372 if err != nil { 373 return nil, err 374 } 375 376 wc := iv.coll.writeConcern 377 if sess.TransactionRunning() { 378 wc = nil 379 } 380 if !writeconcern.AckWrite(wc) { 381 sess = nil 382 } 383 384 selector := makePinnedSelector(sess, iv.coll.writeSelector) 385 386 dio := options.MergeDropIndexesOptions(opts...) 387 op := operation.NewDropIndexes(name). 388 Session(sess).WriteConcern(wc).CommandMonitor(iv.coll.client.monitor). 389 ServerSelector(selector).ClusterClock(iv.coll.client.clock). 390 Database(iv.coll.db.name).Collection(iv.coll.name). 391 Deployment(iv.coll.client.deployment).ServerAPI(iv.coll.client.serverAPI). 392 Timeout(iv.coll.client.timeout).MaxTime(dio.MaxTime) 393 394 err = op.Execute(ctx) 395 if err != nil { 396 return nil, replaceErrors(err) 397 } 398 399 // TODO: it's weird to return a bson.Raw here because we have to convert the result back to BSON 400 ridx, res := bsoncore.AppendDocumentStart(nil) 401 res = bsoncore.AppendInt32Element(res, "nIndexesWas", op.Result().NIndexesWas) 402 res, _ = bsoncore.AppendDocumentEnd(res, ridx) 403 return res, nil 404 } 405 406 // DropOne executes a dropIndexes operation to drop an index on the collection. If the operation succeeds, this returns 407 // a BSON document in the form {nIndexesWas: <int32>}. The "nIndexesWas" field in the response contains the number of 408 // indexes that existed prior to the drop. 409 // 410 // The name parameter should be the name of the index to drop. If the name is "*", ErrMultipleIndexDrop will be returned 411 // without running the command because doing so would drop all indexes. 412 // 413 // The opts parameter can be used to specify options for this operation (see the options.DropIndexesOptions 414 // documentation). 415 // 416 // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/dropIndexes/. 417 func (iv IndexView) DropOne(ctx context.Context, name string, opts ...*options.DropIndexesOptions) (bson.Raw, error) { 418 if name == "*" { 419 return nil, ErrMultipleIndexDrop 420 } 421 422 return iv.drop(ctx, name, opts...) 423 } 424 425 // DropAll executes a dropIndexes operation to drop all indexes on the collection. If the operation succeeds, this 426 // returns a BSON document in the form {nIndexesWas: <int32>}. The "nIndexesWas" field in the response contains the 427 // number of indexes that existed prior to the drop. 428 // 429 // The opts parameter can be used to specify options for this operation (see the options.DropIndexesOptions 430 // documentation). 431 // 432 // For more information about the command, see https://www.mongodb.com/docs/manual/reference/command/dropIndexes/. 433 func (iv IndexView) DropAll(ctx context.Context, opts ...*options.DropIndexesOptions) (bson.Raw, error) { 434 return iv.drop(ctx, "*", opts...) 435 } 436 437 func getOrGenerateIndexName(keySpecDocument bsoncore.Document, model IndexModel) (string, error) { 438 if model.Options != nil && model.Options.Name != nil { 439 return *model.Options.Name, nil 440 } 441 442 name := bytes.NewBufferString("") 443 first := true 444 445 elems, err := keySpecDocument.Elements() 446 if err != nil { 447 return "", err 448 } 449 for _, elem := range elems { 450 if !first { 451 _, err := name.WriteRune('_') 452 if err != nil { 453 return "", err 454 } 455 } 456 457 _, err := name.WriteString(elem.Key()) 458 if err != nil { 459 return "", err 460 } 461 462 _, err = name.WriteRune('_') 463 if err != nil { 464 return "", err 465 } 466 467 var value string 468 469 bsonValue := elem.Value() 470 switch bsonValue.Type { 471 case bsontype.Int32: 472 value = fmt.Sprintf("%d", bsonValue.Int32()) 473 case bsontype.Int64: 474 value = fmt.Sprintf("%d", bsonValue.Int64()) 475 case bsontype.String: 476 value = bsonValue.StringValue() 477 default: 478 return "", ErrInvalidIndexValue 479 } 480 481 _, err = name.WriteString(value) 482 if err != nil { 483 return "", err 484 } 485 486 first = false 487 } 488 489 return name.String(), nil 490 }