Migration from the MongoDB Go driver
How to move code from go.mongodb.org/mongo-driver to doc: what is identical, what changes, and what is not there.
doc's Go API is shaped like the official MongoDB Go driver on purpose. The method names, the result types, the options builders, and the cursor model match, so most code moves over by changing imports and how you open the handle. This page lists what is the same, what changes, and what doc does not have.
The short version
Replace the connect call and the import paths.
Everything from Database and Collection down looks the same.
// MongoDB Go driver
import (
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/bson"
)
client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
coll := client.Database("shop").Collection("users")
res, err := coll.InsertOne(ctx, bson.M{"name": "ada"})
// doc
import (
"github.com/tamnd/doc"
"github.com/tamnd/doc/options"
)
db, err := doc.Open("app.doc")
coll := db.Database("shop").Collection("users")
res, err := coll.InsertOne(ctx, doc.M{"name": "ada"})
Opening the database
This is the one real difference in everyday code.
There is no server and no connection string.
doc.Open(path, opts...) returns a *doc.DB, which plays the role of the driver's *mongo.Client.
Use :memory: for an in-memory database.
Tune it with functional options instead of a URI; see configuration.
| MongoDB driver | doc |
|---|---|
mongo.Connect(ctx, options.Client().ApplyURI(uri)) |
doc.Open(path, opts...) |
client.Ping(ctx, nil) |
not needed; Open either succeeds or returns an error |
client.Disconnect(ctx) |
db.Close() |
db.Close() is idempotent, and any handle derived from a closed DB returns doc.ErrClosed.
Types
The helper types have the same names and roles, under the doc package instead of bson.
| MongoDB driver | doc |
|---|---|
bson.M |
doc.M |
bson.D |
doc.D |
bson.A |
doc.A |
bson.E |
doc.E |
primitive.ObjectID |
doc.ObjectID |
mongo.ErrNoDocuments |
doc.ErrNoDocuments |
Struct tags are unchanged: doc reads the same bson:"..." tags when it decodes into a struct.
Options
The options builders live in github.com/tamnd/doc/options and read the same.
opts := options.Update().SetUpsert(true)
res, err := coll.UpdateOne(ctx, filter, update, opts)
after := options.FindOneAndUpdate().SetReturnDocument(options.After)
err := coll.FindOneAndUpdate(ctx, filter, update, after).Decode(&out)
Operations that are identical
These work the same way, with the same signatures and result types:
- Reads:
FindOne,Find,CountDocuments,EstimatedDocumentCount,Distinct,Aggregate. - Writes:
InsertOne,InsertMany,UpdateOne,UpdateMany,ReplaceOne,DeleteOne,DeleteMany,BulkWrite. - Find-and-modify:
FindOneAndUpdate,FindOneAndReplace,FindOneAndDelete. - Cursors:
cur.All(ctx, &slice),cur.Next(ctx),cur.Decode(&v),cur.Current(),cur.Err(),cur.Close(ctx). - Indexes:
coll.Indexes().CreateOne,CreateMany,List,ListSpecifications,DropOne,DropAll, withIndexModeland the index options. - Bulk models:
doc.NewInsertOneModel,doc.NewUpdateOneModel,doc.NewDeleteOneModel, and the rest, returning a*BulkWriteResult. - Admin:
db.ListCollectionNames,db.ListDatabaseNames,coll.Drop,db.Drop,db.RunCommand.
The MongoDB Query Language and the aggregation pipeline are the same language, so your filters, updates, and pipelines carry over unchanged.
Transactions and change streams
Sessions and transactions follow the driver shape: db.StartSession, sess.WithTransaction, and the manual sess.StartTransaction / CommitTransaction / AbortTransaction with sess.EndSession.
For the manual form, bind a context to the session with doc.NewSessionContext(ctx, sess) so the operations you run join the transaction.
Isolation is snapshot by default and serializable on request through PRAGMA default_isolation, which replaces the driver's read-concern and write-concern knobs for a single-file database.
Change streams use coll.Watch(ctx, pipeline) and db.Watch(ctx, pipeline) with the same event shape.
See the transactions guide.
What is not there
doc is one file on one machine, so the parts of the driver that exist only because MongoDB is a distributed server have no equivalent:
- No connection strings, hosts, ports, or connection pools (until you run the wire server, which a normal driver then connects to in the usual way).
- No replica sets, no read preferences, and no per-operation read or write concern across nodes. Durability is set once with
synchronous, and isolation withdefault_isolation. - No sharding, no
mongos, and no zones. - A
.docfile is single-writer. Run one writer at a time, and put the wire server in front when several processes need to share one database.
If you reach for one of these, you are reaching past what an embedded single-file database does. Everything else is the API you already know.
Talking to doc with the real driver
You do not have to port at all if you would rather keep the official driver.
Run doc <file> serve and point go.mongodb.org/mongo-driver at mongodb://localhost:27017.
The wire server was checked against the official Go, Node, and Python drivers and mongosh.
See the wire-server guide.