...

Source file src/go.mongodb.org/mongo-driver/mongo/integration/index_view_test.go

Documentation: go.mongodb.org/mongo-driver/mongo/integration

     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 integration
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/google/go-cmp/cmp"
    16  	"go.mongodb.org/mongo-driver/bson"
    17  	"go.mongodb.org/mongo-driver/internal/assert"
    18  	"go.mongodb.org/mongo-driver/mongo"
    19  	"go.mongodb.org/mongo-driver/mongo/integration/mtest"
    20  	"go.mongodb.org/mongo-driver/mongo/options"
    21  	"go.mongodb.org/mongo-driver/mongo/writeconcern"
    22  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    23  )
    24  
    25  type index struct {
    26  	Key  bson.D
    27  	Name string
    28  }
    29  
    30  func TestIndexView(t *testing.T) {
    31  	mt := mtest.New(t, noClientOpts)
    32  
    33  	var pbool = func(b bool) *bool { return &b }
    34  	var pint32 = func(i int32) *int32 { return &i }
    35  
    36  	mt.Run("list", func(mt *mtest.T) {
    37  		createIndexes := func(mt *mtest.T, numIndexes int) {
    38  			mt.Helper()
    39  
    40  			models := make([]mongo.IndexModel, 0, numIndexes)
    41  			for i, key := 0, 'a'; i < numIndexes; i, key = i+1, key+1 {
    42  				models = append(models, mongo.IndexModel{
    43  					Keys: bson.M{string(key): 1},
    44  				})
    45  			}
    46  
    47  			_, err := mt.Coll.Indexes().CreateMany(context.Background(), models)
    48  			assert.Nil(mt, err, "CreateMany error: %v", err)
    49  		}
    50  
    51  		// For server versions below 3.0, we internally execute List() as a legacy OP_QUERY against the system.indexes
    52  		// collection. Command monitoring upconversions translate this to a "find" command rather than "listIndexes".
    53  		cmdName := "listIndexes"
    54  		if mtest.CompareServerVersions(mtest.ServerVersion(), "3.0") < 0 {
    55  			cmdName = "find"
    56  		}
    57  
    58  		mt.Run("_id index is always listed", func(mt *mtest.T) {
    59  			verifyIndexExists(mt, mt.Coll.Indexes(), index{
    60  				Key:  bson.D{{"_id", int32(1)}},
    61  				Name: "_id_",
    62  			})
    63  		})
    64  		mt.Run("getMore commands are monitored", func(mt *mtest.T) {
    65  			createIndexes(mt, 2)
    66  			assertGetMoreCommandsAreMonitored(mt, cmdName, func() (*mongo.Cursor, error) {
    67  				return mt.Coll.Indexes().List(context.Background(), options.ListIndexes().SetBatchSize(2))
    68  			})
    69  		})
    70  		mt.Run("killCursors commands are monitored", func(mt *mtest.T) {
    71  			createIndexes(mt, 2)
    72  			assertKillCursorsCommandsAreMonitored(mt, cmdName, func() (*mongo.Cursor, error) {
    73  				return mt.Coll.Indexes().List(context.Background(), options.ListIndexes().SetBatchSize(2))
    74  			})
    75  		})
    76  	})
    77  	mt.RunOpts("create one", noClientOpts, func(mt *mtest.T) {
    78  		mt.Run("name not specified", func(mt *mtest.T) {
    79  			iv := mt.Coll.Indexes()
    80  			keysDoc := bson.D{
    81  				{"foo", int32(1)},
    82  				{"bar", int32(-1)},
    83  			}
    84  			expectedName := "foo_1_bar_-1"
    85  
    86  			indexName, err := iv.CreateOne(context.Background(), mongo.IndexModel{
    87  				Keys: keysDoc,
    88  			})
    89  			assert.Nil(mt, err, "CreateOne error: %v", err)
    90  			assert.Equal(mt, expectedName, indexName, "expected name %q, got %q", expectedName, indexName)
    91  
    92  			verifyIndexExists(mt, iv, index{
    93  				Key:  keysDoc,
    94  				Name: indexName,
    95  			})
    96  		})
    97  		mt.Run("specify name", func(mt *mtest.T) {
    98  			iv := mt.Coll.Indexes()
    99  			keysDoc := bson.D{{"foo", int32(-1)}}
   100  			name := "testname"
   101  
   102  			indexName, err := iv.CreateOne(context.Background(), mongo.IndexModel{
   103  				Keys:    keysDoc,
   104  				Options: options.Index().SetName(name),
   105  			})
   106  			assert.Nil(mt, err, "CreateOne error: %v", err)
   107  			assert.Equal(mt, name, indexName, "expected returned name %q, got %q", name, indexName)
   108  
   109  			verifyIndexExists(mt, iv, index{
   110  				Key:  keysDoc,
   111  				Name: indexName,
   112  			})
   113  		})
   114  		mt.Run("all options", func(mt *mtest.T) {
   115  			opts := options.Index().
   116  				SetBackground(false).
   117  				SetExpireAfterSeconds(10).
   118  				SetName("a").
   119  				SetSparse(false).
   120  				SetUnique(false).
   121  				SetVersion(1).
   122  				SetDefaultLanguage("english").
   123  				SetLanguageOverride("english").
   124  				SetTextVersion(1).
   125  				SetWeights(bson.D{}).
   126  				SetSphereVersion(1).
   127  				SetBits(2).
   128  				SetMax(10).
   129  				SetMin(1).
   130  				SetPartialFilterExpression(bson.D{}).
   131  				SetStorageEngine(bson.D{
   132  					{"wiredTiger", bson.D{
   133  						{"configString", "block_compressor=zlib"},
   134  					}},
   135  				})
   136  
   137  			// Only check SetBucketSize if version is less than 4.9
   138  			if mtest.CompareServerVersions(mtest.ServerVersion(), "4.9") < 0 {
   139  				opts.SetBucketSize(1)
   140  			}
   141  			// Omits collation option because it's incompatible with version option
   142  			_, err := mt.Coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
   143  				Keys:    bson.D{{"foo", "text"}},
   144  				Options: opts,
   145  			})
   146  			assert.Nil(mt, err, "CreateOne error: %v", err)
   147  		})
   148  		mt.RunOpts("collation", mtest.NewOptions().MinServerVersion("3.4"), func(mt *mtest.T) {
   149  			// collation invalid for server versions < 3.4
   150  			_, err := mt.Coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
   151  				Keys: bson.D{{"bar", "text"}},
   152  				Options: options.Index().SetCollation(&options.Collation{
   153  					Locale: "simple",
   154  				}),
   155  			})
   156  			assert.Nil(mt, err, "CreateOne error: %v", err)
   157  		})
   158  		mt.RunOpts("wildcard", mtest.NewOptions().MinServerVersion("4.1").CreateClient(false), func(mt *mtest.T) {
   159  			keysDoc := bson.D{{"$**", int32(1)}}
   160  
   161  			mt.Run("no options", func(mt *mtest.T) {
   162  				iv := mt.Coll.Indexes()
   163  				indexName, err := iv.CreateOne(context.Background(), mongo.IndexModel{
   164  					Keys: keysDoc,
   165  				})
   166  				assert.Nil(mt, err, "CreateOne error: %v", err)
   167  				verifyIndexExists(mt, iv, index{
   168  					Key:  keysDoc,
   169  					Name: indexName,
   170  				})
   171  			})
   172  			mt.Run("wildcard projection", func(mt *mtest.T) {
   173  				iv := mt.Coll.Indexes()
   174  
   175  				// Create an index with a wildcard projection document. The "_id: false" isn't needed to create the
   176  				// index. We use the listIndexes command below to assert that the created index has this projection
   177  				// document and the format of the document returned by listIndexes was changed in 4.5.x to explicitly
   178  				// include "_id: false", so we include it here too.
   179  				proj := bson.D{{"a", true}, {"_id", false}}
   180  				_, err := iv.CreateOne(context.Background(), mongo.IndexModel{
   181  					Keys:    keysDoc,
   182  					Options: options.Index().SetWildcardProjection(proj),
   183  				})
   184  				assert.Nil(mt, err, "CreateOne error: %v", err)
   185  
   186  				indexDoc := getIndexDoc(mt, iv, keysDoc)
   187  				assert.NotNil(mt, indexDoc, "expected to find keys document %v but was not found", keysDoc)
   188  				checkIndexDocContains(mt, indexDoc, bson.E{
   189  					Key:   "wildcardProjection",
   190  					Value: proj,
   191  				})
   192  			})
   193  		})
   194  		mt.RunOpts("hidden", mtest.NewOptions().MinServerVersion("4.4"), func(mt *mtest.T) {
   195  			iv := mt.Coll.Indexes()
   196  			keysDoc := bson.D{{"x", int32(1)}}
   197  			model := mongo.IndexModel{
   198  				Keys:    keysDoc,
   199  				Options: options.Index().SetHidden(true),
   200  			}
   201  
   202  			_, err := iv.CreateOne(context.Background(), model)
   203  			assert.Nil(mt, err, "CreateOne error: %v", err)
   204  
   205  			indexDoc := getIndexDoc(mt, iv, keysDoc)
   206  			assert.NotNil(mt, indexDoc, "index with keys document %v was not found", keysDoc)
   207  			checkIndexDocContains(mt, indexDoc, bson.E{
   208  				Key:   "hidden",
   209  				Value: true,
   210  			})
   211  		})
   212  		mt.Run("nil keys", func(mt *mtest.T) {
   213  			_, err := mt.Coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
   214  				Keys: nil,
   215  			})
   216  			assert.NotNil(mt, err, "expected CreateOne error, got nil")
   217  		})
   218  		// Only run on replica sets as commitQuorum is not supported on standalones.
   219  		mt.RunOpts("commit quorum", mtest.NewOptions().Topologies(mtest.ReplicaSet).CreateClient(false), func(mt *mtest.T) {
   220  			intVal := options.CreateIndexes().SetCommitQuorumInt(1)
   221  			stringVal := options.CreateIndexes().SetCommitQuorumString("majority")
   222  			majority := options.CreateIndexes().SetCommitQuorumMajority()
   223  			votingMembers := options.CreateIndexes().SetCommitQuorumVotingMembers()
   224  
   225  			indexModel := mongo.IndexModel{
   226  				Keys: bson.D{{"x", 1}},
   227  			}
   228  
   229  			testCases := []struct {
   230  				name             string
   231  				opts             *options.CreateIndexesOptions
   232  				expectError      bool
   233  				expectedValue    interface{} // ignored if expectError is true
   234  				minServerVersion string
   235  				maxServerVersion string
   236  			}{
   237  				{"error on server versions before 4.4", majority, true, nil, "", "4.2"},
   238  				{"integer value", intVal, false, int32(1), "4.4", ""},
   239  				{"string value", stringVal, false, "majority", "4.4", ""},
   240  				{"majority", majority, false, "majority", "4.4", ""},
   241  				{"votingMembers", votingMembers, false, "votingMembers", "4.4", ""},
   242  			}
   243  			for _, tc := range testCases {
   244  				mtOpts := mtest.NewOptions().MinServerVersion(tc.minServerVersion).MaxServerVersion(tc.maxServerVersion)
   245  				mt.RunOpts(tc.name, mtOpts, func(mt *mtest.T) {
   246  					mt.ClearEvents()
   247  					_, err := mt.Coll.Indexes().CreateOne(context.Background(), indexModel, tc.opts)
   248  					if tc.expectError {
   249  						assert.NotNil(mt, err, "expected CreateOne error, got nil")
   250  						return
   251  					}
   252  
   253  					assert.Nil(mt, err, "CreateOne error: %v", err)
   254  					cmd := mt.GetStartedEvent().Command
   255  					sentBSONValue, err := cmd.LookupErr("commitQuorum")
   256  					assert.Nil(mt, err, "expected commitQuorum in command %s", cmd)
   257  
   258  					var sentValue interface{}
   259  					err = sentBSONValue.Unmarshal(&sentValue)
   260  					assert.Nil(mt, err, "Unmarshal error: %v", err)
   261  
   262  					assert.Equal(mt, tc.expectedValue, sentValue, "expected commitQuorum value %v, got %v",
   263  						tc.expectedValue, sentValue)
   264  				})
   265  			}
   266  		})
   267  		unackClientOpts := options.Client().
   268  			SetWriteConcern(writeconcern.New(writeconcern.W(0)))
   269  		unackMtOpts := mtest.NewOptions().
   270  			ClientOptions(unackClientOpts).
   271  			MinServerVersion("3.6")
   272  		mt.RunOpts("unacknowledged write", unackMtOpts, func(mt *mtest.T) {
   273  			_, err := mt.Coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{Keys: bson.D{{"x", 1}}})
   274  			if !errors.Is(err, mongo.ErrUnacknowledgedWrite) {
   275  				// Use a direct comparison rather than assert.Equal because assert.Equal will compare the error strings,
   276  				// so the assertion would succeed even if the error had not been wrapped.
   277  				mt.Fatalf("expected CreateOne error %v, got %v", mongo.ErrUnacknowledgedWrite, err)
   278  			}
   279  		})
   280  		// Needs to run on these versions for failpoints
   281  		mt.RunOpts("replace error", mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion("4.0"), func(mt *mtest.T) {
   282  			mt.SetFailPoint(mtest.FailPoint{
   283  				ConfigureFailPoint: "failCommand",
   284  				Mode:               "alwaysOn",
   285  				Data: mtest.FailPointData{
   286  					FailCommands: []string{"createIndexes"},
   287  					ErrorCode:    100,
   288  				},
   289  			})
   290  
   291  			_, err := mt.Coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{Keys: bson.D{{"x", 1}}})
   292  			assert.NotNil(mt, err, "expected CreateOne error, got nil")
   293  			cmdErr, ok := err.(mongo.CommandError)
   294  			assert.True(mt, ok, "expected mongo.CommandError, got %T", err)
   295  			assert.Equal(mt, int32(100), cmdErr.Code, "expected error code 100, got %v", cmdErr.Code)
   296  
   297  		})
   298  		mt.Run("multi-key map", func(mt *mtest.T) {
   299  			iv := mt.Coll.Indexes()
   300  
   301  			_, err := iv.CreateOne(context.Background(), mongo.IndexModel{
   302  				Keys: bson.M{"foo": 1, "bar": -1},
   303  			})
   304  			assert.NotNil(mt, err, "expected CreateOne error, got nil")
   305  			assert.Equal(mt, mongo.ErrMapForOrderedArgument{"keys"}, err, "expected error %v, got %v", mongo.ErrMapForOrderedArgument{"key"}, err)
   306  		})
   307  		mt.Run("single key map", func(mt *mtest.T) {
   308  			iv := mt.Coll.Indexes()
   309  			expectedName := "foo_1"
   310  
   311  			indexName, err := iv.CreateOne(context.Background(), mongo.IndexModel{
   312  				Keys: bson.M{"foo": 1},
   313  			})
   314  			assert.Nil(mt, err, "CreateOne error: %v", err)
   315  			assert.Equal(mt, expectedName, indexName, "expected name %q, got %q", expectedName, indexName)
   316  
   317  			verifyIndexExists(mt, iv, index{
   318  				Key:  bson.D{{"foo", int32(1)}},
   319  				Name: indexName,
   320  			})
   321  		})
   322  	})
   323  	mt.Run("create many", func(mt *mtest.T) {
   324  		mt.Run("success", func(mt *mtest.T) {
   325  			iv := mt.Coll.Indexes()
   326  			firstKeysDoc := bson.D{{"foo", int32(-1)}}
   327  			secondKeysDoc := bson.D{{"bar", int32(1)}, {"baz", int32(-1)}}
   328  			expectedNames := []string{"foo_-1", "bar_1_baz_-1"}
   329  			indexNames, err := iv.CreateMany(context.Background(), []mongo.IndexModel{
   330  				{
   331  					Keys: firstKeysDoc,
   332  				},
   333  				{
   334  					Keys: secondKeysDoc,
   335  				},
   336  			})
   337  			assert.Nil(mt, err, "CreateMany error: %v", err)
   338  			assert.Equal(mt, expectedNames, indexNames, "expected returned names %v, got %v", expectedNames, indexNames)
   339  
   340  			verifyIndexExists(mt, iv, index{
   341  				Key:  firstKeysDoc,
   342  				Name: indexNames[0],
   343  			})
   344  			verifyIndexExists(mt, iv, index{
   345  				Key:  secondKeysDoc,
   346  				Name: indexNames[1],
   347  			})
   348  		})
   349  		wc := writeconcern.New(writeconcern.W(1))
   350  		wcMtOpts := mtest.NewOptions().CollectionOptions(options.Collection().SetWriteConcern(wc))
   351  		mt.RunOpts("uses writeconcern", wcMtOpts, func(mt *mtest.T) {
   352  			iv := mt.Coll.Indexes()
   353  			_, err := iv.CreateMany(context.Background(), []mongo.IndexModel{
   354  				{
   355  					Keys: bson.D{{"foo", -1}},
   356  				},
   357  				{
   358  					Keys: bson.D{{"bar", 1}, {"baz", -1}},
   359  				},
   360  			})
   361  			assert.Nil(mt, err, "CreateMany error: %v", err)
   362  
   363  			evt := mt.GetStartedEvent()
   364  			assert.NotNil(mt, evt, "expected CommandStartedEvent, got nil")
   365  
   366  			assert.Equal(mt, "createIndexes", evt.CommandName, "command name mismatch; expected createIndexes, got %s", evt.CommandName)
   367  
   368  			actual, err := evt.Command.LookupErr("writeConcern", "w")
   369  			assert.Nil(mt, err, "error getting writeConcern.w: %s", err)
   370  
   371  			wcVal := numberFromValue(mt, actual)
   372  			assert.Equal(mt, int64(1), wcVal, "expected writeConcern to be 1, got: %v", wcVal)
   373  		})
   374  		// Only run on replica sets as commitQuorum is not supported on standalones.
   375  		mt.RunOpts("commit quorum", mtest.NewOptions().Topologies(mtest.ReplicaSet).CreateClient(false), func(mt *mtest.T) {
   376  			intVal := options.CreateIndexes().SetCommitQuorumInt(1)
   377  			stringVal := options.CreateIndexes().SetCommitQuorumString("majority")
   378  			majority := options.CreateIndexes().SetCommitQuorumMajority()
   379  			votingMembers := options.CreateIndexes().SetCommitQuorumVotingMembers()
   380  
   381  			indexModel1 := mongo.IndexModel{
   382  				Keys: bson.D{{"x", 1}},
   383  			}
   384  			indexModel2 := mongo.IndexModel{
   385  				Keys: bson.D{{"y", 1}},
   386  			}
   387  
   388  			testCases := []struct {
   389  				name             string
   390  				opts             *options.CreateIndexesOptions
   391  				expectError      bool
   392  				expectedValue    interface{} // ignored if expectError is true
   393  				minServerVersion string
   394  				maxServerVersion string
   395  			}{
   396  				{"error on server versions before 4.4", majority, true, nil, "", "4.2"},
   397  				{"integer value", intVal, false, int32(1), "4.4", ""},
   398  				{"string value", stringVal, false, "majority", "4.4", ""},
   399  				{"majority", majority, false, "majority", "4.4", ""},
   400  				{"votingMembers", votingMembers, false, "votingMembers", "4.4", ""},
   401  			}
   402  			for _, tc := range testCases {
   403  				mtOpts := mtest.NewOptions().MinServerVersion(tc.minServerVersion).MaxServerVersion(tc.maxServerVersion)
   404  				mt.RunOpts(tc.name, mtOpts, func(mt *mtest.T) {
   405  					mt.ClearEvents()
   406  					_, err := mt.Coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{indexModel1, indexModel2}, tc.opts)
   407  					if tc.expectError {
   408  						assert.NotNil(mt, err, "expected CreateMany error, got nil")
   409  						return
   410  					}
   411  
   412  					assert.Nil(mt, err, "CreateMany error: %v", err)
   413  					cmd := mt.GetStartedEvent().Command
   414  					sentBSONValue, err := cmd.LookupErr("commitQuorum")
   415  					assert.Nil(mt, err, "expected commitQuorum in command %s", cmd)
   416  
   417  					var sentValue interface{}
   418  					err = sentBSONValue.Unmarshal(&sentValue)
   419  					assert.Nil(mt, err, "Unmarshal error: %v", err)
   420  
   421  					assert.Equal(mt, tc.expectedValue, sentValue, "expected commitQuorum value %v, got %v",
   422  						tc.expectedValue, sentValue)
   423  				})
   424  			}
   425  		})
   426  		// Needs to run on these versions for failpoints
   427  		mt.RunOpts("replace error", mtest.NewOptions().Topologies(mtest.ReplicaSet).MinServerVersion("4.0"), func(mt *mtest.T) {
   428  			mt.SetFailPoint(mtest.FailPoint{
   429  				ConfigureFailPoint: "failCommand",
   430  				Mode:               "alwaysOn",
   431  				Data: mtest.FailPointData{
   432  					FailCommands: []string{"createIndexes"},
   433  					ErrorCode:    100,
   434  				},
   435  			})
   436  
   437  			_, err := mt.Coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
   438  				{
   439  					Keys: bson.D{{"foo", int32(-1)}},
   440  				},
   441  				{
   442  					Keys: bson.D{{"bar", int32(1)}, {"baz", int32(-1)}},
   443  				},
   444  			})
   445  			assert.NotNil(mt, err, "expected CreateMany error, got nil")
   446  			cmdErr, ok := err.(mongo.CommandError)
   447  			assert.True(mt, ok, "expected mongo.CommandError, got %T", err)
   448  			assert.Equal(mt, int32(100), cmdErr.Code, "expected error code 100, got %v", cmdErr.Code)
   449  
   450  		})
   451  		mt.Run("multi-key map", func(mt *mtest.T) {
   452  			iv := mt.Coll.Indexes()
   453  			_, err := iv.CreateMany(context.Background(), []mongo.IndexModel{
   454  				{
   455  					Keys: bson.M{"foo": 1, "bar": -1},
   456  				},
   457  				{
   458  					Keys: bson.D{{"bar", int32(1)}, {"baz", int32(-1)}},
   459  				},
   460  			})
   461  			assert.NotNil(mt, err, "expected CreateOne error, got nil")
   462  			assert.Equal(mt, mongo.ErrMapForOrderedArgument{"keys"}, err, "expected error %v, got %v", mongo.ErrMapForOrderedArgument{"keys"}, err)
   463  		})
   464  		mt.Run("single key map", func(mt *mtest.T) {
   465  			iv := mt.Coll.Indexes()
   466  			firstKeysDoc := bson.M{"foo": -1}
   467  			secondKeysDoc := bson.D{{"bar", int32(1)}, {"baz", int32(-1)}}
   468  			expectedNames := []string{"foo_-1", "bar_1_baz_-1"}
   469  			indexNames, err := iv.CreateMany(context.Background(), []mongo.IndexModel{
   470  				{
   471  					Keys: firstKeysDoc,
   472  				},
   473  				{
   474  					Keys: secondKeysDoc,
   475  				},
   476  			})
   477  			assert.Nil(mt, err, "CreateMany error: %v", err)
   478  			assert.Equal(mt, expectedNames, indexNames, "expected returned names %v, got %v", expectedNames, indexNames)
   479  
   480  			verifyIndexExists(mt, iv, index{
   481  				Key:  bson.D{{"foo", int32(-1)}},
   482  				Name: indexNames[0],
   483  			})
   484  			verifyIndexExists(mt, iv, index{
   485  				Key:  secondKeysDoc,
   486  				Name: indexNames[1],
   487  			})
   488  		})
   489  	})
   490  	mt.RunOpts("list specifications", noClientOpts, func(mt *mtest.T) {
   491  		mt.Run("verify results", func(mt *mtest.T) {
   492  			// Create a handful of indexes
   493  			_, err := mt.Coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
   494  				{
   495  					Keys:    bson.D{{"foo", int32(-1)}},
   496  					Options: options.Index().SetUnique(true),
   497  				},
   498  				{
   499  					Keys:    bson.D{{"bar", int32(1)}},
   500  					Options: options.Index().SetExpireAfterSeconds(120),
   501  				},
   502  				{
   503  					Keys:    bson.D{{"baz", int32(1)}},
   504  					Options: options.Index().SetSparse(true),
   505  				},
   506  				{
   507  					Keys: bson.D{{"bar", int32(1)}, {"baz", int32(-1)}},
   508  				},
   509  			})
   510  			assert.Nil(mt, err, "CreateMany error: %v", err)
   511  
   512  			expectedSpecs := []*mongo.IndexSpecification{
   513  				{
   514  					Name:               "_id_",
   515  					Namespace:          mt.DB.Name() + "." + mt.Coll.Name(),
   516  					KeysDocument:       bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32("_id", 1).Build()),
   517  					Version:            2,
   518  					ExpireAfterSeconds: nil,
   519  					Sparse:             nil,
   520  					// ID index is special and does not return 'true', despite being unique.
   521  					Unique: nil,
   522  				},
   523  				{
   524  					Name:               "foo_-1",
   525  					Namespace:          mt.DB.Name() + "." + mt.Coll.Name(),
   526  					KeysDocument:       bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32("foo", -1).Build()),
   527  					Version:            2,
   528  					ExpireAfterSeconds: nil,
   529  					Sparse:             nil,
   530  					Unique:             pbool(true),
   531  				},
   532  				{
   533  					Name:               "bar_1",
   534  					Namespace:          mt.DB.Name() + "." + mt.Coll.Name(),
   535  					KeysDocument:       bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32("bar", 1).Build()),
   536  					Version:            2,
   537  					ExpireAfterSeconds: pint32(120),
   538  					Sparse:             nil,
   539  					Unique:             nil,
   540  				},
   541  				{
   542  					Name:               "baz_1",
   543  					Namespace:          mt.DB.Name() + "." + mt.Coll.Name(),
   544  					KeysDocument:       bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32("baz", 1).Build()),
   545  					Version:            2,
   546  					ExpireAfterSeconds: nil,
   547  					Sparse:             pbool(true),
   548  					Unique:             nil,
   549  				},
   550  				{
   551  					Name:               "bar_1_baz_-1",
   552  					Namespace:          mt.DB.Name() + "." + mt.Coll.Name(),
   553  					KeysDocument:       bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32("bar", 1).AppendInt32("baz", -1).Build()),
   554  					Version:            2,
   555  					ExpireAfterSeconds: nil,
   556  					Sparse:             nil,
   557  					Unique:             nil,
   558  				},
   559  			}
   560  			if mtest.CompareServerVersions(mtest.ServerVersion(), "3.4") < 0 {
   561  				for _, expectedSpec := range expectedSpecs {
   562  					expectedSpec.Version = 1
   563  				}
   564  			}
   565  
   566  			specs, err := mt.Coll.Indexes().ListSpecifications(context.Background())
   567  			assert.Nil(mt, err, "ListSpecifications error: %v", err)
   568  			assert.Equal(mt, len(expectedSpecs), len(specs), "expected %d specification, got %d", len(expectedSpecs), len(specs))
   569  			assert.True(mt, cmp.Equal(specs, expectedSpecs), "expected specifications to match: %v", cmp.Diff(specs, expectedSpecs))
   570  		})
   571  		mt.RunOpts("options passed to listIndexes", mtest.NewOptions().MinServerVersion("3.0"), func(mt *mtest.T) {
   572  			opts := options.ListIndexes().SetMaxTime(100 * time.Millisecond)
   573  			_, err := mt.Coll.Indexes().ListSpecifications(context.Background(), opts)
   574  			assert.Nil(mt, err, "ListSpecifications error: %v", err)
   575  
   576  			evt := mt.GetStartedEvent()
   577  			assert.Equal(mt, evt.CommandName, "listIndexes", "expected %q command to be sent, got %q", "listIndexes",
   578  				evt.CommandName)
   579  			maxTimeMS, ok := evt.Command.Lookup("maxTimeMS").Int64OK()
   580  			assert.True(mt, ok, "expected command %v to contain %q field", evt.Command, "maxTimeMS")
   581  			assert.Equal(mt, int64(100), maxTimeMS, "expected maxTimeMS value to be 100, got %d", maxTimeMS)
   582  		})
   583  	})
   584  	mt.Run("drop one", func(mt *mtest.T) {
   585  		iv := mt.Coll.Indexes()
   586  		indexNames, err := iv.CreateMany(context.Background(), []mongo.IndexModel{
   587  			{
   588  				Keys: bson.D{{"foo", -1}},
   589  			},
   590  			{
   591  				Keys: bson.D{{"bar", 1}, {"baz", -1}},
   592  			},
   593  		})
   594  		assert.Nil(mt, err, "CreateMany error: %v", err)
   595  		assert.Equal(mt, 2, len(indexNames), "expected 2 index names, got %v", len(indexNames))
   596  
   597  		_, err = iv.DropOne(context.Background(), indexNames[1])
   598  		assert.Nil(mt, err, "DropOne error: %v", err)
   599  
   600  		cursor, err := iv.List(context.Background())
   601  		assert.Nil(mt, err, "List error: %v", err)
   602  		for cursor.Next(context.Background()) {
   603  			var idx index
   604  			err = cursor.Decode(&idx)
   605  			assert.Nil(mt, err, "Decode error: %v (document %v)", err, cursor.Current)
   606  			assert.NotEqual(mt, indexNames[1], idx.Name, "found index %v after dropping", indexNames[1])
   607  		}
   608  		assert.Nil(mt, cursor.Err(), "cursor error: %v", cursor.Err())
   609  	})
   610  	mt.Run("drop all", func(mt *mtest.T) {
   611  		iv := mt.Coll.Indexes()
   612  		names, err := iv.CreateMany(context.Background(), []mongo.IndexModel{
   613  			{
   614  				Keys: bson.D{{"foo", -1}},
   615  			},
   616  			{
   617  				Keys: bson.D{{"bar", 1}, {"baz", -1}},
   618  			},
   619  		})
   620  		assert.Nil(mt, err, "CreateMany error: %v", err)
   621  		assert.Equal(mt, 2, len(names), "expected 2 index names, got %v", len(names))
   622  		_, err = iv.DropAll(context.Background())
   623  		assert.Nil(mt, err, "DropAll error: %v", err)
   624  
   625  		cursor, err := iv.List(context.Background())
   626  		assert.Nil(mt, err, "List error: %v", err)
   627  		for cursor.Next(context.Background()) {
   628  			var idx index
   629  			err = cursor.Decode(&idx)
   630  			assert.Nil(mt, err, "Decode error: %v (document %v)", err, cursor.Current)
   631  			assert.NotEqual(mt, names[0], idx.Name, "found index %v, after dropping", names[0])
   632  			assert.NotEqual(mt, names[1], idx.Name, "found index %v, after dropping", names[1])
   633  		}
   634  		assert.Nil(mt, cursor.Err(), "cursor error: %v", cursor.Err())
   635  	})
   636  	mt.RunOpts("clustered indexes", mtest.NewOptions().MinServerVersion("5.3"), func(mt *mtest.T) {
   637  		const name = "clustered"
   638  		clustered := mt.CreateCollection(mtest.Collection{
   639  			Name:       name,
   640  			CreateOpts: options.CreateCollection().SetClusteredIndex(bson.D{{"key", bson.D{{"_id", 1}}}, {"unique", true}}),
   641  		}, true)
   642  		mt.Run("create one", func(mt *mtest.T) {
   643  			_, err := clustered.Indexes().CreateOne(context.Background(), mongo.IndexModel{
   644  				Keys: bson.D{{"foo", int32(-1)}},
   645  			})
   646  			assert.Nil(mt, err, "CreateOne error: %v", err)
   647  			specs, err := clustered.Indexes().ListSpecifications(context.Background())
   648  			assert.Nil(mt, err, "ListSpecifications error: %v", err)
   649  			expectedSpecs := []*mongo.IndexSpecification{
   650  				{
   651  					Name:         "_id_",
   652  					Namespace:    mt.DB.Name() + "." + name,
   653  					KeysDocument: bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32("_id", 1).Build()),
   654  					Version:      2,
   655  					Unique:       func(b bool) *bool { return &b }(true),
   656  					Clustered:    func(b bool) *bool { return &b }(true),
   657  				},
   658  				{
   659  					Name:         "foo_-1",
   660  					Namespace:    mt.DB.Name() + "." + name,
   661  					KeysDocument: bson.Raw(bsoncore.NewDocumentBuilder().AppendInt32("foo", -1).Build()),
   662  					Version:      2,
   663  				},
   664  			}
   665  			assert.True(mt, cmp.Equal(specs, expectedSpecs), "expected specifications to match: %v", cmp.Diff(specs, expectedSpecs))
   666  		})
   667  	})
   668  }
   669  
   670  func getIndexDoc(mt *mtest.T, iv mongo.IndexView, expectedKeyDoc bson.D) bson.D {
   671  	c, err := iv.List(context.Background())
   672  	assert.Nil(mt, err, "List error: %v", err)
   673  
   674  	for c.Next(context.Background()) {
   675  		var index bson.D
   676  		err = c.Decode(&index)
   677  		assert.Nil(mt, err, "Decode error: %v", err)
   678  
   679  		for _, elem := range index {
   680  			if elem.Key != "key" {
   681  				continue
   682  			}
   683  
   684  			if cmp.Equal(expectedKeyDoc, elem.Value.(bson.D)) {
   685  				return index
   686  			}
   687  		}
   688  	}
   689  	return nil
   690  }
   691  
   692  func checkIndexDocContains(mt *mtest.T, indexDoc bson.D, expectedElem bson.E) {
   693  	for _, elem := range indexDoc {
   694  		if elem.Key != expectedElem.Key {
   695  			continue
   696  		}
   697  
   698  		assert.Equal(mt, expectedElem, elem, "expected element %v, got %v", expectedElem, elem)
   699  		return
   700  	}
   701  
   702  	mt.Fatalf("no element matching %v found", expectedElem)
   703  }
   704  
   705  func verifyIndexExists(mt *mtest.T, iv mongo.IndexView, expected index) {
   706  	mt.Helper()
   707  
   708  	cursor, err := iv.List(context.Background())
   709  	assert.Nil(mt, err, "List error: %v", err)
   710  
   711  	var found bool
   712  	for cursor.Next(context.Background()) {
   713  		var idx index
   714  		err = cursor.Decode(&idx)
   715  		assert.Nil(mt, err, "Decode error: %v", err)
   716  
   717  		if idx.Name == expected.Name {
   718  			if expected.Key != nil {
   719  				assert.Equal(mt, expected.Key, idx.Key, "key document mismatch; expected %v, got %v", expected.Key, idx.Key)
   720  			}
   721  			found = true
   722  		}
   723  	}
   724  	assert.Nil(mt, cursor.Err(), "cursor error: %v", err)
   725  	assert.True(mt, found, "expected to find index %v but was not found", expected.Name)
   726  }
   727  

View as plain text