diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 1fe772167a..97fccf15ce 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -407,7 +407,7 @@ functions: params: binary: "bash" env: - TEST_SEARCH_INDEX: "${MONGODB_URI}" + SEARCH_INDEX_URI: "${SEARCH_INDEX_URI}" args: [*task-runner, evg-test-search-index] add-aws-auth-variables-to-file: - command: ec2.assume_role @@ -1916,7 +1916,7 @@ task_groups: params: working_dir: src/go.mongodb.org/mongo-driver binary: bash - include_expansions_in_env: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN] + include_expansions_in_env: [AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, MONGODB_URI] env: MONGODB_VERSION: ${VERSION} LAMBDA_STACK_NAME: dbx-go-lambda @@ -1925,6 +1925,15 @@ task_groups: - command: expansions.update params: file: src/go.mongodb.org/mongo-driver/atlas-expansion.yml + - command: shell.exec + params: + working_dir: src/go.mongodb.org/mongo-driver + shell: bash + script: |- + echo "SEARCH_INDEX_URI: ${MONGODB_URI}" > atlas-expansion.yml + - command: expansions.update + params: + file: src/go.mongodb.org/mongo-driver/atlas-expansion.yml teardown_group: - command: subprocess.exec params: diff --git a/internal/integration/search_index_prose_test.go b/internal/integration/search_index_prose_test.go index a9fbfef4a4..9b41a77bc9 100644 --- a/internal/integration/search_index_prose_test.go +++ b/internal/integration/search_index_prose_test.go @@ -31,7 +31,7 @@ func TestSearchIndexProse(t *testing.T) { const timeout = 5 * time.Minute - uri := os.Getenv("TEST_INDEX_URI") + uri := os.Getenv("SEARCH_INDEX_URI") if uri == "" { t.Skip("skipping") } @@ -57,12 +57,15 @@ func TestSearchIndexProse(t *testing.T) { require.NoError(mt, err, "failed to create index") require.Equal(mt, searchName, index, "unmatched name") + awaitCtx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + var doc bson.Raw for doc == nil { - cursor, err := view.List(ctx, opts) + cursor, err := view.List(awaitCtx, opts) require.NoError(mt, err, "failed to list") - if !cursor.Next(ctx) { + if !cursor.Next(awaitCtx) { break } name := cursor.Current.Lookup("name").StringValue() @@ -70,7 +73,7 @@ func TestSearchIndexProse(t *testing.T) { if name == searchName && queryable { doc = cursor.Current } else { - t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) time.Sleep(5 * time.Second) } } @@ -110,7 +113,7 @@ func TestSearchIndexProse(t *testing.T) { require.Contains(mt, indexes, *args.Name) } - getDocument := func(opts *options.SearchIndexesOptionsBuilder) bson.Raw { + getDocument := func(ctx context.Context, opts *options.SearchIndexesOptionsBuilder) bson.Raw { for { cursor, err := view.List(ctx, opts) require.NoError(mt, err, "failed to list") @@ -127,7 +130,7 @@ func TestSearchIndexProse(t *testing.T) { if name == *args.Name && queryable { return cursor.Current } - t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) time.Sleep(5 * time.Second) } } @@ -138,7 +141,10 @@ func TestSearchIndexProse(t *testing.T) { go func(opts *options.SearchIndexesOptionsBuilder) { defer wg.Done() - doc := getDocument(opts) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + + doc := getDocument(ctx, opts) require.NotNil(mt, doc, "got empty document") args, err := mongoutil.NewOptions[options.SearchIndexesOptions](opts) @@ -173,12 +179,14 @@ func TestSearchIndexProse(t *testing.T) { require.NoError(mt, err, "failed to create index") require.Equal(mt, searchName, index, "unmatched name") + createOneCtx, createOneCancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer createOneCancel() var doc bson.Raw for doc == nil { - cursor, err := view.List(ctx, opts) + cursor, err := view.List(createOneCtx, opts) require.NoError(mt, err, "failed to list") - if !cursor.Next(ctx) { + if !cursor.Next(createOneCtx) { break } name := cursor.Current.Lookup("name").StringValue() @@ -186,7 +194,7 @@ func TestSearchIndexProse(t *testing.T) { if name == searchName && queryable { doc = cursor.Current } else { - t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) time.Sleep(5 * time.Second) } } @@ -194,14 +202,16 @@ func TestSearchIndexProse(t *testing.T) { err = view.DropOne(ctx, searchName) require.NoError(mt, err, "failed to drop index") + dropOneCtx, dropOneCancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer dropOneCancel() for { - cursor, err := view.List(ctx, opts) + cursor, err := view.List(dropOneCtx, opts) require.NoError(mt, err, "failed to list") - if !cursor.Next(ctx) { + if !cursor.Next(dropOneCtx) { break } - t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) time.Sleep(5 * time.Second) } }) @@ -224,12 +234,14 @@ func TestSearchIndexProse(t *testing.T) { require.NoError(mt, err, "failed to create index") require.Equal(mt, searchName, index, "unmatched name") + createOneCtx, createOneCancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer createOneCancel() var doc bson.Raw for doc == nil { - cursor, err := view.List(ctx, opts) + cursor, err := view.List(createOneCtx, opts) require.NoError(mt, err, "failed to list") - if !cursor.Next(ctx) { + if !cursor.Next(createOneCtx) { break } name := cursor.Current.Lookup("name").StringValue() @@ -237,7 +249,7 @@ func TestSearchIndexProse(t *testing.T) { if name == searchName && queryable { doc = cursor.Current } else { - t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) time.Sleep(5 * time.Second) } } @@ -248,11 +260,13 @@ func TestSearchIndexProse(t *testing.T) { require.NoError(mt, err, "failed to marshal definition") err = view.UpdateOne(ctx, searchName, definition) require.NoError(mt, err, "failed to update index") + updateOneCtx, updateOneCancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer updateOneCancel() for doc == nil { - cursor, err := view.List(ctx, opts) + cursor, err := view.List(updateOneCtx, opts) require.NoError(mt, err, "failed to list") - if !cursor.Next(ctx) { + if !cursor.Next(updateOneCtx) { break } name := cursor.Current.Lookup("name").StringValue() @@ -262,7 +276,7 @@ func TestSearchIndexProse(t *testing.T) { if name == searchName && queryable && status == "READY" && bytes.Equal(latestDefinition, expected) { doc = cursor.Current } else { - t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) time.Sleep(5 * time.Second) } } @@ -302,12 +316,14 @@ func TestSearchIndexProse(t *testing.T) { }) require.NoError(mt, err, "failed to create index") require.Equal(mt, searchName, index, "unmatched name") + awaitCtx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() var doc bson.Raw for doc == nil { - cursor, err := view.List(ctx, opts) + cursor, err := view.List(awaitCtx, opts) require.NoError(mt, err, "failed to list") - if !cursor.Next(ctx) { + if !cursor.Next(awaitCtx) { break } name := cursor.Current.Lookup("name").StringValue() @@ -315,7 +331,7 @@ func TestSearchIndexProse(t *testing.T) { if name == searchName && queryable { doc = cursor.Current } else { - t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) time.Sleep(5 * time.Second) } } @@ -348,12 +364,14 @@ func TestSearchIndexProse(t *testing.T) { }) require.NoError(mt, err, "failed to create index") require.Equal(mt, indexName, index, "unmatched name") + implicitCtx, implicitCancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer implicitCancel() var doc bson.Raw for doc == nil { - cursor, err := view.List(ctx, opts) + cursor, err := view.List(implicitCtx, opts) require.NoError(mt, err, "failed to list") - if !cursor.Next(ctx) { + if !cursor.Next(implicitCtx) { break } name := cursor.Current.Lookup("name").StringValue() @@ -363,7 +381,7 @@ func TestSearchIndexProse(t *testing.T) { doc = cursor.Current assert.Equal(mt, indexType, "search") } else { - t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) time.Sleep(5 * time.Second) } } @@ -376,12 +394,14 @@ func TestSearchIndexProse(t *testing.T) { }) require.NoError(mt, err, "failed to create index") require.Equal(mt, indexName, index, "unmatched name") + explicitCtx, explicitCancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer explicitCancel() doc = nil for doc == nil { - cursor, err := view.List(ctx, opts) + cursor, err := view.List(explicitCtx, opts) require.NoError(mt, err, "failed to list") - if !cursor.Next(ctx) { + if !cursor.Next(explicitCtx) { break } name := cursor.Current.Lookup("name").StringValue() @@ -391,7 +411,7 @@ func TestSearchIndexProse(t *testing.T) { doc = cursor.Current assert.Equal(mt, indexType, "search") } else { - t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) time.Sleep(5 * time.Second) } } @@ -417,12 +437,14 @@ func TestSearchIndexProse(t *testing.T) { }) require.NoError(mt, err, "failed to create index") require.Equal(mt, indexName, index, "unmatched name") + vectorCtx, vectorCancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer vectorCancel() doc = nil for doc == nil { - cursor, err := view.List(ctx, opts) + cursor, err := view.List(vectorCtx, opts) require.NoError(mt, err, "failed to list") - if !cursor.Next(ctx) { + if !cursor.Next(vectorCtx) { break } name := cursor.Current.Lookup("name").StringValue() @@ -432,7 +454,7 @@ func TestSearchIndexProse(t *testing.T) { doc = cursor.Current assert.Equal(mt, indexType, "vectorSearch") } else { - t.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) time.Sleep(5 * time.Second) } } @@ -472,4 +494,55 @@ func TestSearchIndexProse(t *testing.T) { }) assert.ErrorContains(mt, err, "Attribute mappings missing") }) + + mt.Run("case 9: Drivers use server default for unspecified name (`default`) and type (`search`)", func(mt *mtest.T) { + cases := []struct { + name string + opts *options.SearchIndexesOptionsBuilder + }{ + {name: "empty options", opts: options.SearchIndexes()}, + {name: "nil options", opts: nil}, + } + + for _, tc := range cases { + mt.Run(tc.name, func(mt *mtest.T) { + view := mt.Coll.SearchIndexes() + definition := bson.D{ + {"mappings", bson.D{ + {"dynamic", true}, + }}, + } + + indexName, err := view.CreateOne(context.Background(), mongo.SearchIndexModel{ + Definition: definition, + Options: tc.opts, + }) + require.NoError(mt, err, "failed to create index") + require.Equal(mt, "default", indexName) + + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancel() + + var doc bson.Raw + for doc == nil { + cursor, err := view.List(ctx, tc.opts) + require.NoError(mt, err, "failed to list") + + if !cursor.Next(ctx) { + break + } + name := cursor.Current.Lookup("name").StringValue() + queryable := cursor.Current.Lookup("queryable").Boolean() + indexType := cursor.Current.Lookup("type").StringValue() + if name == indexName && queryable { + doc = cursor.Current + require.Equal(mt, indexType, "search") + } else { + mt.Logf("cursor: %s, sleep 5 seconds...", cursor.Current.String()) + time.Sleep(5 * time.Second) + } + } + }) + } + }) } diff --git a/mongo/search_index_view.go b/mongo/search_index_view.go index 5df3b8fb4e..2ac92eb8c2 100644 --- a/mongo/search_index_view.go +++ b/mongo/search_index_view.go @@ -123,13 +123,13 @@ func (siv SearchIndexView) CreateMany( } var iidx int32 + iidx, indexes = bsoncore.AppendDocumentElementStart(indexes, strconv.Itoa(i)) if model.Options != nil { searchIndexArgs, err := mongoutil.NewOptions[options.SearchIndexesOptions](model.Options) if err != nil { return nil, fmt.Errorf("failed to construct options from builder: %w", err) } - iidx, indexes = bsoncore.AppendDocumentElementStart(indexes, strconv.Itoa(i)) if searchIndexArgs.Name != nil { indexes = bsoncore.AppendStringElement(indexes, "name", *searchIndexArgs.Name) }