From 72e67157052590c257af24a197e24f6a4b6401ee Mon Sep 17 00:00:00 2001 From: Qingyang Hu Date: Tue, 8 Jul 2025 09:30:48 -0400 Subject: [PATCH 1/4] add find/findAndModify --- .../unified/collection_operation_execution.go | 20 +++ mongo/collection.go | 21 +++ mongo/options/findoptions.go | 22 ++++ x/mongo/driver/operation/find.go | 15 +++ x/mongo/driver/operation/find_and_modify.go | 15 +++ x/mongo/driver/xoptions/options.go | 123 ++++++++++++++++-- 6 files changed, 207 insertions(+), 9 deletions(-) diff --git a/internal/integration/unified/collection_operation_execution.go b/internal/integration/unified/collection_operation_execution.go index e58a869eed..7bfb3fdef6 100644 --- a/internal/integration/unified/collection_operation_execution.go +++ b/internal/integration/unified/collection_operation_execution.go @@ -873,6 +873,11 @@ func executeFindOneAndDelete(ctx context.Context, operation *operation) (*operat opts.SetSort(val.Document()) case "let": opts.SetLet(val.Document()) + case "rawData": + err = xoptions.SetInternalFindOneAndDeleteOptions(opts, key, val.Boolean()) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("unrecognized findOneAndDelete option %q", key) } @@ -955,6 +960,11 @@ func executeFindOneAndReplace(ctx context.Context, operation *operation) (*opera opts.SetSort(val.Document()) case "upsert": opts.SetUpsert(val.Boolean()) + case "rawData": + err = xoptions.SetInternalFindOneAndReplaceOptions(opts, key, val.Boolean()) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("unrecognized findOneAndReplace option %q", key) } @@ -1047,6 +1057,11 @@ func executeFindOneAndUpdate(ctx context.Context, operation *operation) (*operat } case "upsert": opts.SetUpsert(val.Boolean()) + case "rawData": + err = xoptions.SetInternalFindOneAndUpdateOptions(opts, key, val.Boolean()) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("unrecognized findOneAndUpdate option %q", key) } @@ -1541,6 +1556,11 @@ func createFindCursor(ctx context.Context, operation *operation) (*cursorResult, case "maxAwaitTimeMS": maxAwaitTimeMS := time.Duration(val.Int32()) * time.Millisecond opts.SetMaxAwaitTime(maxAwaitTimeMS) + case "rawData": + err = xoptions.SetInternalFindOptions(opts, key, val.Boolean()) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("unrecognized find option %q", key) } diff --git a/mongo/collection.go b/mongo/collection.go index 41b4494d82..927ecbf5ca 100644 --- a/mongo/collection.go +++ b/mongo/collection.go @@ -1536,6 +1536,11 @@ func (coll *Collection) find( } op.Sort(sort) } + if rawDataOpt := optionsutil.Value(args.Internal, "rawData"); rawDataOpt != nil { + if rawData, ok := rawDataOpt.(bool); ok { + op = op.RawData(rawData) + } + } retry := driver.RetryNone if coll.client.retryReads { retry = driver.RetryOncePerCommand @@ -1569,6 +1574,7 @@ func newFindArgsFromFindOneArgs(args *options.FindOneOptions) *options.FindOptio v.ShowRecordID = args.ShowRecordID v.Skip = args.Skip v.Sort = args.Sort + v.Internal = args.Internal } return v } @@ -1731,6 +1737,11 @@ func (coll *Collection) FindOneAndDelete( } op = op.Let(let) } + if rawDataOpt := optionsutil.Value(args.Internal, "rawData"); rawDataOpt != nil { + if rawData, ok := rawDataOpt.(bool); ok { + op = op.RawData(rawData) + } + } return coll.findAndModify(ctx, op) } @@ -1828,6 +1839,11 @@ func (coll *Collection) FindOneAndReplace( } op = op.Let(let) } + if rawDataOpt := optionsutil.Value(args.Internal, "rawData"); rawDataOpt != nil { + if rawData, ok := rawDataOpt.(bool); ok { + op = op.RawData(rawData) + } + } return coll.findAndModify(ctx, op) } @@ -1937,6 +1953,11 @@ func (coll *Collection) FindOneAndUpdate( } op = op.Let(let) } + if rawDataOpt := optionsutil.Value(args.Internal, "rawData"); rawDataOpt != nil { + if rawData, ok := rawDataOpt.(bool); ok { + op = op.RawData(rawData) + } + } return coll.findAndModify(ctx, op) } diff --git a/mongo/options/findoptions.go b/mongo/options/findoptions.go index ea627900ea..dd66eabc7a 100644 --- a/mongo/options/findoptions.go +++ b/mongo/options/findoptions.go @@ -8,6 +8,8 @@ package options import ( "time" + + "go.mongodb.org/mongo-driver/v2/internal/optionsutil" ) // FindOptions represents arguments that can be used to configure a Find @@ -35,6 +37,10 @@ type FindOptions struct { Let interface{} Limit *int64 NoCursorTimeout *bool + + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + Internal optionsutil.Options } // FindOptionsBuilder represents functional options that configure an Findopts. @@ -285,6 +291,10 @@ type FindOneOptions struct { ShowRecordID *bool Skip *int64 Sort interface{} + + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + Internal optionsutil.Options } // FindOneOptionsBuilder represents functional options that configure an @@ -450,6 +460,10 @@ type FindOneAndReplaceOptions struct { Upsert *bool Hint interface{} Let interface{} + + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + Internal optionsutil.Options } // FindOneAndReplaceOptionsBuilder contains options to perform a findAndModify @@ -611,6 +625,10 @@ type FindOneAndUpdateOptions struct { Upsert *bool Hint interface{} Let interface{} + + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + Internal optionsutil.Options } // FindOneAndUpdateOptionsBuilder contains options to configure a @@ -782,6 +800,10 @@ type FindOneAndDeleteOptions struct { Sort interface{} Hint interface{} Let interface{} + + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + Internal optionsutil.Options } // FindOneAndDeleteOptionsBuilder contains options to configure delete diff --git a/x/mongo/driver/operation/find.go b/x/mongo/driver/operation/find.go index b607cb14d7..615e240850 100644 --- a/x/mongo/driver/operation/find.go +++ b/x/mongo/driver/operation/find.go @@ -61,6 +61,7 @@ type Find struct { result driver.CursorResponse serverAPI *driver.ServerAPIOptions timeout *time.Duration + rawData *bool logger *logger.Logger omitMaxTimeMS bool } @@ -191,6 +192,10 @@ func (f *Find) command(dst []byte, desc description.SelectedServer) ([]byte, err if f.tailable != nil { dst = bsoncore.AppendBooleanElement(dst, "tailable", *f.tailable) } + // Set rawData for 8.2+ servers. + if f.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) { + dst = bsoncore.AppendBooleanElement(dst, "rawData", *f.rawData) + } return dst, nil } @@ -565,6 +570,16 @@ func (f *Find) Authenticator(authenticator driver.Authenticator) *Find { return f } +// RawData sets the rawData to access timeseries data in the compressed format. +func (f *Find) RawData(rawData bool) *Find { + if f == nil { + f = new(Find) + } + + f.rawData = &rawData + return f +} + // OmitMaxTimeMS omits the automatically-calculated "maxTimeMS" from the // command. func (f *Find) OmitMaxTimeMS(omit bool) *Find { diff --git a/x/mongo/driver/operation/find_and_modify.go b/x/mongo/driver/operation/find_and_modify.go index 9328a73ed5..0920568110 100644 --- a/x/mongo/driver/operation/find_and_modify.go +++ b/x/mongo/driver/operation/find_and_modify.go @@ -50,6 +50,7 @@ type FindAndModify struct { serverAPI *driver.ServerAPIOptions let bsoncore.Document timeout *time.Duration + rawData *bool result FindAndModifyResult } @@ -211,6 +212,10 @@ func (fam *FindAndModify) command(dst []byte, desc description.SelectedServer) ( if fam.let != nil { dst = bsoncore.AppendDocumentElement(dst, "let", fam.let) } + // Set rawData for 8.2+ servers. + if fam.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) { + dst = bsoncore.AppendBooleanElement(dst, "rawData", *fam.rawData) + } return dst, nil } @@ -476,3 +481,13 @@ func (fam *FindAndModify) Authenticator(authenticator driver.Authenticator) *Fin fam.authenticator = authenticator return fam } + +// RawData sets the rawData to access timeseries data in the compressed format. +func (fam *FindAndModify) RawData(rawData bool) *FindAndModify { + if fam == nil { + fam = new(FindAndModify) + } + + fam.rawData = &rawData + return fam +} diff --git a/x/mongo/driver/xoptions/options.go b/x/mongo/driver/xoptions/options.go index 97c47e233f..2a3af03780 100644 --- a/x/mongo/driver/xoptions/options.go +++ b/x/mongo/driver/xoptions/options.go @@ -39,7 +39,7 @@ func SetInternalClientOptions(opts *options.ClientOptions, key string, option an } opts.Custom = optionsutil.WithValue(opts.Custom, key, b) default: - return fmt.Errorf("unsupported option: %s", key) + return fmt.Errorf("unsupported option: %q", key) } return nil } @@ -60,7 +60,7 @@ func SetInternalAggregateOptions(a *options.AggregateOptionsBuilder, key string, return nil }) default: - return fmt.Errorf("unsupported option: %s", key) + return fmt.Errorf("unsupported option: %q", key) } return nil } @@ -81,7 +81,7 @@ func SetInternalCountOptions(a *options.CountOptionsBuilder, key string, option return nil }) default: - return fmt.Errorf("unsupported option: %s", key) + return fmt.Errorf("unsupported option: %q", key) } return nil } @@ -102,7 +102,7 @@ func SetInternalDeleteOneOptions(a *options.DeleteOneOptionsBuilder, key string, return nil }) default: - return fmt.Errorf("unsupported option: %s", key) + return fmt.Errorf("unsupported option: %q", key) } return nil } @@ -123,7 +123,7 @@ func SetInternalDeleteManyOptions(a *options.DeleteManyOptionsBuilder, key strin return nil }) default: - return fmt.Errorf("unsupported option: %s", key) + return fmt.Errorf("unsupported option: %q", key) } return nil } @@ -144,7 +144,7 @@ func SetInternalDistinctOptions(a *options.DistinctOptionsBuilder, key string, o return nil }) default: - return fmt.Errorf("unsupported option: %s", key) + return fmt.Errorf("unsupported option: %q", key) } return nil } @@ -165,7 +165,112 @@ func SetInternalEstimatedDocumentCountOptions(a *options.EstimatedDocumentCountO return nil }) default: - return fmt.Errorf("unsupported option: %s", key) + return fmt.Errorf("unsupported option: %q", key) + } + return nil +} + +// SetInternalFindOptions sets internal options for FindOptions. +func SetInternalFindOptions(a *options.FindOptionsBuilder, key string, option any) error { + typeErrFunc := func(t string) error { + return fmt.Errorf("unexpected type for %q: %T is not %s", key, option, t) + } + switch key { + case "rawData": + b, ok := option.(bool) + if !ok { + return typeErrFunc("bool") + } + a.Opts = append(a.Opts, func(opts *options.FindOptions) error { + opts.Internal = optionsutil.WithValue(opts.Internal, key, b) + return nil + }) + default: + return fmt.Errorf("unsupported option: %q", key) + } + return nil +} + +// SetInternalFindOneOptions sets internal options for FindOneOptions. +func SetInternalFindOneOptions(a *options.FindOneOptionsBuilder, key string, option any) error { + typeErrFunc := func(t string) error { + return fmt.Errorf("unexpected type for %q: %T is not %s", key, option, t) + } + switch key { + case "rawData": + b, ok := option.(bool) + if !ok { + return typeErrFunc("bool") + } + a.Opts = append(a.Opts, func(opts *options.FindOneOptions) error { + opts.Internal = optionsutil.WithValue(opts.Internal, key, b) + return nil + }) + default: + return fmt.Errorf("unsupported option: %q", key) + } + return nil +} + +// SetInternalFindOneAndDeleteOptions sets internal options for FindOneAndDeleteOptions. +func SetInternalFindOneAndDeleteOptions(a *options.FindOneAndDeleteOptionsBuilder, key string, option any) error { + typeErrFunc := func(t string) error { + return fmt.Errorf("unexpected type for %q: %T is not %s", key, option, t) + } + switch key { + case "rawData": + b, ok := option.(bool) + if !ok { + return typeErrFunc("bool") + } + a.Opts = append(a.Opts, func(opts *options.FindOneAndDeleteOptions) error { + opts.Internal = optionsutil.WithValue(opts.Internal, key, b) + return nil + }) + default: + return fmt.Errorf("unsupported option: %q", key) + } + return nil +} + +// SetInternalFindOneAndReplaceOptions sets internal options for FindOneAndReplaceOptions. +func SetInternalFindOneAndReplaceOptions(a *options.FindOneAndReplaceOptionsBuilder, key string, option any) error { + typeErrFunc := func(t string) error { + return fmt.Errorf("unexpected type for %q: %T is not %s", key, option, t) + } + switch key { + case "rawData": + b, ok := option.(bool) + if !ok { + return typeErrFunc("bool") + } + a.Opts = append(a.Opts, func(opts *options.FindOneAndReplaceOptions) error { + opts.Internal = optionsutil.WithValue(opts.Internal, key, b) + return nil + }) + default: + return fmt.Errorf("unsupported option: %q", key) + } + return nil +} + +// SetInternalFindOneAndUpdateOptions sets internal options for FindOneAndUpdateOptions. +func SetInternalFindOneAndUpdateOptions(a *options.FindOneAndUpdateOptionsBuilder, key string, option any) error { + typeErrFunc := func(t string) error { + return fmt.Errorf("unexpected type for %q: %T is not %s", key, option, t) + } + switch key { + case "rawData": + b, ok := option.(bool) + if !ok { + return typeErrFunc("bool") + } + a.Opts = append(a.Opts, func(opts *options.FindOneAndUpdateOptions) error { + opts.Internal = optionsutil.WithValue(opts.Internal, key, b) + return nil + }) + default: + return fmt.Errorf("unsupported option: %q", key) } return nil } @@ -186,7 +291,7 @@ func SetInternalInsertManyOptions(a *options.InsertManyOptionsBuilder, key strin return nil }) default: - return fmt.Errorf("unsupported option: %s", key) + return fmt.Errorf("unsupported option: %q", key) } return nil } @@ -207,7 +312,7 @@ func SetInternalInsertOneOptions(a *options.InsertOneOptionsBuilder, key string, return nil }) default: - return fmt.Errorf("unsupported option: %s", key) + return fmt.Errorf("unsupported option: %q", key) } return nil } From 7baebb040764d9b4be2085af2febb69a6f122fca Mon Sep 17 00:00:00 2001 From: Qingyang Hu Date: Tue, 8 Jul 2025 09:47:21 -0400 Subject: [PATCH 2/4] add update/replace --- .../unified/collection_operation_execution.go | 5 ++ internal/integration/unified/crud_helpers.go | 11 ++++ mongo/collection.go | 7 +++ mongo/options/replaceoptions.go | 6 ++ mongo/options/updateoptions.go | 10 +++ x/mongo/driver/operation/update.go | 15 +++++ x/mongo/driver/xoptions/options.go | 63 +++++++++++++++++++ 7 files changed, 117 insertions(+) diff --git a/internal/integration/unified/collection_operation_execution.go b/internal/integration/unified/collection_operation_execution.go index 7bfb3fdef6..41e74bf5c6 100644 --- a/internal/integration/unified/collection_operation_execution.go +++ b/internal/integration/unified/collection_operation_execution.go @@ -1358,6 +1358,11 @@ func executeReplaceOne(ctx context.Context, operation *operation) (*operationRes opts.SetUpsert(val.Boolean()) case "let": opts.SetLet(val.Document()) + case "rawData": + err = xoptions.SetInternalReplaceOptions(opts, key, val.Boolean()) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("unrecognized replaceOne option %q", key) } diff --git a/internal/integration/unified/crud_helpers.go b/internal/integration/unified/crud_helpers.go index 34de29d683..98719224c4 100644 --- a/internal/integration/unified/crud_helpers.go +++ b/internal/integration/unified/crud_helpers.go @@ -12,6 +12,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/internal/bsonutil" "go.mongodb.org/mongo-driver/v2/mongo/options" + "go.mongodb.org/mongo-driver/v2/x/mongo/driver/xoptions" ) // newMissingArgumentError creates an error to convey that an argument that is required to run an operation is missing @@ -67,6 +68,11 @@ func createUpdateManyArguments(args bson.Raw) (*updateArguments, *options.Update } case "upsert": opts.SetUpsert(val.Boolean()) + case "rawData": + err := xoptions.SetInternalUpdateManyOptions(opts, key, val.Boolean()) + if err != nil { + return nil, nil, err + } default: return nil, nil, fmt.Errorf("unrecognized update option %q", key) } @@ -125,6 +131,11 @@ func createUpdateOneArguments(args bson.Raw) (*updateArguments, *options.UpdateO opts.SetUpsert(val.Boolean()) case "sort": opts.SetSort(val.Document()) + case "rawData": + err := xoptions.SetInternalUpdateOneOptions(opts, key, val.Boolean()) + if err != nil { + return nil, nil, err + } default: return nil, nil, fmt.Errorf("unrecognized update option %q", key) } diff --git a/mongo/collection.go b/mongo/collection.go index 927ecbf5ca..db0227ab03 100644 --- a/mongo/collection.go +++ b/mongo/collection.go @@ -700,6 +700,11 @@ func (coll *Collection) updateOrReplace( } op = op.Comment(comment) } + if rawDataOpt := optionsutil.Value(args.Internal, "rawData"); rawDataOpt != nil { + if rawData, ok := rawDataOpt.(bool); ok { + op = op.RawData(rawData) + } + } retry := driver.RetryNone // retryable writes are only enabled updateOne/replaceOne operations if !multi && coll.client.retryWrites { @@ -794,6 +799,7 @@ func (coll *Collection) UpdateOne( Hint: args.Hint, Upsert: args.Upsert, Let: args.Let, + Internal: args.Internal, } return coll.updateOrReplace(ctx, f, update, false, rrOne, true, args.Sort, updateOptions) @@ -884,6 +890,7 @@ func (coll *Collection) ReplaceOne( Hint: args.Hint, Let: args.Let, Comment: args.Comment, + Internal: args.Internal, } return coll.updateOrReplace(ctx, f, r, false, rrOne, false, args.Sort, updateOptions) diff --git a/mongo/options/replaceoptions.go b/mongo/options/replaceoptions.go index 32caceff16..8b6c7e166d 100644 --- a/mongo/options/replaceoptions.go +++ b/mongo/options/replaceoptions.go @@ -6,6 +6,8 @@ package options +import "go.mongodb.org/mongo-driver/v2/internal/optionsutil" + // ReplaceOptions represents arguments that can be used to configure a ReplaceOne // operation. // @@ -18,6 +20,10 @@ type ReplaceOptions struct { Upsert *bool Let interface{} Sort interface{} + + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + Internal optionsutil.Options } // ReplaceOptionsBuilder contains options to configure replace operations. Each diff --git a/mongo/options/updateoptions.go b/mongo/options/updateoptions.go index f7b22e6f84..2a9f4f48d0 100644 --- a/mongo/options/updateoptions.go +++ b/mongo/options/updateoptions.go @@ -6,6 +6,8 @@ package options +import "go.mongodb.org/mongo-driver/v2/internal/optionsutil" + // UpdateOneOptions represents arguments that can be used to configure UpdateOne // operations. // @@ -19,6 +21,10 @@ type UpdateOneOptions struct { Upsert *bool Let interface{} Sort interface{} + + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + Internal optionsutil.Options } // UpdateOneOptionsBuilder contains options to configure UpdateOne operations. @@ -164,6 +170,10 @@ type UpdateManyOptions struct { Hint interface{} Upsert *bool Let interface{} + + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + Internal optionsutil.Options } // UpdateManyOptionsBuilder contains options to configure UpdateMany operations. diff --git a/x/mongo/driver/operation/update.go b/x/mongo/driver/operation/update.go index 6e6222f2f4..00e193ef49 100644 --- a/x/mongo/driver/operation/update.go +++ b/x/mongo/driver/operation/update.go @@ -46,6 +46,7 @@ type Update struct { serverAPI *driver.ServerAPIOptions let bsoncore.Document timeout *time.Duration + rawData *bool logger *logger.Logger } @@ -203,6 +204,10 @@ func (u *Update) command(dst []byte, desc description.SelectedServer) ([]byte, e if u.let != nil { dst = bsoncore.AppendDocumentElement(dst, "let", u.let) } + // Set rawData for 8.2+ servers. + if u.rawData != nil && desc.WireVersion != nil && driverutil.VersionRangeIncludes(*desc.WireVersion, 27) { + dst = bsoncore.AppendBooleanElement(dst, "rawData", *u.rawData) + } return dst, nil } @@ -422,3 +427,13 @@ func (u *Update) Authenticator(authenticator driver.Authenticator) *Update { u.authenticator = authenticator return u } + +// RawData sets the rawData to access timeseries data in the compressed format. +func (u *Update) RawData(rawData bool) *Update { + if u == nil { + u = new(Update) + } + + u.rawData = &rawData + return u +} diff --git a/x/mongo/driver/xoptions/options.go b/x/mongo/driver/xoptions/options.go index 2a3af03780..91ebf7bb86 100644 --- a/x/mongo/driver/xoptions/options.go +++ b/x/mongo/driver/xoptions/options.go @@ -316,3 +316,66 @@ func SetInternalInsertOneOptions(a *options.InsertOneOptionsBuilder, key string, } return nil } + +// SetInternalReplaceOptions sets internal options for ReplaceOptions. +func SetInternalReplaceOptions(a *options.ReplaceOptionsBuilder, key string, option any) error { + typeErrFunc := func(t string) error { + return fmt.Errorf("unexpected type for %q: %T is not %s", key, option, t) + } + switch key { + case "rawData": + b, ok := option.(bool) + if !ok { + return typeErrFunc("bool") + } + a.Opts = append(a.Opts, func(opts *options.ReplaceOptions) error { + opts.Internal = optionsutil.WithValue(opts.Internal, key, b) + return nil + }) + default: + return fmt.Errorf("unsupported option: %q", key) + } + return nil +} + +// SetInternalUpdateManyOptions sets internal options for UpdateManyOptions. +func SetInternalUpdateManyOptions(a *options.UpdateManyOptionsBuilder, key string, option any) error { + typeErrFunc := func(t string) error { + return fmt.Errorf("unexpected type for %q: %T is not %s", key, option, t) + } + switch key { + case "rawData": + b, ok := option.(bool) + if !ok { + return typeErrFunc("bool") + } + a.Opts = append(a.Opts, func(opts *options.UpdateManyOptions) error { + opts.Internal = optionsutil.WithValue(opts.Internal, key, b) + return nil + }) + default: + return fmt.Errorf("unsupported option: %q", key) + } + return nil +} + +// SetInternalUpdateOneOptions sets internal options for UpdateOneOptions. +func SetInternalUpdateOneOptions(a *options.UpdateOneOptionsBuilder, key string, option any) error { + typeErrFunc := func(t string) error { + return fmt.Errorf("unexpected type for %q: %T is not %s", key, option, t) + } + switch key { + case "rawData": + b, ok := option.(bool) + if !ok { + return typeErrFunc("bool") + } + a.Opts = append(a.Opts, func(opts *options.UpdateOneOptions) error { + opts.Internal = optionsutil.WithValue(opts.Internal, key, b) + return nil + }) + default: + return fmt.Errorf("unsupported option: %q", key) + } + return nil +} From 7004d7d874c250f3444aafc301b49add71113c9a Mon Sep 17 00:00:00 2001 From: Qingyang Hu Date: Tue, 8 Jul 2025 09:58:36 -0400 Subject: [PATCH 3/4] add bulkWrite --- .../unified/collection_operation_execution.go | 5 +++++ mongo/bulk_write.go | 13 ++++++++++++ mongo/collection.go | 5 +++++ mongo/options/bulkwriteoptions.go | 6 ++++++ x/mongo/driver/xoptions/options.go | 21 +++++++++++++++++++ 5 files changed, 50 insertions(+) diff --git a/internal/integration/unified/collection_operation_execution.go b/internal/integration/unified/collection_operation_execution.go index 41e74bf5c6..be9afe0cd7 100644 --- a/internal/integration/unified/collection_operation_execution.go +++ b/internal/integration/unified/collection_operation_execution.go @@ -131,6 +131,11 @@ func executeBulkWrite(ctx context.Context, operation *operation) (*operationResu } case "let": opts.SetLet(val.Document()) + case "rawData": + err = xoptions.SetInternalBulkWriteOptions(opts, key, val.Boolean()) + if err != nil { + return nil, err + } default: return nil, fmt.Errorf("unrecognized bulkWrite option %q", key) } diff --git a/mongo/bulk_write.go b/mongo/bulk_write.go index 415a90ae55..7a3181c6c4 100644 --- a/mongo/bulk_write.go +++ b/mongo/bulk_write.go @@ -39,6 +39,7 @@ type bulkWrite struct { writeConcern *writeconcern.WriteConcern result BulkWriteResult let interface{} + rawData *bool } func (bw *bulkWrite) execute(ctx context.Context) error { @@ -209,6 +210,10 @@ func (bw *bulkWrite) runInsert(ctx context.Context, batch bulkWriteBatch) (opera } op = op.Retry(retry) + if bw.rawData != nil { + op.RawData(*bw.rawData) + } + err := op.Execute(ctx) return op.Result(), err @@ -282,6 +287,10 @@ func (bw *bulkWrite) runDelete(ctx context.Context, batch bulkWriteBatch) (opera } op = op.Retry(retry) + if bw.rawData != nil { + op.RawData(*bw.rawData) + } + err := op.Execute(ctx) return op.Result(), err @@ -415,6 +424,10 @@ func (bw *bulkWrite) runUpdate(ctx context.Context, batch bulkWriteBatch) (opera } op = op.Retry(retry) + if bw.rawData != nil { + op.RawData(*bw.rawData) + } + err := op.Execute(ctx) return op.Result(), err diff --git a/mongo/collection.go b/mongo/collection.go index db0227ab03..80c15c48d0 100644 --- a/mongo/collection.go +++ b/mongo/collection.go @@ -246,6 +246,11 @@ func (coll *Collection) BulkWrite(ctx context.Context, models []WriteModel, writeConcern: wc, let: args.Let, } + if rawDataOpt := optionsutil.Value(args.Internal, "rawData"); rawDataOpt != nil { + if rawData, ok := rawDataOpt.(bool); ok { + op.rawData = &rawData + } + } err = op.execute(ctx) diff --git a/mongo/options/bulkwriteoptions.go b/mongo/options/bulkwriteoptions.go index 186e83a0c5..20d0395efe 100644 --- a/mongo/options/bulkwriteoptions.go +++ b/mongo/options/bulkwriteoptions.go @@ -6,6 +6,8 @@ package options +import "go.mongodb.org/mongo-driver/v2/internal/optionsutil" + // DefaultOrdered is the default value for the Ordered option in BulkWriteOptions. var DefaultOrdered = true @@ -18,6 +20,10 @@ type BulkWriteOptions struct { Comment interface{} Ordered *bool Let interface{} + + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + Internal optionsutil.Options } // BulkWriteOptionsBuilder contains options to configure bulk write operations. diff --git a/x/mongo/driver/xoptions/options.go b/x/mongo/driver/xoptions/options.go index 91ebf7bb86..52b308a74e 100644 --- a/x/mongo/driver/xoptions/options.go +++ b/x/mongo/driver/xoptions/options.go @@ -65,6 +65,27 @@ func SetInternalAggregateOptions(a *options.AggregateOptionsBuilder, key string, return nil } +// SetInternalBulkWriteOptions sets internal options for BulkWriteOptions. +func SetInternalBulkWriteOptions(a *options.BulkWriteOptionsBuilder, key string, option any) error { + typeErrFunc := func(t string) error { + return fmt.Errorf("unexpected type for %q: %T is not %s", key, option, t) + } + switch key { + case "rawData": + b, ok := option.(bool) + if !ok { + return typeErrFunc("bool") + } + a.Opts = append(a.Opts, func(opts *options.BulkWriteOptions) error { + opts.Internal = optionsutil.WithValue(opts.Internal, key, b) + return nil + }) + default: + return fmt.Errorf("unsupported option: %q", key) + } + return nil +} + // SetInternalCountOptions sets internal options for CountOptions. func SetInternalCountOptions(a *options.CountOptionsBuilder, key string, option any) error { typeErrFunc := func(t string) error { From 79faf0569c98cc078d81f7c384e4fe74b3129ead Mon Sep 17 00:00:00 2001 From: Qingyang Hu Date: Fri, 1 Aug 2025 17:41:42 -0400 Subject: [PATCH 4/4] update test case --- x/mongo/driver/xoptions/options_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/mongo/driver/xoptions/options_test.go b/x/mongo/driver/xoptions/options_test.go index e56de6bdcc..4a4e1a371e 100644 --- a/x/mongo/driver/xoptions/options_test.go +++ b/x/mongo/driver/xoptions/options_test.go @@ -84,6 +84,6 @@ func TestSetInternalClientOptions(t *testing.T) { opts := options.Client() err := SetInternalClientOptions(opts, "unsupported", "unsupported") - require.EqualError(t, err, "unsupported option: unsupported") + require.EqualError(t, err, "unsupported option: \"unsupported\"") }) }