You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of the easymongo refactor (see PLAN.md on the refactor branch).
Working context (read this first)
Repository github.com/tophergopher/easymongo. Start from branch claude/mongotest-easymongo-refactor-wu2vhm; create a branch issue-<number>-<short-slug> from it and open a pull request back into it. PLAN.md on that branch is the source of truth; read it before writing code.
Go project, single module, all packages at the repo root except the new easymongotest/ subpackage. The final toolchain target is Go 1.27 (set by Toolchain: go 1.27, latest dependencies, tidy, remove replace directives #10); any Go 1.24 or newer works until then. Before pushing: gofmt -l . prints nothing, go vet ./..., go test -race ./....
The existing test suite (*_test.go at the root, package easymongo_test, every test function calling setup(t) from common_test.go) is the regression net for this refactor. It must pass after your change. It needs a Docker daemon reachable through the normal Docker environment and the mongo:8 image.
Method is test-driven: write the tests listed under "Tests first", confirm they fail for the expected reason, then implement. Never delete, skip or weaken an existing test to get green; if a test must change because the public API changed (for example primitive.ObjectID to bson.ObjectID), change only the types.
Commit messages: imperative summary line, blank line, body explaining why. Mention this issue number in the pull request description.
Why
Driver v2 collection methods take options.Lister[T] values. The &options.FindOptions{...} struct literals easymongo passes today no longer satisfy that interface. MaxTime fields were removed in favour of context deadlines, which easymongo already applies through Query.getContext() in query.go.
Scope
Convert each literal to a builder chain that only sets fields whose easymongo value is non-nil:
update_query.go: UpdateOptions (52-61). ArrayFilters changes from options.ArrayFilters to []any; update UpdateQuery.arrayFilters and its setter.
delete_query.go: DeleteOptions (25-31) splits into DeleteOneOptions and DeleteManyOptions builders (options.DeleteOne(), options.DeleteMany()).
find_and_query.go: FindOneAndUpdateOptions (127-141), FindOneAndReplaceOptions (147-160), FindOneAndDeleteOptions (191-201); ArrayFilters as above; options.ReturnDocument, options.Before, options.After are unchanged.
Delete every MaxTime: assignment; confirm getContext() is applied to each of these calls so the timeout still takes effect.
insert_query.go, replace_query.go, index.go, database.go already use constructors (options.InsertOne(), options.Replace(), options.CreateIndexes(), options.ListCollections().SetNameOnly(true), options.Collection()); verify they compile and that Index types still match.
Keep the fluent easymongo API unchanged; this is an internal change.
Implementation details
options.Lister[T] is interface{ List() []func(*T) error }; every options.Find()-style builder satisfies it. Builder setters verified in v2.9.0 for FindOptionsBuilder: SetAllowDiskUse, SetAllowPartialResults, SetBatchSize(int32), SetCollation(*options.Collation), SetComment(any), SetHint(any), SetLimit(int64), SetProjection(any), SetSkip(int64), SetSort(any), SetNoCursorTimeout(bool). FindOne(), Count(), Distinct(), Aggregate(), UpdateOne(), UpdateMany(), DeleteOne(), DeleteMany(), FindOneAndUpdate(), FindOneAndReplace(), FindOneAndDelete() have the matching subsets. UpdateOptions.ArrayFilters is []any with SetArrayFilters([]any). Collection method shapes, for example: coll.Find(ctx, filter any, opts ...options.Lister[options.FindOptions]) (*mongo.Cursor, error), coll.DeleteOne(ctx, filter any, opts ...options.Lister[options.DeleteOneOptions]) (*mongo.DeleteResult, error).
Conversion pattern to apply everywhere (only set what the easymongo query has):
// before (v1)opts:=&options.FindOptions{Limit: q.limit, Skip: q.skip, Sort: q.sortFields, MaxTime: q.timeout}
// after (v2)opts:=options.Find()
ifq.limit!=nil { opts.SetLimit(*q.limit) }
ifq.skip!=nil { opts.SetSkip(*q.skip) }
iflen(q.sortFields) >0 { opts.SetSort(q.sortFields) }
// MaxTime dropped: the ctx from q.getContext() already carries the deadline
Where easymongo stored *options.ArrayFilters, store []any and change the setter's parameter type to []any (or keep a ...any variadic for convenience).
For DeleteQuery, build options.DeleteOne() in One() and options.DeleteMany() in Many() from the same Collation/Hint fields.
Tests first
Existing tests for each file must pass unchanged. Add:
A unit test per builder helper that a fully populated easymongo query yields the expected driver options (inspect the built options by applying the Lister to a zero struct).
Timeout on a FindQuery still cancels a deliberately slow query ($where sleep or a large Limit on a big collection) with ErrTimeoutOccurred.
Array filters: an update with ArrayFilters on a nested array succeeds (update_query_test.go).
Acceptance
go vet ./... clean; no reference to MaxTime or options.ArrayFilters remains.
Depends on: #2. Lands in the same pull request as #2, #4 and #5.
Part of the easymongo refactor (see
PLAN.mdon the refactor branch).Working context (read this first)
github.com/tophergopher/easymongo. Start from branchclaude/mongotest-easymongo-refactor-wu2vhm; create a branchissue-<number>-<short-slug>from it and open a pull request back into it.PLAN.mdon that branch is the source of truth; read it before writing code.easymongotest/subpackage. The final toolchain target is Go 1.27 (set by Toolchain: go 1.27, latest dependencies, tidy, remove replace directives #10); any Go 1.24 or newer works until then. Before pushing:gofmt -l .prints nothing,go vet ./...,go test -race ./....*_test.goat the root, packageeasymongo_test, every test function callingsetup(t)fromcommon_test.go) is the regression net for this refactor. It must pass after your change. It needs a Docker daemon reachable through the normal Docker environment and themongo:8image.*mongo.Clienttype flows fromConnectiontoDatabasetoCollection), so the four driver issues Driver v2: migrate connect.go (mongo.Connect, Disconnect, BSONOptions, write concern) #2, Driver v2: options struct literals become builders; drop MaxTime; ArrayFilters and Delete option changes #3, Driver v2: primitive package merged into bson; replace x/bsonx in index.go #4 and Driver v2: Distinct returns a DistinctResult that must be decoded #5 land together in one pull request, worked in that order. Before them, New easymongotest subpackage wrapping mongotest/v2 #7 and Switch the easymongo test suite to easymongotest #8 must land so the test suite no longer depends on the oldgithub.com/tophergopher/mongotestv0.1.0 module (which itself imports easymongo and breaks the moment easymongo's types change). Until mongotest'sv2module is tagged, use a localreplace github.com/tophergopher/mongotest/v2 => ../mongotest/v2ingo.modand say so in the pull request; Toolchain: go 1.27, latest dependencies, tidy, remove replace directives #10 removes it.primitive.ObjectIDtobson.ObjectID), change only the types.Why
Driver v2 collection methods take
options.Lister[T]values. The&options.FindOptions{...}struct literals easymongo passes today no longer satisfy that interface.MaxTimefields were removed in favour of context deadlines, which easymongo already applies throughQuery.getContext()inquery.go.Scope
Convert each literal to a builder chain that only sets fields whose easymongo value is non-nil:
find_query.go:FindOneOptions(99-114),FindOptions(140-166),CountOptions(182-191),DistinctOptions(206-209).aggregate.go:AggregateOptions(40-51).update_query.go:UpdateOptions(52-61).ArrayFilterschanges fromoptions.ArrayFiltersto[]any; updateUpdateQuery.arrayFiltersand its setter.delete_query.go:DeleteOptions(25-31) splits intoDeleteOneOptionsandDeleteManyOptionsbuilders (options.DeleteOne(),options.DeleteMany()).find_and_query.go:FindOneAndUpdateOptions(127-141),FindOneAndReplaceOptions(147-160),FindOneAndDeleteOptions(191-201);ArrayFiltersas above;options.ReturnDocument,options.Before,options.Afterare unchanged.MaxTime:assignment; confirmgetContext()is applied to each of these calls so the timeout still takes effect.insert_query.go,replace_query.go,index.go,database.goalready use constructors (options.InsertOne(),options.Replace(),options.CreateIndexes(),options.ListCollections().SetNameOnly(true),options.Collection()); verify they compile and thatIndextypes still match.Implementation details
options.Lister[T]isinterface{ List() []func(*T) error }; everyoptions.Find()-style builder satisfies it. Builder setters verified in v2.9.0 forFindOptionsBuilder:SetAllowDiskUse,SetAllowPartialResults,SetBatchSize(int32),SetCollation(*options.Collation),SetComment(any),SetHint(any),SetLimit(int64),SetProjection(any),SetSkip(int64),SetSort(any),SetNoCursorTimeout(bool).FindOne(),Count(),Distinct(),Aggregate(),UpdateOne(),UpdateMany(),DeleteOne(),DeleteMany(),FindOneAndUpdate(),FindOneAndReplace(),FindOneAndDelete()have the matching subsets.UpdateOptions.ArrayFiltersis[]anywithSetArrayFilters([]any). Collection method shapes, for example:coll.Find(ctx, filter any, opts ...options.Lister[options.FindOptions]) (*mongo.Cursor, error),coll.DeleteOne(ctx, filter any, opts ...options.Lister[options.DeleteOneOptions]) (*mongo.DeleteResult, error).Conversion pattern to apply everywhere (only set what the easymongo query has):
Where easymongo stored
*options.ArrayFilters, store[]anyand change the setter's parameter type to[]any(or keep a...anyvariadic for convenience).For
DeleteQuery, buildoptions.DeleteOne()inOne()andoptions.DeleteMany()inMany()from the sameCollation/Hintfields.Tests first
Existing tests for each file must pass unchanged. Add:
Listerto a zero struct).Timeouton aFindQuerystill cancels a deliberately slow query ($wheresleep or a largeLimiton a big collection) withErrTimeoutOccurred.ArrayFilterson a nested array succeeds (update_query_test.go).Acceptance
go vet ./...clean; no reference toMaxTimeoroptions.ArrayFiltersremains.Depends on: #2. Lands in the same pull request as #2, #4 and #5.