From 391345273f68b6f0e5d3bff055bb752d87ffbe46 Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Wed, 19 Nov 2025 16:46:44 +0100 Subject: [PATCH 1/3] GODRIVER-3696: Add CI/CD label to label checker (#2243) --- .github/workflows/check-labels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-labels.yml b/.github/workflows/check-labels.yml index 467249202f..b00391fc37 100644 --- a/.github/workflows/check-labels.yml +++ b/.github/workflows/check-labels.yml @@ -15,5 +15,5 @@ jobs: steps: - uses: docker://agilepathway/pull-request-label-checker:latest with: - one_of: bug,feature,enhancement,documentation,dependencies,ignore-for-release + one_of: bug,feature,enhancement,documentation,dependencies,ignore-for-release,ci/cd repo_token: ${{ secrets.GITHUB_TOKEN }} From 9da64b83f51feddfaa77d77fa498df966e36efee Mon Sep 17 00:00:00 2001 From: Preston Vasquez Date: Wed, 26 Nov 2025 14:06:13 -0700 Subject: [PATCH 2/3] GODRIVER-3692 Bump govulncheck to 1.25.3 (#2236) (#2242) Signed-off-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com> Co-authored-by: Andreas Braun Co-authored-by: Matt Dale <9760375+matthewdale@users.noreply.github.com> Co-authored-by: mongodb-dbx-release-bot[bot] <167856002+mongodb-dbx-release-bot[bot]@users.noreply.github.com> --- etc/govulncheck.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/govulncheck.sh b/etc/govulncheck.sh index 23988f9df2..5eaa72f098 100755 --- a/etc/govulncheck.sh +++ b/etc/govulncheck.sh @@ -7,7 +7,7 @@ set -ex # Note: this needs to be updated if the listed Go version has vulnerabilities # discovered because they will show up in the scan results along with Go Driver # and dependency vulnerabilities. -GO_VERSION=1.25.1 +GO_VERSION=1.25.3 go install golang.org/dl/go$GO_VERSION@latest go${GO_VERSION} download From c3077a17172f951d246a49569806d636e009bb7e Mon Sep 17 00:00:00 2001 From: Qingyang Hu <103950869+qingyang-hu@users.noreply.github.com> Date: Fri, 5 Dec 2025 22:32:19 -0500 Subject: [PATCH 3/3] GODRIVER-3704 Fix search index failure on empty "Options". (#2247) Co-authored-by: Preston Vasquez --- .evergreen/config.yml | 18 ++- Taskfile.yml | 5 +- .../integration/search_index_prose_test.go | 135 ++++++++++++++---- mongo/search_index_view.go | 2 +- 4 files changed, 118 insertions(+), 42 deletions(-) diff --git a/.evergreen/config.yml b/.evergreen/config.yml index f57ffb1e75..df75f2ca6a 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -26,11 +26,6 @@ timeout: args: [ls, -la] functions: - assume-test-secrets-ec2-role: - - command: ec2.assume_role - params: - role_arn: ${aws_test_secrets_role} - setup-system: # Executes clone and applies the submitted patch, if any - command: git.get_project @@ -439,7 +434,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: @@ -2063,7 +2058,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 @@ -2072,6 +2067,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/Taskfile.yml b/Taskfile.yml index 5fd8210bda..e54a4bc060 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,14 +1,13 @@ # See https://taskfile.dev/usage/ -version: '3' +version: "3" env: TEST_TIMEOUT: 1800 LONG_TEST_TIMEOUT: 3600 -dotenv: ['.test.env'] +dotenv: [".test.env"] tasks: - ### Utility tasks. ### default: deps: [build, check-license, check-fmt, check-modules, lint, test-short] 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) }