newline battles continue

This commit is contained in:
bel
2020-01-19 20:41:30 +00:00
parent 98adb53caf
commit 573696774e
1456 changed files with 501133 additions and 6 deletions

42
vendor/go.mongodb.org/mongo-driver/mongo/batch_cursor.go generated vendored Executable file
View File

@@ -0,0 +1,42 @@
package mongo
import (
"context"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
"go.mongodb.org/mongo-driver/x/mongo/driver"
)
// batchCursor is the interface implemented by types that can provide batches of document results.
// The Cursor type is built on top of this type.
type batchCursor interface {
// ID returns the ID of the cursor.
ID() int64
// Next returns true if there is a batch available.
Next(context.Context) bool
// Batch will return a DocumentSequence for the current batch of documents. The returned
// DocumentSequence is only valid until the next call to Next or Close.
Batch() *bsoncore.DocumentSequence
// Server returns a pointer to the cursor's server.
Server() driver.Server
// Err returns the last error encountered.
Err() error
// Close closes the cursor.
Close(context.Context) error
}
// changeStreamCursor is the interface implemented by batch cursors that also provide the functionality for retrieving
// a postBatchResumeToken from commands and allows for the cursor to be killed rather than closed
type changeStreamCursor interface {
batchCursor
// PostBatchResumeToken returns the latest seen post batch resume token.
PostBatchResumeToken() bsoncore.Document
// KillCursor kills cursor on server without closing batch cursor
KillCursor(context.Context) error
}

341
vendor/go.mongodb.org/mongo-driver/mongo/bulk_write.go generated vendored Executable file
View File

@@ -0,0 +1,341 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/x/mongo/driverlegacy"
)
// WriteModel is the interface satisfied by all models for bulk writes.
type WriteModel interface {
convertModel() driverlegacy.WriteModel
}
// InsertOneModel is the write model for insert operations.
type InsertOneModel struct {
Document interface{}
}
// NewInsertOneModel creates a new InsertOneModel.
func NewInsertOneModel() *InsertOneModel {
return &InsertOneModel{}
}
// SetDocument sets the BSON document for the InsertOneModel.
func (iom *InsertOneModel) SetDocument(doc interface{}) *InsertOneModel {
iom.Document = doc
return iom
}
func (iom *InsertOneModel) convertModel() driverlegacy.WriteModel {
return driverlegacy.InsertOneModel{
Document: iom.Document,
}
}
// DeleteOneModel is the write model for delete operations.
type DeleteOneModel struct {
Filter interface{}
Collation *options.Collation
}
// NewDeleteOneModel creates a new DeleteOneModel.
func NewDeleteOneModel() *DeleteOneModel {
return &DeleteOneModel{}
}
// SetFilter sets the filter for the DeleteOneModel.
func (dom *DeleteOneModel) SetFilter(filter interface{}) *DeleteOneModel {
dom.Filter = filter
return dom
}
// SetCollation sets the collation for the DeleteOneModel.
func (dom *DeleteOneModel) SetCollation(collation *options.Collation) *DeleteOneModel {
dom.Collation = collation
return dom
}
func (dom *DeleteOneModel) convertModel() driverlegacy.WriteModel {
return driverlegacy.DeleteOneModel{
Collation: dom.Collation,
Filter: dom.Filter,
}
}
// DeleteManyModel is the write model for deleteMany operations.
type DeleteManyModel struct {
Filter interface{}
Collation *options.Collation
}
// NewDeleteManyModel creates a new DeleteManyModel.
func NewDeleteManyModel() *DeleteManyModel {
return &DeleteManyModel{}
}
// SetFilter sets the filter for the DeleteManyModel.
func (dmm *DeleteManyModel) SetFilter(filter interface{}) *DeleteManyModel {
dmm.Filter = filter
return dmm
}
// SetCollation sets the collation for the DeleteManyModel.
func (dmm *DeleteManyModel) SetCollation(collation *options.Collation) *DeleteManyModel {
dmm.Collation = collation
return dmm
}
func (dmm *DeleteManyModel) convertModel() driverlegacy.WriteModel {
return driverlegacy.DeleteManyModel{
Collation: dmm.Collation,
Filter: dmm.Filter,
}
}
// ReplaceOneModel is the write model for replace operations.
type ReplaceOneModel struct {
Collation *options.Collation
Upsert *bool
Filter interface{}
Replacement interface{}
}
// NewReplaceOneModel creates a new ReplaceOneModel.
func NewReplaceOneModel() *ReplaceOneModel {
return &ReplaceOneModel{}
}
// SetFilter sets the filter for the ReplaceOneModel.
func (rom *ReplaceOneModel) SetFilter(filter interface{}) *ReplaceOneModel {
rom.Filter = filter
return rom
}
// SetReplacement sets the replacement document for the ReplaceOneModel.
func (rom *ReplaceOneModel) SetReplacement(rep interface{}) *ReplaceOneModel {
rom.Replacement = rep
return rom
}
// SetCollation sets the collation for the ReplaceOneModel.
func (rom *ReplaceOneModel) SetCollation(collation *options.Collation) *ReplaceOneModel {
rom.Collation = collation
return rom
}
// SetUpsert specifies if a new document should be created if no document matches the query.
func (rom *ReplaceOneModel) SetUpsert(upsert bool) *ReplaceOneModel {
rom.Upsert = &upsert
return rom
}
func (rom *ReplaceOneModel) convertModel() driverlegacy.WriteModel {
um := driverlegacy.UpdateModel{
Collation: rom.Collation,
}
if rom.Upsert != nil {
um.Upsert = *rom.Upsert
um.UpsertSet = true
}
return driverlegacy.ReplaceOneModel{
UpdateModel: um,
Filter: rom.Filter,
Replacement: rom.Replacement,
}
}
// UpdateOneModel is the write model for update operations.
type UpdateOneModel struct {
Collation *options.Collation
Upsert *bool
Filter interface{}
Update interface{}
ArrayFilters *options.ArrayFilters
}
// NewUpdateOneModel creates a new UpdateOneModel.
func NewUpdateOneModel() *UpdateOneModel {
return &UpdateOneModel{}
}
// SetFilter sets the filter for the UpdateOneModel.
func (uom *UpdateOneModel) SetFilter(filter interface{}) *UpdateOneModel {
uom.Filter = filter
return uom
}
// SetUpdate sets the update document for the UpdateOneModel.
func (uom *UpdateOneModel) SetUpdate(update interface{}) *UpdateOneModel {
uom.Update = update
return uom
}
// SetArrayFilters specifies a set of filters specifying to which array elements an update should apply.
func (uom *UpdateOneModel) SetArrayFilters(filters options.ArrayFilters) *UpdateOneModel {
uom.ArrayFilters = &filters
return uom
}
// SetCollation sets the collation for the UpdateOneModel.
func (uom *UpdateOneModel) SetCollation(collation *options.Collation) *UpdateOneModel {
uom.Collation = collation
return uom
}
// SetUpsert specifies if a new document should be created if no document matches the query.
func (uom *UpdateOneModel) SetUpsert(upsert bool) *UpdateOneModel {
uom.Upsert = &upsert
return uom
}
func (uom *UpdateOneModel) convertModel() driverlegacy.WriteModel {
um := driverlegacy.UpdateModel{
Collation: uom.Collation,
}
if uom.Upsert != nil {
um.Upsert = *uom.Upsert
um.UpsertSet = true
}
converted := driverlegacy.UpdateOneModel{
UpdateModel: um,
Filter: uom.Filter,
Update: uom.Update,
}
if uom.ArrayFilters != nil {
converted.ArrayFilters = *uom.ArrayFilters
converted.ArrayFiltersSet = true
}
return converted
}
// UpdateManyModel is the write model for updateMany operations.
type UpdateManyModel struct {
Collation *options.Collation
Upsert *bool
Filter interface{}
Update interface{}
ArrayFilters *options.ArrayFilters
}
// NewUpdateManyModel creates a new UpdateManyModel.
func NewUpdateManyModel() *UpdateManyModel {
return &UpdateManyModel{}
}
// SetFilter sets the filter for the UpdateManyModel.
func (umm *UpdateManyModel) SetFilter(filter interface{}) *UpdateManyModel {
umm.Filter = filter
return umm
}
// SetUpdate sets the update document for the UpdateManyModel.
func (umm *UpdateManyModel) SetUpdate(update interface{}) *UpdateManyModel {
umm.Update = update
return umm
}
// SetArrayFilters specifies a set of filters specifying to which array elements an update should apply.
func (umm *UpdateManyModel) SetArrayFilters(filters options.ArrayFilters) *UpdateManyModel {
umm.ArrayFilters = &filters
return umm
}
// SetCollation sets the collation for the UpdateManyModel.
func (umm *UpdateManyModel) SetCollation(collation *options.Collation) *UpdateManyModel {
umm.Collation = collation
return umm
}
// SetUpsert specifies if a new document should be created if no document matches the query.
func (umm *UpdateManyModel) SetUpsert(upsert bool) *UpdateManyModel {
umm.Upsert = &upsert
return umm
}
func (umm *UpdateManyModel) convertModel() driverlegacy.WriteModel {
um := driverlegacy.UpdateModel{
Collation: umm.Collation,
}
if umm.Upsert != nil {
um.Upsert = *umm.Upsert
um.UpsertSet = true
}
converted := driverlegacy.UpdateManyModel{
UpdateModel: um,
Filter: umm.Filter,
Update: umm.Update,
}
if umm.ArrayFilters != nil {
converted.ArrayFilters = *umm.ArrayFilters
converted.ArrayFiltersSet = true
}
return converted
}
func dispatchToMongoModel(model driverlegacy.WriteModel) WriteModel {
switch conv := model.(type) {
case driverlegacy.InsertOneModel:
return &InsertOneModel{
Document: conv.Document,
}
case driverlegacy.DeleteOneModel:
return &DeleteOneModel{
Filter: conv.Filter,
Collation: conv.Collation,
}
case driverlegacy.DeleteManyModel:
return &DeleteManyModel{
Filter: conv.Filter,
Collation: conv.Collation,
}
case driverlegacy.ReplaceOneModel:
rom := &ReplaceOneModel{
Filter: conv.Filter,
Replacement: conv.Replacement,
Collation: conv.Collation,
}
if conv.UpsertSet {
rom.Upsert = &conv.Upsert
}
return rom
case driverlegacy.UpdateOneModel:
uom := &UpdateOneModel{
Filter: conv.Filter,
Update: conv.Update,
Collation: conv.Collation,
}
if conv.UpsertSet {
uom.Upsert = &conv.Upsert
}
if conv.ArrayFiltersSet {
uom.ArrayFilters = &conv.ArrayFilters
}
return uom
case driverlegacy.UpdateManyModel:
umm := &UpdateManyModel{
Filter: conv.Filter,
Update: conv.Update,
Collation: conv.Collation,
}
if conv.UpsertSet {
umm.Upsert = &conv.Upsert
}
if conv.ArrayFiltersSet {
umm.ArrayFilters = &conv.ArrayFilters
}
return umm
}
return nil
}

489
vendor/go.mongodb.org/mongo-driver/mongo/change_stream.go generated vendored Executable file
View File

@@ -0,0 +1,489 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"context"
"errors"
"fmt"
"reflect"
"strconv"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readconcern"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
"go.mongodb.org/mongo-driver/x/mongo/driver"
"go.mongodb.org/mongo-driver/x/mongo/driver/description"
"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
"go.mongodb.org/mongo-driver/x/mongo/driver/session"
)
const errorInterrupted int32 = 11601
const errorCappedPositionLost int32 = 136
const errorCursorKilled int32 = 237
// ErrMissingResumeToken indicates that a change stream notification from the server did not
// contain a resume token.
var ErrMissingResumeToken = errors.New("cannot provide resume functionality when the resume token is missing")
// ErrNilCursor indicates that the cursor for the change stream is nil.
var ErrNilCursor = errors.New("cursor is nil")
// ChangeStream instances iterate a stream of change documents. Each document can be decoded via the
// Decode method. Resume tokens should be retrieved via the ResumeToken method and can be stored to
// resume the change stream at a specific point in time.
//
// A typical usage of the ChangeStream type would be:
type ChangeStream struct {
Current bson.Raw
aggregate *operation.Aggregate
pipelineSlice []bsoncore.Document
cursor changeStreamCursor
cursorOptions driver.CursorOptions
batch []bsoncore.Document
resumeToken bson.Raw
err error
sess *session.Client
client *Client
registry *bsoncodec.Registry
streamType StreamType
options *options.ChangeStreamOptions
selector description.ServerSelector
operationTime *primitive.Timestamp
}
type changeStreamConfig struct {
readConcern *readconcern.ReadConcern
readPreference *readpref.ReadPref
client *Client
registry *bsoncodec.Registry
streamType StreamType
collectionName string
databaseName string
}
func newChangeStream(ctx context.Context, config changeStreamConfig, pipeline interface{},
opts ...*options.ChangeStreamOptions) (*ChangeStream, error) {
if ctx == nil {
ctx = context.Background()
}
cs := &ChangeStream{
client: config.client,
registry: config.registry,
streamType: config.streamType,
options: options.MergeChangeStreamOptions(opts...),
selector: description.ReadPrefSelector(config.readPreference),
}
cs.sess = sessionFromContext(ctx)
if cs.sess == nil && cs.client.topology.SessionPool != nil {
cs.sess, cs.err = session.NewClientSession(cs.client.topology.SessionPool, cs.client.id, session.Implicit)
if cs.err != nil {
return nil, cs.Err()
}
}
if cs.err = cs.client.validSession(cs.sess); cs.err != nil {
closeImplicitSession(cs.sess)
return nil, cs.Err()
}
cs.aggregate = operation.NewAggregate(nil).
ReadPreference(config.readPreference).ReadConcern(config.readConcern).
Deployment(cs.client.topology).ClusterClock(cs.client.clock).
CommandMonitor(cs.client.monitor).Session(cs.sess).ServerSelector(cs.selector)
if cs.options.Collation != nil {
cs.aggregate.Collation(bsoncore.Document(cs.options.Collation.ToDocument()))
}
if cs.options.BatchSize != nil {
cs.cursorOptions.BatchSize = *cs.options.BatchSize
}
if cs.options.MaxAwaitTime != nil {
cs.cursorOptions.MaxTimeMS = int64(time.Duration(*cs.options.MaxAwaitTime) / time.Millisecond)
}
cs.cursorOptions.CommandMonitor = cs.client.monitor
switch cs.streamType {
case ClientStream:
cs.aggregate.Database("admin")
case DatabaseStream:
cs.aggregate.Database(config.databaseName)
case CollectionStream:
cs.aggregate.Collection(config.collectionName).Database(config.databaseName)
default:
closeImplicitSession(cs.sess)
return nil, fmt.Errorf("must supply a valid StreamType in config, instead of %v", cs.streamType)
}
// When starting a change stream, cache startAfter as the first resume token if it is set. If not, cache
// resumeAfter. If neither is set, do not cache a resume token.
resumeToken := cs.options.StartAfter
if resumeToken == nil {
resumeToken = cs.options.ResumeAfter
}
var marshaledToken bson.Raw
if resumeToken != nil {
if marshaledToken, cs.err = bson.Marshal(resumeToken); cs.err != nil {
closeImplicitSession(cs.sess)
return nil, cs.Err()
}
}
cs.resumeToken = marshaledToken
if cs.err = cs.buildPipelineSlice(pipeline); cs.err != nil {
closeImplicitSession(cs.sess)
return nil, cs.Err()
}
var pipelineArr bsoncore.Document
pipelineArr, cs.err = cs.pipelineToBSON()
cs.aggregate.Pipeline(pipelineArr)
if cs.err = cs.executeOperation(ctx, false); cs.err != nil {
closeImplicitSession(cs.sess)
return nil, cs.Err()
}
return cs, cs.Err()
}
func (cs *ChangeStream) executeOperation(ctx context.Context, resuming bool) error {
var server driver.Server
var conn driver.Connection
if server, cs.err = cs.client.topology.SelectServer(ctx, cs.selector); cs.err != nil {
return cs.Err()
}
if conn, cs.err = server.Connection(ctx); cs.err != nil {
return cs.Err()
}
cs.aggregate.Deployment(driver.SingleConnectionDeployment{
C: conn,
})
if resuming {
cs.replaceOptions(ctx, conn.Description().WireVersion) // pass wire version
csOptDoc := cs.createPipelineOptionsDoc()
pipIdx, pipDoc := bsoncore.AppendDocumentStart(nil)
pipDoc = bsoncore.AppendDocumentElement(pipDoc, "$changeStream", csOptDoc)
if pipDoc, cs.err = bsoncore.AppendDocumentEnd(pipDoc, pipIdx); cs.err != nil {
return cs.Err()
}
cs.pipelineSlice[0] = pipDoc
var plArr bsoncore.Document
if plArr, cs.err = cs.pipelineToBSON(); cs.err != nil {
return cs.Err()
}
cs.aggregate.Pipeline(plArr)
}
if cs.err = replaceErrors(cs.aggregate.Execute(ctx)); cs.err != nil {
return cs.Err()
}
cr := cs.aggregate.ResultCursorResponse()
cr.Server = server
cs.cursor, cs.err = driver.NewBatchCursor(cr, cs.sess, cs.client.clock, cs.cursorOptions)
if cs.err = replaceErrors(cs.err); cs.err != nil {
return cs.Err()
}
cs.updatePbrtFromCommand()
if cs.options.StartAtOperationTime == nil && cs.options.ResumeAfter == nil &&
cs.options.StartAfter == nil && conn.Description().WireVersion.Max >= 7 &&
cs.emptyBatch() && cs.resumeToken == nil {
cs.operationTime = cs.sess.OperationTime
}
return cs.Err()
}
// Updates the post batch resume token after a successful aggregate or getMore operation.
func (cs *ChangeStream) updatePbrtFromCommand() {
// Only cache the pbrt if an empty batch was returned and a pbrt was included
if pbrt := cs.cursor.PostBatchResumeToken(); cs.emptyBatch() && pbrt != nil {
cs.resumeToken = bson.Raw(pbrt)
}
}
func (cs *ChangeStream) storeResumeToken() error {
// If cs.Current is the last document in the batch and a pbrt is included, cache the pbrt
// Otherwise, cache the _id of the document
var tokenDoc bson.Raw
if len(cs.batch) == 0 {
if pbrt := cs.cursor.PostBatchResumeToken(); pbrt != nil {
tokenDoc = bson.Raw(pbrt)
}
}
if tokenDoc == nil {
var ok bool
tokenDoc, ok = cs.Current.Lookup("_id").DocumentOK()
if !ok {
_ = cs.Close(context.Background())
return ErrMissingResumeToken
}
}
cs.resumeToken = tokenDoc
return nil
}
func (cs *ChangeStream) buildPipelineSlice(pipeline interface{}) error {
val := reflect.ValueOf(pipeline)
if !val.IsValid() || !(val.Kind() == reflect.Slice) {
cs.err = errors.New("can only transform slices and arrays into aggregation pipelines, but got invalid")
return cs.err
}
cs.pipelineSlice = make([]bsoncore.Document, 0, val.Len()+1)
csIdx, csDoc := bsoncore.AppendDocumentStart(nil)
csDocTemp := cs.createPipelineOptionsDoc()
if cs.err != nil {
return cs.err
}
csDoc = bsoncore.AppendDocumentElement(csDoc, "$changeStream", csDocTemp)
csDoc, cs.err = bsoncore.AppendDocumentEnd(csDoc, csIdx)
if cs.err != nil {
return cs.err
}
cs.pipelineSlice = append(cs.pipelineSlice, csDoc)
for i := 0; i < val.Len(); i++ {
var elem []byte
elem, cs.err = transformBsoncoreDocument(cs.registry, val.Index(i).Interface())
if cs.err != nil {
return cs.err
}
cs.pipelineSlice = append(cs.pipelineSlice, elem)
}
return cs.err
}
func (cs *ChangeStream) createPipelineOptionsDoc() bsoncore.Document {
plDocIdx, plDoc := bsoncore.AppendDocumentStart(nil)
if cs.streamType == ClientStream {
plDoc = bsoncore.AppendBooleanElement(plDoc, "allChangesForCluster", true)
}
if cs.options.FullDocument != nil {
plDoc = bsoncore.AppendStringElement(plDoc, "fullDocument", string(*cs.options.FullDocument))
}
if cs.options.ResumeAfter != nil {
var raDoc bsoncore.Document
raDoc, cs.err = transformBsoncoreDocument(cs.registry, cs.options.ResumeAfter)
if cs.err != nil {
return nil
}
plDoc = bsoncore.AppendDocumentElement(plDoc, "resumeAfter", raDoc)
}
if cs.options.StartAfter != nil {
var saDoc bsoncore.Document
saDoc, cs.err = transformBsoncoreDocument(cs.registry, cs.options.StartAfter)
if cs.err != nil {
return nil
}
plDoc = bsoncore.AppendDocumentElement(plDoc, "startAfter", saDoc)
}
if cs.options.StartAtOperationTime != nil {
plDoc = bsoncore.AppendTimestampElement(plDoc, "startAtOperationTime", cs.options.StartAtOperationTime.T, cs.options.StartAtOperationTime.I)
}
if plDoc, cs.err = bsoncore.AppendDocumentEnd(plDoc, plDocIdx); cs.err != nil {
return nil
}
return plDoc
}
func (cs *ChangeStream) pipelineToBSON() (bsoncore.Document, error) {
pipelineDocIdx, pipelineArr := bsoncore.AppendArrayStart(nil)
for i, doc := range cs.pipelineSlice {
pipelineArr = bsoncore.AppendDocumentElement(pipelineArr, strconv.Itoa(i), doc)
}
if pipelineArr, cs.err = bsoncore.AppendArrayEnd(pipelineArr, pipelineDocIdx); cs.err != nil {
return nil, cs.err
}
return pipelineArr, cs.err
}
func (cs *ChangeStream) replaceOptions(ctx context.Context, wireVersion *description.VersionRange) {
// Cached resume token: use the resume token as the resumeAfter option and set no other resume options
if cs.resumeToken != nil {
cs.options.SetResumeAfter(cs.resumeToken)
cs.options.SetStartAfter(nil)
cs.options.SetStartAtOperationTime(nil)
return
}
// No cached resume token but cached operation time: use the operation time as the startAtOperationTime option and
// set no other resume options
if (cs.sess.OperationTime != nil || cs.options.StartAtOperationTime != nil) && wireVersion.Max >= 7 {
opTime := cs.options.StartAtOperationTime
if cs.operationTime != nil {
opTime = cs.sess.OperationTime
}
cs.options.SetStartAtOperationTime(opTime)
cs.options.SetResumeAfter(nil)
cs.options.SetStartAfter(nil)
return
}
// No cached resume token or operation time: set none of the resume options
cs.options.SetResumeAfter(nil)
cs.options.SetStartAfter(nil)
cs.options.SetStartAtOperationTime(nil)
}
// ID returns the cursor ID for this change stream.
func (cs *ChangeStream) ID() int64 {
if cs.cursor == nil {
return 0
}
return cs.cursor.ID()
}
// Decode will decode the current document into val.
func (cs *ChangeStream) Decode(val interface{}) error {
if cs.cursor == nil {
return ErrNilCursor
}
return bson.UnmarshalWithRegistry(cs.registry, cs.Current, val)
}
// Err returns the current error.
func (cs *ChangeStream) Err() error {
if cs.err != nil {
return replaceErrors(cs.err)
}
if cs.cursor == nil {
return nil
}
return replaceErrors(cs.cursor.Err())
}
// Close closes this cursor.
func (cs *ChangeStream) Close(ctx context.Context) error {
if ctx == nil {
ctx = context.Background()
}
closeImplicitSession(cs.sess)
if cs.cursor == nil {
return nil // cursor is already closed
}
cs.err = replaceErrors(cs.cursor.Close(ctx))
cs.cursor = nil
return cs.Err()
}
// ResumeToken returns the last cached resume token for this change stream.
func (cs *ChangeStream) ResumeToken() bson.Raw {
return cs.resumeToken
}
// Next gets the next result from this change stream. Returns true if there were no errors and the next
// result is available for decoding.
func (cs *ChangeStream) Next(ctx context.Context) bool {
if ctx == nil {
ctx = context.Background()
}
if len(cs.batch) == 0 {
cs.loopNext(ctx)
if cs.err != nil || len(cs.batch) == 0 {
cs.err = replaceErrors(cs.err)
return false
}
}
cs.Current = bson.Raw(cs.batch[0])
cs.batch = cs.batch[1:]
if cs.err = cs.storeResumeToken(); cs.err != nil {
return false
}
return true
}
func (cs *ChangeStream) loopNext(ctx context.Context) {
for {
if cs.cursor == nil {
return
}
if cs.cursor.Next(ctx) {
// If this is the first batch, the batch cursor will return true, but the batch could be empty.
if cs.batch, cs.err = cs.cursor.Batch().Documents(); cs.err != nil || len(cs.batch) > 0 {
return
}
// no error but empty batch
cs.updatePbrtFromCommand()
continue
}
cs.err = replaceErrors(cs.cursor.Err())
if cs.err == nil {
// If a getMore was done but the batch was empty, the batch cursor will return false with no error
if len(cs.batch) == 0 {
continue
}
return
}
switch t := cs.err.(type) {
case CommandError:
if t.Code == errorInterrupted || t.Code == errorCappedPositionLost || t.Code == errorCursorKilled || t.HasErrorLabel("NonResumableChangeStreamError") {
return
}
}
// ignore error from cursor close because if the cursor is deleted or errors we tried to close it and will remake and try to get next batch
_ = cs.cursor.Close(ctx)
if cs.err = cs.executeOperation(ctx, true); cs.err != nil {
return
}
}
}
// Returns true if the underlying cursor's batch is empty
func (cs *ChangeStream) emptyBatch() bool {
return len(cs.cursor.Batch().Data) == 5 // empty BSON array
}
// StreamType represents the type of a change stream.
type StreamType uint8
// These constants represent valid change stream types. A change stream can be initialized over a collection, all
// collections in a database, or over a whole client.
const (
CollectionStream StreamType = iota
DatabaseStream
ClientStream
)

564
vendor/go.mongodb.org/mongo-driver/mongo/client.go generated vendored Executable file
View File

@@ -0,0 +1,564 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"context"
"crypto/tls"
"strings"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/event"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readconcern"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
"go.mongodb.org/mongo-driver/x/mongo/driver"
"go.mongodb.org/mongo-driver/x/mongo/driver/auth"
"go.mongodb.org/mongo-driver/x/mongo/driver/connstring"
"go.mongodb.org/mongo-driver/x/mongo/driver/description"
"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
"go.mongodb.org/mongo-driver/x/mongo/driver/session"
"go.mongodb.org/mongo-driver/x/mongo/driver/topology"
"go.mongodb.org/mongo-driver/x/mongo/driver/uuid"
"go.mongodb.org/mongo-driver/x/mongo/driverlegacy"
"go.mongodb.org/mongo-driver/x/network/command"
)
const defaultLocalThreshold = 15 * time.Millisecond
// Client performs operations on a given topology.
type Client struct {
id uuid.UUID
topologyOptions []topology.Option
topology *topology.Topology
connString connstring.ConnString
localThreshold time.Duration
retryWrites bool
clock *session.ClusterClock
readPreference *readpref.ReadPref
readConcern *readconcern.ReadConcern
writeConcern *writeconcern.WriteConcern
registry *bsoncodec.Registry
marshaller BSONAppender
monitor *event.CommandMonitor
}
// Connect creates a new Client and then initializes it using the Connect method.
func Connect(ctx context.Context, opts ...*options.ClientOptions) (*Client, error) {
c, err := NewClient(opts...)
if err != nil {
return nil, err
}
err = c.Connect(ctx)
if err != nil {
return nil, err
}
return c, nil
}
// NewClient creates a new client to connect to a cluster specified by the uri.
//
// When creating an options.ClientOptions, the order the methods are called matters. Later Set*
// methods will overwrite the values from previous Set* method invocations. This includes the
// ApplyURI method. This allows callers to determine the order of precedence for option
// application. For instance, if ApplyURI is called before SetAuth, the Credential from
// SetAuth will overwrite the values from the connection string. If ApplyURI is called
// after SetAuth, then its values will overwrite those from SetAuth.
//
// The opts parameter is processed using options.MergeClientOptions, which will overwrite entire
// option fields of previous options, there is no partial overwriting. For example, if Username is
// set in the Auth field for the first option, and Password is set for the second but with no
// Username, after the merge the Username field will be empty.
func NewClient(opts ...*options.ClientOptions) (*Client, error) {
clientOpt := options.MergeClientOptions(opts...)
id, err := uuid.New()
if err != nil {
return nil, err
}
client := &Client{id: id}
err = client.configure(clientOpt)
if err != nil {
return nil, err
}
client.topology, err = topology.New(client.topologyOptions...)
if err != nil {
return nil, replaceErrors(err)
}
return client, nil
}
// Connect initializes the Client by starting background monitoring goroutines.
// This method must be called before a Client can be used.
func (c *Client) Connect(ctx context.Context) error {
err := c.topology.Connect()
if err != nil {
return replaceErrors(err)
}
return nil
}
// Disconnect closes sockets to the topology referenced by this Client. It will
// shut down any monitoring goroutines, close the idle connection pool, and will
// wait until all the in use connections have been returned to the connection
// pool and closed before returning. If the context expires via cancellation,
// deadline, or timeout before the in use connections have returned, the in use
// connections will be closed, resulting in the failure of any in flight read
// or write operations. If this method returns with no errors, all connections
// associated with this Client have been closed.
func (c *Client) Disconnect(ctx context.Context) error {
if ctx == nil {
ctx = context.Background()
}
c.endSessions(ctx)
return replaceErrors(c.topology.Disconnect(ctx))
}
// Ping verifies that the client can connect to the topology.
// If readPreference is nil then will use the client's default read
// preference.
func (c *Client) Ping(ctx context.Context, rp *readpref.ReadPref) error {
if ctx == nil {
ctx = context.Background()
}
if rp == nil {
rp = c.readPreference
}
db := c.Database("admin")
res := db.RunCommand(ctx, bson.D{
{"ping", 1},
})
return replaceErrors(res.Err())
}
// StartSession starts a new session.
func (c *Client) StartSession(opts ...*options.SessionOptions) (Session, error) {
if c.topology.SessionPool == nil {
return nil, ErrClientDisconnected
}
sopts := options.MergeSessionOptions(opts...)
coreOpts := &session.ClientOptions{
DefaultReadConcern: c.readConcern,
DefaultReadPreference: c.readPreference,
DefaultWriteConcern: c.writeConcern,
}
if sopts.CausalConsistency != nil {
coreOpts.CausalConsistency = sopts.CausalConsistency
}
if sopts.DefaultReadConcern != nil {
coreOpts.DefaultReadConcern = sopts.DefaultReadConcern
}
if sopts.DefaultWriteConcern != nil {
coreOpts.DefaultWriteConcern = sopts.DefaultWriteConcern
}
if sopts.DefaultReadPreference != nil {
coreOpts.DefaultReadPreference = sopts.DefaultReadPreference
}
sess, err := session.NewClientSession(c.topology.SessionPool, c.id, session.Explicit, coreOpts)
if err != nil {
return nil, replaceErrors(err)
}
sess.RetryWrite = c.retryWrites
return &sessionImpl{
clientSession: sess,
client: c,
topo: c.topology,
}, nil
}
func (c *Client) endSessions(ctx context.Context) {
if c.topology.SessionPool == nil {
return
}
cmd := command.EndSessions{
Clock: c.clock,
SessionIDs: c.topology.SessionPool.IDSlice(),
}
_, _ = driverlegacy.EndSessions(ctx, cmd, c.topology, description.ReadPrefSelector(readpref.PrimaryPreferred()))
}
func (c *Client) configure(opts *options.ClientOptions) error {
if err := opts.Validate(); err != nil {
return err
}
var connOpts []topology.ConnectionOption
var serverOpts []topology.ServerOption
var topologyOpts []topology.Option
// TODO(GODRIVER-814): Add tests for topology, server, and connection related options.
// AppName
var appName string
if opts.AppName != nil {
appName = *opts.AppName
}
// Compressors & ZlibLevel
var comps []string
if len(opts.Compressors) > 0 {
comps = opts.Compressors
connOpts = append(connOpts, topology.WithCompressors(
func(compressors []string) []string {
return append(compressors, comps...)
},
))
for _, comp := range comps {
if comp == "zlib" {
connOpts = append(connOpts, topology.WithZlibLevel(func(level *int) *int {
return opts.ZlibLevel
}))
}
}
serverOpts = append(serverOpts, topology.WithCompressionOptions(
func(opts ...string) []string { return append(opts, comps...) },
))
}
// Handshaker
var handshaker = func(driver.Handshaker) driver.Handshaker {
return operation.NewIsMaster().AppName(appName).Compressors(comps)
}
// Auth & Database & Password & Username
if opts.Auth != nil {
cred := &auth.Cred{
Username: opts.Auth.Username,
Password: opts.Auth.Password,
PasswordSet: opts.Auth.PasswordSet,
Props: opts.Auth.AuthMechanismProperties,
Source: opts.Auth.AuthSource,
}
mechanism := opts.Auth.AuthMechanism
if len(cred.Source) == 0 {
switch strings.ToUpper(mechanism) {
case auth.MongoDBX509, auth.GSSAPI, auth.PLAIN:
cred.Source = "$external"
default:
cred.Source = "admin"
}
}
authenticator, err := auth.CreateAuthenticator(mechanism, cred)
if err != nil {
return err
}
handshakeOpts := &auth.HandshakeOptions{
AppName: appName,
Authenticator: authenticator,
Compressors: comps,
}
if mechanism == "" {
// Required for SASL mechanism negotiation during handshake
handshakeOpts.DBUser = cred.Source + "." + cred.Username
}
if opts.AuthenticateToAnything != nil && *opts.AuthenticateToAnything {
// Authenticate arbiters
handshakeOpts.PerformAuthentication = func(serv description.Server) bool {
return true
}
}
handshaker = func(driver.Handshaker) driver.Handshaker {
return auth.Handshaker(nil, handshakeOpts)
}
}
connOpts = append(connOpts, topology.WithHandshaker(handshaker))
// ConnectTimeout
if opts.ConnectTimeout != nil {
serverOpts = append(serverOpts, topology.WithHeartbeatTimeout(
func(time.Duration) time.Duration { return *opts.ConnectTimeout },
))
connOpts = append(connOpts, topology.WithConnectTimeout(
func(time.Duration) time.Duration { return *opts.ConnectTimeout },
))
}
// Dialer
if opts.Dialer != nil {
connOpts = append(connOpts, topology.WithDialer(
func(topology.Dialer) topology.Dialer { return opts.Dialer },
))
}
// Direct
if opts.Direct != nil && *opts.Direct {
topologyOpts = append(topologyOpts, topology.WithMode(
func(topology.MonitorMode) topology.MonitorMode { return topology.SingleMode },
))
}
// HeartbeatInterval
if opts.HeartbeatInterval != nil {
serverOpts = append(serverOpts, topology.WithHeartbeatInterval(
func(time.Duration) time.Duration { return *opts.HeartbeatInterval },
))
}
// Hosts
hosts := []string{"localhost:27017"} // default host
if len(opts.Hosts) > 0 {
hosts = opts.Hosts
}
topologyOpts = append(topologyOpts, topology.WithSeedList(
func(...string) []string { return hosts },
))
// LocalThreshold
if opts.LocalThreshold != nil {
c.localThreshold = *opts.LocalThreshold
}
// MaxConIdleTime
if opts.MaxConnIdleTime != nil {
connOpts = append(connOpts, topology.WithIdleTimeout(
func(time.Duration) time.Duration { return *opts.MaxConnIdleTime },
))
}
// MaxPoolSize
if opts.MaxPoolSize != nil {
serverOpts = append(
serverOpts,
topology.WithMaxConnections(func(uint16) uint16 { return *opts.MaxPoolSize }),
topology.WithMaxIdleConnections(func(uint16) uint16 { return *opts.MaxPoolSize }),
)
}
// Monitor
if opts.Monitor != nil {
c.monitor = opts.Monitor
connOpts = append(connOpts, topology.WithMonitor(
func(*event.CommandMonitor) *event.CommandMonitor { return opts.Monitor },
))
}
// ReadConcern
c.readConcern = readconcern.New()
if opts.ReadConcern != nil {
c.readConcern = opts.ReadConcern
}
// ReadPreference
c.readPreference = readpref.Primary()
if opts.ReadPreference != nil {
c.readPreference = opts.ReadPreference
}
// Registry
c.registry = bson.DefaultRegistry
if opts.Registry != nil {
c.registry = opts.Registry
}
// ReplicaSet
if opts.ReplicaSet != nil {
topologyOpts = append(topologyOpts, topology.WithReplicaSetName(
func(string) string { return *opts.ReplicaSet },
))
}
// RetryWrites
if opts.RetryWrites != nil {
c.retryWrites = *opts.RetryWrites
}
// ServerSelectionTimeout
if opts.ServerSelectionTimeout != nil {
topologyOpts = append(topologyOpts, topology.WithServerSelectionTimeout(
func(time.Duration) time.Duration { return *opts.ServerSelectionTimeout },
))
}
// SocketTimeout
if opts.SocketTimeout != nil {
connOpts = append(
connOpts,
topology.WithReadTimeout(func(time.Duration) time.Duration { return *opts.SocketTimeout }),
topology.WithWriteTimeout(func(time.Duration) time.Duration { return *opts.SocketTimeout }),
)
}
// TLSConfig
if opts.TLSConfig != nil {
connOpts = append(connOpts, topology.WithTLSConfig(
func(*tls.Config) *tls.Config {
return opts.TLSConfig
},
))
}
// WriteConcern
if opts.WriteConcern != nil {
c.writeConcern = opts.WriteConcern
}
// ClusterClock
c.clock = new(session.ClusterClock)
serverOpts = append(
serverOpts,
topology.WithClock(func(*session.ClusterClock) *session.ClusterClock { return c.clock }),
topology.WithConnectionOptions(func(...topology.ConnectionOption) []topology.ConnectionOption { return connOpts }),
)
c.topologyOptions = append(topologyOpts, topology.WithServerOptions(
func(...topology.ServerOption) []topology.ServerOption { return serverOpts },
))
return nil
}
// validSession returns an error if the session doesn't belong to the client
func (c *Client) validSession(sess *session.Client) error {
if sess != nil && !uuid.Equal(sess.ClientID, c.id) {
return ErrWrongClient
}
return nil
}
// Database returns a handle for a given database.
func (c *Client) Database(name string, opts ...*options.DatabaseOptions) *Database {
return newDatabase(c, name, opts...)
}
// ListDatabases returns a ListDatabasesResult.
func (c *Client) ListDatabases(ctx context.Context, filter interface{}, opts ...*options.ListDatabasesOptions) (ListDatabasesResult, error) {
if ctx == nil {
ctx = context.Background()
}
sess := sessionFromContext(ctx)
err := c.validSession(sess)
if sess == nil && c.topology.SessionPool != nil {
sess, err = session.NewClientSession(c.topology.SessionPool, c.id, session.Implicit)
if err != nil {
return ListDatabasesResult{}, err
}
defer sess.EndSession()
}
err = c.validSession(sess)
if err != nil {
return ListDatabasesResult{}, err
}
filterDoc, err := transformBsoncoreDocument(c.registry, filter)
if err != nil {
return ListDatabasesResult{}, err
}
selector := description.CompositeSelector([]description.ServerSelector{
description.ReadPrefSelector(readpref.Primary()),
description.LatencySelector(c.localThreshold),
})
if sess != nil && sess.PinnedServer != nil {
selector = sess.PinnedServer
}
ldo := options.MergeListDatabasesOptions(opts...)
op := operation.NewListDatabases(filterDoc).
Session(sess).ReadPreference(c.readPreference).CommandMonitor(c.monitor).
ServerSelector(selector).ClusterClock(c.clock).Database("admin").Deployment(c.topology)
if ldo.NameOnly != nil {
op = op.NameOnly(*ldo.NameOnly)
}
err = op.Execute(ctx)
if err != nil {
return ListDatabasesResult{}, replaceErrors(err)
}
return newListDatabasesResultFromOperation(op.Result()), nil
}
// ListDatabaseNames returns a slice containing the names of all of the databases on the server.
func (c *Client) ListDatabaseNames(ctx context.Context, filter interface{}, opts ...*options.ListDatabasesOptions) ([]string, error) {
opts = append(opts, options.ListDatabases().SetNameOnly(true))
res, err := c.ListDatabases(ctx, filter, opts...)
if err != nil {
return nil, err
}
names := make([]string, 0)
for _, spec := range res.Databases {
names = append(names, spec.Name)
}
return names, nil
}
// WithSession allows a user to start a session themselves and manage
// its lifetime. The only way to provide a session to a CRUD method is
// to invoke that CRUD method with the mongo.SessionContext within the
// closure. The mongo.SessionContext can be used as a regular context,
// so methods like context.WithDeadline and context.WithTimeout are
// supported.
//
// If the context.Context already has a mongo.Session attached, that
// mongo.Session will be replaced with the one provided.
//
// Errors returned from the closure are transparently returned from
// this function.
func WithSession(ctx context.Context, sess Session, fn func(SessionContext) error) error {
return fn(contextWithSession(ctx, sess))
}
// UseSession creates a default session, that is only valid for the
// lifetime of the closure. No cleanup outside of closing the session
// is done upon exiting the closure. This means that an outstanding
// transaction will be aborted, even if the closure returns an error.
//
// If ctx already contains a mongo.Session, that mongo.Session will be
// replaced with the newly created mongo.Session.
//
// Errors returned from the closure are transparently returned from
// this method.
func (c *Client) UseSession(ctx context.Context, fn func(SessionContext) error) error {
return c.UseSessionWithOptions(ctx, options.Session(), fn)
}
// UseSessionWithOptions works like UseSession but allows the caller
// to specify the options used to create the session.
func (c *Client) UseSessionWithOptions(ctx context.Context, opts *options.SessionOptions, fn func(SessionContext) error) error {
defaultSess, err := c.StartSession(opts)
if err != nil {
return err
}
defer defaultSess.EndSession(ctx)
sessCtx := sessionContext{
Context: context.WithValue(ctx, sessionKey{}, defaultSess),
Session: defaultSess,
}
return fn(sessCtx)
}
// Watch returns a change stream cursor used to receive information of changes to the client. This method is preferred
// to running a raw aggregation with a $changeStream stage because it supports resumability in the case of some errors.
// The client must have read concern majority or no read concern for a change stream to be created successfully.
func (c *Client) Watch(ctx context.Context, pipeline interface{},
opts ...*options.ChangeStreamOptions) (*ChangeStream, error) {
if c.topology.SessionPool == nil {
return nil, ErrClientDisconnected
}
csConfig := changeStreamConfig{
readConcern: c.readConcern,
readPreference: c.readPreference,
client: c,
registry: c.registry,
streamType: ClientStream,
}
return newChangeStream(ctx, csConfig, pipeline, opts...)
}

1399
vendor/go.mongodb.org/mongo-driver/mongo/collection.go generated vendored Executable file

File diff suppressed because it is too large Load Diff

225
vendor/go.mongodb.org/mongo-driver/mongo/cursor.go generated vendored Executable file
View File

@@ -0,0 +1,225 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"context"
"errors"
"io"
"reflect"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
"go.mongodb.org/mongo-driver/x/mongo/driver/session"
"go.mongodb.org/mongo-driver/x/mongo/driverlegacy"
)
// Cursor is used to iterate a stream of documents. Each document is decoded into the result
// according to the rules of the bson package.
//
// A typical usage of the Cursor type would be:
//
// var cur *Cursor
// ctx := context.Background()
// defer cur.Close(ctx)
//
// for cur.Next(ctx) {
// elem := &bson.D{}
// if err := cur.Decode(elem); err != nil {
// log.Fatal(err)
// }
//
// // do something with elem....
// }
//
// if err := cur.Err(); err != nil {
// log.Fatal(err)
// }
//
type Cursor struct {
// Current is the BSON bytes of the current document. This property is only valid until the next
// call to Next or Close. If continued access is required to the bson.Raw, you must make a copy
// of it.
Current bson.Raw
bc batchCursor
batch *bsoncore.DocumentSequence
registry *bsoncodec.Registry
clientSession *session.Client
err error
}
func newCursor(bc batchCursor, registry *bsoncodec.Registry) (*Cursor, error) {
return newCursorWithSession(bc, registry, nil)
}
func newCursorWithSession(bc batchCursor, registry *bsoncodec.Registry, clientSession *session.Client) (*Cursor, error) {
if registry == nil {
registry = bson.DefaultRegistry
}
if bc == nil {
return nil, errors.New("batch cursor must not be nil")
}
c := &Cursor{
bc: bc,
registry: registry,
clientSession: clientSession,
}
if bc.ID() == 0 {
c.closeImplicitSession()
}
return c, nil
}
func newEmptyCursor() *Cursor {
return &Cursor{bc: driverlegacy.NewEmptyBatchCursor()}
}
// ID returns the ID of this cursor.
func (c *Cursor) ID() int64 { return c.bc.ID() }
// Next gets the next result from this cursor. Returns true if there were no errors and the next
// result is available for decoding.
func (c *Cursor) Next(ctx context.Context) bool {
if ctx == nil {
ctx = context.Background()
}
doc, err := c.batch.Next()
switch err {
case nil:
c.Current = bson.Raw(doc)
return true
case io.EOF: // Need to do a getMore
default:
c.err = err
return false
}
// call the Next method in a loop until at least one document is returned in the next batch or
// the context times out.
for {
// If we don't have a next batch
if !c.bc.Next(ctx) {
// Do we have an error? If so we return false.
c.err = c.bc.Err()
if c.err != nil {
return false
}
// Is the cursor ID zero?
if c.bc.ID() == 0 {
c.closeImplicitSession()
return false
}
// empty batch, but cursor is still valid, so continue.
continue
}
// close the implicit session if this was the last getMore
if c.bc.ID() == 0 {
c.closeImplicitSession()
}
c.batch = c.bc.Batch()
doc, err = c.batch.Next()
switch err {
case nil:
c.Current = bson.Raw(doc)
return true
case io.EOF: // Empty batch so we continue
default:
c.err = err
return false
}
}
}
// Decode will decode the current document into val. If val is nil or is a typed nil, an error will be returned.
func (c *Cursor) Decode(val interface{}) error {
return bson.UnmarshalWithRegistry(c.registry, c.Current, val)
}
// Err returns the current error.
func (c *Cursor) Err() error { return c.err }
// Close closes this cursor.
func (c *Cursor) Close(ctx context.Context) error {
defer c.closeImplicitSession()
return c.bc.Close(ctx)
}
// All iterates the cursor and decodes each document into results.
// The results parameter must be a pointer to a slice. The slice pointed to by results will be completely overwritten.
// If the cursor has been iterated, any previously iterated documents will not be included in results.
func (c *Cursor) All(ctx context.Context, results interface{}) error {
resultsVal := reflect.ValueOf(results)
if resultsVal.Kind() != reflect.Ptr {
return errors.New("results argument must be a pointer to a slice")
}
sliceVal := resultsVal.Elem()
elementType := sliceVal.Type().Elem()
var index int
var err error
batch := c.batch // exhaust the current batch before iterating the batch cursor
for {
sliceVal, index, err = c.addFromBatch(sliceVal, elementType, batch, index)
if err != nil {
return err
}
if !c.bc.Next(ctx) {
break
}
batch = c.bc.Batch()
}
if err = c.bc.Err(); err != nil {
return err
}
resultsVal.Elem().Set(sliceVal.Slice(0, index))
return nil
}
// addFromBatch adds all documents from batch to sliceVal starting at the given index. It returns the new slice value,
// the next empty index in the slice, and an error if one occurs.
func (c *Cursor) addFromBatch(sliceVal reflect.Value, elemType reflect.Type, batch *bsoncore.DocumentSequence,
index int) (reflect.Value, int, error) {
docs, err := batch.Documents()
if err != nil {
return sliceVal, index, err
}
for _, doc := range docs {
if sliceVal.Len() == index {
// slice is full
newElem := reflect.New(elemType)
sliceVal = reflect.Append(sliceVal, newElem.Elem())
sliceVal = sliceVal.Slice(0, sliceVal.Cap())
}
currElem := sliceVal.Index(index).Addr().Interface()
if err = bson.UnmarshalWithRegistry(c.registry, doc, currElem); err != nil {
return sliceVal, index, err
}
index++
}
return sliceVal, index, nil
}
func (c *Cursor) closeImplicitSession() {
if c.clientSession != nil && c.clientSession.SessionType == session.Implicit {
c.clientSession.EndSession()
}
}

365
vendor/go.mongodb.org/mongo-driver/mongo/database.go generated vendored Executable file
View File

@@ -0,0 +1,365 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"context"
"errors"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readconcern"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
"go.mongodb.org/mongo-driver/x/bsonx"
"go.mongodb.org/mongo-driver/x/mongo/driver"
"go.mongodb.org/mongo-driver/x/mongo/driver/description"
"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
"go.mongodb.org/mongo-driver/x/mongo/driver/session"
)
var (
defaultRunCmdOpts = []*options.RunCmdOptions{options.RunCmd().SetReadPreference(readpref.Primary())}
)
// Database performs operations on a given database.
type Database struct {
client *Client
name string
readConcern *readconcern.ReadConcern
writeConcern *writeconcern.WriteConcern
readPreference *readpref.ReadPref
readSelector description.ServerSelector
writeSelector description.ServerSelector
registry *bsoncodec.Registry
}
func newDatabase(client *Client, name string, opts ...*options.DatabaseOptions) *Database {
dbOpt := options.MergeDatabaseOptions(opts...)
rc := client.readConcern
if dbOpt.ReadConcern != nil {
rc = dbOpt.ReadConcern
}
rp := client.readPreference
if dbOpt.ReadPreference != nil {
rp = dbOpt.ReadPreference
}
wc := client.writeConcern
if dbOpt.WriteConcern != nil {
wc = dbOpt.WriteConcern
}
db := &Database{
client: client,
name: name,
readPreference: rp,
readConcern: rc,
writeConcern: wc,
registry: client.registry,
}
db.readSelector = description.CompositeSelector([]description.ServerSelector{
description.ReadPrefSelector(db.readPreference),
description.LatencySelector(db.client.localThreshold),
})
db.writeSelector = description.CompositeSelector([]description.ServerSelector{
description.WriteSelector(),
description.LatencySelector(db.client.localThreshold),
})
return db
}
// Client returns the Client the database was created from.
func (db *Database) Client() *Client {
return db.client
}
// Name returns the name of the database.
func (db *Database) Name() string {
return db.name
}
// Collection gets a handle for a given collection in the database.
func (db *Database) Collection(name string, opts ...*options.CollectionOptions) *Collection {
return newCollection(db, name, opts...)
}
// Aggregate runs an aggregation framework pipeline.
//
// See https://docs.mongodb.com/manual/aggregation/.
func (db *Database) Aggregate(ctx context.Context, pipeline interface{},
opts ...*options.AggregateOptions) (*Cursor, error) {
a := aggregateParams{
ctx: ctx,
pipeline: pipeline,
client: db.client,
registry: db.registry,
readConcern: db.readConcern,
writeConcern: db.writeConcern,
db: db.name,
readSelector: db.readSelector,
writeSelector: db.writeSelector,
readPreference: db.readPreference,
opts: opts,
}
return aggregate(a)
}
func (db *Database) processRunCommand(ctx context.Context, cmd interface{},
opts ...*options.RunCmdOptions) (*operation.Command, *session.Client, error) {
sess := sessionFromContext(ctx)
if sess == nil && db.client.topology.SessionPool != nil {
var err error
sess, err = session.NewClientSession(db.client.topology.SessionPool, db.client.id, session.Implicit)
if err != nil {
return nil, sess, err
}
}
err := db.client.validSession(sess)
if err != nil {
return nil, sess, err
}
ro := options.MergeRunCmdOptions(append(defaultRunCmdOpts, opts...)...)
if sess != nil && sess.TransactionRunning() && ro.ReadPreference != nil && ro.ReadPreference.Mode() != readpref.PrimaryMode {
return nil, sess, errors.New("read preference in a transaction must be primary")
}
runCmdDoc, err := transformBsoncoreDocument(db.registry, cmd)
if err != nil {
return nil, sess, err
}
readSelect := description.CompositeSelector([]description.ServerSelector{
description.ReadPrefSelector(ro.ReadPreference),
description.LatencySelector(db.client.localThreshold),
})
if sess != nil && sess.PinnedServer != nil {
readSelect = sess.PinnedServer
}
return operation.NewCommand(runCmdDoc).
Session(sess).CommandMonitor(db.client.monitor).
ServerSelector(readSelect).ClusterClock(db.client.clock).
Database(db.name).Deployment(db.client.topology).ReadConcern(db.readConcern), sess, nil
}
// RunCommand runs a command on the database. A user can supply a custom
// context to this method, or nil to default to context.Background().
func (db *Database) RunCommand(ctx context.Context, runCommand interface{}, opts ...*options.RunCmdOptions) *SingleResult {
if ctx == nil {
ctx = context.Background()
}
op, sess, err := db.processRunCommand(ctx, runCommand, opts...)
defer closeImplicitSession(sess)
if err != nil {
return &SingleResult{err: err}
}
err = op.Execute(ctx)
return &SingleResult{
err: replaceErrors(err),
rdr: bson.Raw(op.Result()),
reg: db.registry,
}
}
// RunCommandCursor runs a command on the database and returns a cursor over the resulting reader. A user can supply
// a custom context to this method, or nil to default to context.Background().
func (db *Database) RunCommandCursor(ctx context.Context, runCommand interface{}, opts ...*options.RunCmdOptions) (*Cursor, error) {
if ctx == nil {
ctx = context.Background()
}
op, sess, err := db.processRunCommand(ctx, runCommand, opts...)
if err != nil {
closeImplicitSession(sess)
return nil, err
}
bc, err := op.ResultCursor(driver.CursorOptions{})
if err != nil {
closeImplicitSession(sess)
return nil, replaceErrors(err)
}
cursor, err := newCursorWithSession(bc, db.registry, sess)
return cursor, replaceErrors(err)
}
// Drop drops this database from mongodb.
func (db *Database) Drop(ctx context.Context) error {
if ctx == nil {
ctx = context.Background()
}
sess := sessionFromContext(ctx)
if sess == nil && db.client.topology.SessionPool != nil {
sess, err := session.NewClientSession(db.client.topology.SessionPool, db.client.id, session.Implicit)
if err != nil {
return err
}
defer sess.EndSession()
}
err := db.client.validSession(sess)
if err != nil {
return err
}
wc := db.writeConcern
if sess.TransactionRunning() {
wc = nil
}
if !writeconcern.AckWrite(wc) {
sess = nil
}
selector := db.writeSelector
if sess != nil && sess.PinnedServer != nil {
selector = sess.PinnedServer
}
op := operation.NewDropDatabase().
Session(sess).WriteConcern(wc).CommandMonitor(db.client.monitor).
ServerSelector(selector).ClusterClock(db.client.clock).
Database(db.name).Deployment(db.client.topology)
err = op.Execute(ctx)
driverErr, ok := err.(driver.Error)
if err != nil && (!ok || !driverErr.NamespaceNotFound()) {
return replaceErrors(err)
}
return nil
}
// ListCollections returns a cursor over the collections in a database.
func (db *Database) ListCollections(ctx context.Context, filter interface{}, opts ...*options.ListCollectionsOptions) (*Cursor, error) {
if ctx == nil {
ctx = context.Background()
}
filterDoc, err := transformBsoncoreDocument(db.registry, filter)
if err != nil {
return nil, err
}
sess := sessionFromContext(ctx)
if sess == nil && db.client.topology.SessionPool != nil {
sess, err = session.NewClientSession(db.client.topology.SessionPool, db.client.id, session.Implicit)
if err != nil {
return nil, err
}
}
err = db.client.validSession(sess)
if err != nil {
closeImplicitSession(sess)
return nil, err
}
selector := db.readSelector
if sess != nil && sess.PinnedServer != nil {
selector = sess.PinnedServer
}
lco := options.MergeListCollectionsOptions(opts...)
op := operation.NewListCollections(filterDoc).
Session(sess).ReadPreference(db.readPreference).CommandMonitor(db.client.monitor).
ServerSelector(selector).ClusterClock(db.client.clock).
Database(db.name).Deployment(db.client.topology)
if lco.NameOnly != nil {
op = op.NameOnly(*lco.NameOnly)
}
err = op.Execute(ctx)
if err != nil {
closeImplicitSession(sess)
return nil, replaceErrors(err)
}
bc, err := op.Result(driver.CursorOptions{})
if err != nil {
closeImplicitSession(sess)
return nil, replaceErrors(err)
}
cursor, err := newCursorWithSession(bc, db.registry, sess)
return cursor, replaceErrors(err)
}
// ListCollectionNames returns a slice containing the names of all of the collections on the server.
func (db *Database) ListCollectionNames(ctx context.Context, filter interface{}, opts ...*options.ListCollectionsOptions) ([]string, error) {
opts = append(opts, options.ListCollections().SetNameOnly(true))
res, err := db.ListCollections(ctx, filter, opts...)
if err != nil {
return nil, err
}
names := make([]string, 0)
for res.Next(ctx) {
next := &bsonx.Doc{}
err = res.Decode(next)
if err != nil {
return nil, err
}
elem, err := next.LookupErr("name")
if err != nil {
return nil, err
}
if elem.Type() != bson.TypeString {
return nil, fmt.Errorf("incorrect type for 'name'. got %v. want %v", elem.Type(), bson.TypeString)
}
elemName := elem.StringValue()
names = append(names, elemName)
}
return names, nil
}
// ReadConcern returns the read concern of this database.
func (db *Database) ReadConcern() *readconcern.ReadConcern {
return db.readConcern
}
// ReadPreference returns the read preference of this database.
func (db *Database) ReadPreference() *readpref.ReadPref {
return db.readPreference
}
// WriteConcern returns the write concern of this database.
func (db *Database) WriteConcern() *writeconcern.WriteConcern {
return db.writeConcern
}
// Watch returns a change stream cursor used to receive information of changes to the database. This method is preferred
// to running a raw aggregation with a $changeStream stage because it supports resumability in the case of some errors.
// The database must have read concern majority or no read concern for a change stream to be created successfully.
func (db *Database) Watch(ctx context.Context, pipeline interface{},
opts ...*options.ChangeStreamOptions) (*ChangeStream, error) {
csConfig := changeStreamConfig{
readConcern: db.readConcern,
readPreference: db.readPreference,
client: db.client,
registry: db.registry,
streamType: DatabaseStream,
databaseName: db.Name(),
}
return newChangeStream(ctx, csConfig, pipeline, opts...)
}

81
vendor/go.mongodb.org/mongo-driver/mongo/doc.go generated vendored Executable file
View File

@@ -0,0 +1,81 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// NOTE: This documentation should be kept in line with the Example* test functions.
// Package mongo provides a MongoDB Driver API for Go.
//
// Basic usage of the driver starts with creating a Client from a connection
// string. To do so, call the NewClient and Connect functions:
//
// client, err := NewClient(options.Client().ApplyURI("mongodb://foo:bar@localhost:27017"))
// if err != nil { return err }
// ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
// defer cancel()
// err = client.Connect(ctx)
// if err != nil { return err }
//
// This will create a new client and start monitoring the MongoDB server on localhost.
// The Database and Collection types can be used to access the database:
//
// collection := client.Database("baz").Collection("qux")
//
// A Collection can be used to query the database or insert documents:
//
// res, err := collection.InsertOne(context.Background(), bson.M{"hello": "world"})
// if err != nil { return err }
// id := res.InsertedID
//
// Several methods return a cursor, which can be used like this:
//
// cur, err := collection.Find(context.Background(), bson.D{})
// if err != nil { log.Fatal(err) }
// defer cur.Close(context.Background())
// for cur.Next(context.Background()) {
// // To decode into a struct, use cursor.Decode()
// result := struct{
// Foo string
// Bar int32
// }{}
// err := cur.Decode(&result)
// if err != nil { log.Fatal(err) }
// // do something with result...
//
// // To get the raw bson bytes use cursor.Current
// raw := cur.Current
// // do something with raw...
// }
// if err := cur.Err(); err != nil {
// return err
// }
//
// Methods that only return a single document will return a *SingleResult, which works
// like a *sql.Row:
//
// result := struct{
// Foo string
// Bar int32
// }{}
// filter := bson.D{{"hello", "world"}}
// err := collection.FindOne(context.Background(), filter).Decode(&result)
// if err != nil { return err }
// // do something with result...
//
// All Client, Collection, and Database methods that take parameters of type interface{}
// will return ErrNilDocument if nil is passed in for an interface{}.
//
// Additional examples can be found under the examples directory in the driver's repository and
// on the MongoDB website.
//
// Potential DNS Issues
//
// Building with Go 1.11+ and using connection strings with the "mongodb+srv"[1] scheme is
// incompatible with some DNS servers in the wild due to the change introduced in
// https://github.com/golang/go/issues/10622. If you receive an error with the message "cannot
// unmarshal DNS message" while running an operation, we suggest you use a different DNS server.
//
// [1] See https://docs.mongodb.com/manual/reference/connection-string/#dns-seedlist-connection-format
package mongo

258
vendor/go.mongodb.org/mongo-driver/mongo/errors.go generated vendored Executable file
View File

@@ -0,0 +1,258 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"bytes"
"errors"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/x/mongo/driver"
"go.mongodb.org/mongo-driver/x/mongo/driver/topology"
"go.mongodb.org/mongo-driver/x/mongo/driverlegacy"
"go.mongodb.org/mongo-driver/x/network/command"
"go.mongodb.org/mongo-driver/x/network/result"
)
// ErrUnacknowledgedWrite is returned from functions that have an unacknowledged
// write concern.
var ErrUnacknowledgedWrite = errors.New("unacknowledged write")
// ErrClientDisconnected is returned when a user attempts to call a method on a
// disconnected client
var ErrClientDisconnected = errors.New("client is disconnected")
// ErrNilDocument is returned when a user attempts to pass a nil document or filter
// to a function where the field is required.
var ErrNilDocument = errors.New("document is nil")
// ErrEmptySlice is returned when a user attempts to pass an empty slice as input
// to a function wehere the field is required.
var ErrEmptySlice = errors.New("must provide at least one element in input slice")
func replaceErrors(err error) error {
if err == topology.ErrTopologyClosed {
return ErrClientDisconnected
}
if ce, ok := err.(command.Error); ok {
return CommandError{Code: ce.Code, Message: ce.Message, Labels: ce.Labels, Name: ce.Name}
}
if de, ok := err.(driver.Error); ok {
return CommandError{Code: de.Code, Message: de.Message, Labels: de.Labels, Name: de.Name}
}
if conv, ok := err.(driverlegacy.BulkWriteException); ok {
return BulkWriteException{
WriteConcernError: convertWriteConcernError(conv.WriteConcernError),
WriteErrors: convertBulkWriteErrors(conv.WriteErrors),
}
}
return err
}
// CommandError represents an error in execution of a command against the database.
type CommandError struct {
Code int32
Message string
Labels []string
Name string
}
// Error implements the error interface.
func (e CommandError) Error() string {
if e.Name != "" {
return fmt.Sprintf("(%v) %v", e.Name, e.Message)
}
return e.Message
}
// HasErrorLabel returns true if the error contains the specified label.
func (e CommandError) HasErrorLabel(label string) bool {
if e.Labels != nil {
for _, l := range e.Labels {
if l == label {
return true
}
}
}
return false
}
// WriteError is a non-write concern failure that occurred as a result of a write
// operation.
type WriteError struct {
Index int
Code int
Message string
}
func (we WriteError) Error() string { return we.Message }
// WriteErrors is a group of non-write concern failures that occurred as a result
// of a write operation.
type WriteErrors []WriteError
func (we WriteErrors) Error() string {
var buf bytes.Buffer
fmt.Fprint(&buf, "write errors: [")
for idx, err := range we {
if idx != 0 {
fmt.Fprintf(&buf, ", ")
}
fmt.Fprintf(&buf, "{%s}", err)
}
fmt.Fprint(&buf, "]")
return buf.String()
}
func writeErrorsFromResult(rwes []result.WriteError) WriteErrors {
wes := make(WriteErrors, 0, len(rwes))
for _, err := range rwes {
wes = append(wes, WriteError{Index: err.Index, Code: err.Code, Message: err.ErrMsg})
}
return wes
}
func writeErrorsFromDriverWriteErrors(errs driver.WriteErrors) WriteErrors {
wes := make(WriteErrors, 0, len(errs))
for _, err := range errs {
wes = append(wes, WriteError{Index: int(err.Index), Code: int(err.Code), Message: err.Message})
}
return wes
}
// WriteConcernError is a write concern failure that occurred as a result of a
// write operation.
type WriteConcernError struct {
Name string
Code int
Message string
Details bson.Raw
}
func (wce WriteConcernError) Error() string {
if wce.Name != "" {
return fmt.Sprintf("(%v) %v", wce.Name, wce.Message)
}
return wce.Message
}
// WriteException is an error for a non-bulk write operation.
type WriteException struct {
WriteConcernError *WriteConcernError
WriteErrors WriteErrors
}
func (mwe WriteException) Error() string {
var buf bytes.Buffer
fmt.Fprint(&buf, "multiple write errors: [")
fmt.Fprintf(&buf, "{%s}, ", mwe.WriteErrors)
fmt.Fprintf(&buf, "{%s}]", mwe.WriteConcernError)
return buf.String()
}
func convertBulkWriteErrors(errors []driverlegacy.BulkWriteError) []BulkWriteError {
bwErrors := make([]BulkWriteError, 0, len(errors))
for _, err := range errors {
bwErrors = append(bwErrors, BulkWriteError{
WriteError{
Index: err.Index,
Code: err.Code,
Message: err.ErrMsg,
},
dispatchToMongoModel(err.Model),
})
}
return bwErrors
}
func convertWriteConcernError(wce *result.WriteConcernError) *WriteConcernError {
if wce == nil {
return nil
}
return &WriteConcernError{Name: wce.Name, Code: wce.Code, Message: wce.ErrMsg, Details: wce.ErrInfo}
}
func convertDriverWriteConcernError(wce *driver.WriteConcernError) *WriteConcernError {
if wce == nil {
return nil
}
return &WriteConcernError{Code: int(wce.Code), Message: wce.Message, Details: bson.Raw(wce.Details)}
}
// BulkWriteError is an error for one operation in a bulk write.
type BulkWriteError struct {
WriteError
Request WriteModel
}
func (bwe BulkWriteError) Error() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "{%s}", bwe.WriteError)
return buf.String()
}
// BulkWriteException is an error for a bulk write operation.
type BulkWriteException struct {
WriteConcernError *WriteConcernError
WriteErrors []BulkWriteError
}
func (bwe BulkWriteException) Error() string {
var buf bytes.Buffer
fmt.Fprint(&buf, "bulk write error: [")
fmt.Fprintf(&buf, "{%s}, ", bwe.WriteErrors)
fmt.Fprintf(&buf, "{%s}]", bwe.WriteConcernError)
return buf.String()
}
// returnResult is used to determine if a function calling processWriteError should return
// the result or return nil. Since the processWriteError function is used by many different
// methods, both *One and *Many, we need a way to differentiate if the method should return
// the result and the error.
type returnResult int
const (
rrNone returnResult = 1 << iota // None means do not return the result ever.
rrOne // One means return the result if this was called by a *One method.
rrMany // Many means return the result is this was called by a *Many method.
rrAll returnResult = rrOne | rrMany // All means always return the result.
)
// processWriteError handles processing the result of a write operation. If the retrunResult matches
// the calling method's type, it should return the result object in addition to the error.
// This function will wrap the errors from other packages and return them as errors from this package.
//
// WriteConcernError will be returned over WriteErrors if both are present.
func processWriteError(wce *result.WriteConcernError, wes []result.WriteError, err error) (returnResult, error) {
switch {
case err == command.ErrUnacknowledgedWrite, err == driver.ErrUnacknowledgedWrite:
return rrAll, ErrUnacknowledgedWrite
case err != nil:
switch tt := err.(type) {
case driver.WriteCommandError:
return rrMany, WriteException{
WriteConcernError: convertDriverWriteConcernError(tt.WriteConcernError),
WriteErrors: writeErrorsFromDriverWriteErrors(tt.WriteErrors),
}
default:
return rrNone, replaceErrors(err)
}
case wce != nil || len(wes) > 0:
return rrMany, WriteException{
WriteConcernError: convertWriteConcernError(wce),
WriteErrors: writeErrorsFromResult(wes),
}
default:
return rrAll, nil
}
}

View File

@@ -0,0 +1,134 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"go.mongodb.org/mongo-driver/bson"
)
// IndexOptionsBuilder constructs a BSON document for index options
type IndexOptionsBuilder struct {
document bson.D
}
// NewIndexOptionsBuilder creates a new instance of IndexOptionsBuilder
func NewIndexOptionsBuilder() *IndexOptionsBuilder {
return &IndexOptionsBuilder{}
}
// Background sets the background option
func (iob *IndexOptionsBuilder) Background(background bool) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"background", background})
return iob
}
// ExpireAfterSeconds sets the expireAfterSeconds option
func (iob *IndexOptionsBuilder) ExpireAfterSeconds(expireAfterSeconds int32) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"expireAfterSeconds", expireAfterSeconds})
return iob
}
// Name sets the name option
func (iob *IndexOptionsBuilder) Name(name string) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"name", name})
return iob
}
// Sparse sets the sparse option
func (iob *IndexOptionsBuilder) Sparse(sparse bool) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"sparse", sparse})
return iob
}
// StorageEngine sets the storageEngine option
func (iob *IndexOptionsBuilder) StorageEngine(storageEngine interface{}) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"storageEngine", storageEngine})
return iob
}
// Unique sets the unique option
func (iob *IndexOptionsBuilder) Unique(unique bool) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"unique", unique})
return iob
}
// Version sets the version option
func (iob *IndexOptionsBuilder) Version(version int32) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"v", version})
return iob
}
// DefaultLanguage sets the defaultLanguage option
func (iob *IndexOptionsBuilder) DefaultLanguage(defaultLanguage string) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"default_language", defaultLanguage})
return iob
}
// LanguageOverride sets the languageOverride option
func (iob *IndexOptionsBuilder) LanguageOverride(languageOverride string) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"language_override", languageOverride})
return iob
}
// TextVersion sets the textVersion option
func (iob *IndexOptionsBuilder) TextVersion(textVersion int32) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"textIndexVersion", textVersion})
return iob
}
// Weights sets the weights option
func (iob *IndexOptionsBuilder) Weights(weights interface{}) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"weights", weights})
return iob
}
// SphereVersion sets the sphereVersion option
func (iob *IndexOptionsBuilder) SphereVersion(sphereVersion int32) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"2dsphereIndexVersion", sphereVersion})
return iob
}
// Bits sets the bits option
func (iob *IndexOptionsBuilder) Bits(bits int32) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"bits", bits})
return iob
}
// Max sets the max option
func (iob *IndexOptionsBuilder) Max(max float64) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"max", max})
return iob
}
// Min sets the min option
func (iob *IndexOptionsBuilder) Min(min float64) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"min", min})
return iob
}
// BucketSize sets the bucketSize option
func (iob *IndexOptionsBuilder) BucketSize(bucketSize int32) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"bucketSize", bucketSize})
return iob
}
// PartialFilterExpression sets the partialFilterExpression option
func (iob *IndexOptionsBuilder) PartialFilterExpression(partialFilterExpression interface{}) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"partialFilterExpression", partialFilterExpression})
return iob
}
// Collation sets the collation option
func (iob *IndexOptionsBuilder) Collation(collation interface{}) *IndexOptionsBuilder {
iob.document = append(iob.document, bson.E{"collation", collation})
return iob
}
// Build returns the BSON document from the builder
func (iob *IndexOptionsBuilder) Build() bson.D {
return iob.document
}

423
vendor/go.mongodb.org/mongo-driver/mongo/index_view.go generated vendored Executable file
View File

@@ -0,0 +1,423 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"bytes"
"context"
"errors"
"fmt"
"strconv"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
"go.mongodb.org/mongo-driver/x/mongo/driver"
"go.mongodb.org/mongo-driver/x/mongo/driver/description"
"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
"go.mongodb.org/mongo-driver/x/mongo/driver/session"
)
// ErrInvalidIndexValue indicates that the index Keys document has a value that isn't either a number or a string.
var ErrInvalidIndexValue = errors.New("invalid index value")
// ErrNonStringIndexName indicates that the index name specified in the options is not a string.
var ErrNonStringIndexName = errors.New("index name must be a string")
// ErrMultipleIndexDrop indicates that multiple indexes would be dropped from a call to IndexView.DropOne.
var ErrMultipleIndexDrop = errors.New("multiple indexes would be dropped")
// IndexView is used to create, drop, and list indexes on a given collection.
type IndexView struct {
coll *Collection
}
// IndexModel contains information about an index.
type IndexModel struct {
Keys interface{}
Options *options.IndexOptions
}
func isNamespaceNotFoundError(err error) bool {
if de, ok := err.(driver.Error); ok {
return de.Code == 26
}
return false
}
// List returns a cursor iterating over all the indexes in the collection.
func (iv IndexView) List(ctx context.Context, opts ...*options.ListIndexesOptions) (*Cursor, error) {
if ctx == nil {
ctx = context.Background()
}
sess := sessionFromContext(ctx)
if sess == nil && iv.coll.client.topology.SessionPool != nil {
var err error
sess, err = session.NewClientSession(iv.coll.client.topology.SessionPool, iv.coll.client.id, session.Implicit)
if err != nil {
return nil, err
}
}
err := iv.coll.client.validSession(sess)
if err != nil {
closeImplicitSession(sess)
return nil, err
}
readSelector := description.CompositeSelector([]description.ServerSelector{
description.ReadPrefSelector(readpref.Primary()),
description.LatencySelector(iv.coll.client.localThreshold),
})
op := operation.NewListIndexes().
Session(sess).CommandMonitor(iv.coll.client.monitor).
ServerSelector(readSelector).ClusterClock(iv.coll.client.clock).
Database(iv.coll.db.name).Collection(iv.coll.name).
Deployment(iv.coll.client.topology)
var cursorOpts driver.CursorOptions
lio := options.MergeListIndexesOptions(opts...)
if lio.BatchSize != nil {
op = op.BatchSize(*lio.BatchSize)
cursorOpts.BatchSize = *lio.BatchSize
}
if lio.MaxTime != nil {
op = op.MaxTimeMS(int64(*lio.MaxTime / time.Millisecond))
}
err = op.Execute(ctx)
if err != nil {
// for namespaceNotFound errors, return an empty cursor and do not throw an error
closeImplicitSession(sess)
if isNamespaceNotFoundError(err) {
return newEmptyCursor(), nil
}
return nil, replaceErrors(err)
}
bc, err := op.Result(cursorOpts)
if err != nil {
closeImplicitSession(sess)
return nil, replaceErrors(err)
}
cursor, err := newCursorWithSession(bc, iv.coll.registry, sess)
return cursor, replaceErrors(err)
}
// CreateOne creates a single index in the collection specified by the model.
func (iv IndexView) CreateOne(ctx context.Context, model IndexModel, opts ...*options.CreateIndexesOptions) (string, error) {
names, err := iv.CreateMany(ctx, []IndexModel{model}, opts...)
if err != nil {
return "", err
}
return names[0], nil
}
// CreateMany creates multiple indexes in the collection specified by the models. The names of the
// created indexes are returned.
func (iv IndexView) CreateMany(ctx context.Context, models []IndexModel, opts ...*options.CreateIndexesOptions) ([]string, error) {
names := make([]string, 0, len(models))
var indexes bsoncore.Document
aidx, indexes := bsoncore.AppendArrayStart(indexes)
for i, model := range models {
if model.Keys == nil {
return nil, fmt.Errorf("index model keys cannot be nil")
}
name, err := getOrGenerateIndexName(iv.coll.registry, model)
if err != nil {
return nil, err
}
names = append(names, name)
keys, err := transformBsoncoreDocument(iv.coll.registry, model.Keys)
if err != nil {
return nil, err
}
var iidx int32
iidx, indexes = bsoncore.AppendDocumentElementStart(indexes, strconv.Itoa(i))
indexes = bsoncore.AppendDocumentElement(indexes, "key", keys)
if model.Options == nil {
model.Options = options.Index()
}
model.Options.SetName(name)
optsDoc, err := iv.createOptionsDoc(model.Options)
if err != nil {
return nil, err
}
indexes = bsoncore.AppendDocument(indexes, optsDoc)
indexes, err = bsoncore.AppendDocumentEnd(indexes, iidx)
if err != nil {
return nil, err
}
}
indexes, err := bsoncore.AppendArrayEnd(indexes, aidx)
if err != nil {
return nil, err
}
sess := sessionFromContext(ctx)
if sess == nil && iv.coll.client.topology.SessionPool != nil {
sess, err = session.NewClientSession(iv.coll.client.topology.SessionPool, iv.coll.client.id, session.Implicit)
if err != nil {
return nil, err
}
defer sess.EndSession()
}
err = iv.coll.client.validSession(sess)
if err != nil {
return nil, err
}
selector := iv.coll.writeSelector
if sess != nil && sess.PinnedServer != nil {
selector = sess.PinnedServer
}
option := options.MergeCreateIndexesOptions(opts...)
op := operation.NewCreateIndexes(indexes).
Session(sess).ClusterClock(iv.coll.client.clock).
Database(iv.coll.db.name).Collection(iv.coll.name).CommandMonitor(iv.coll.client.monitor).
Deployment(iv.coll.client.topology).ServerSelector(selector)
if option.MaxTime != nil {
op.MaxTimeMS(int64(*option.MaxTime / time.Millisecond))
}
err = op.Execute(ctx)
if err != nil {
return nil, err
}
return names, nil
}
func (iv IndexView) createOptionsDoc(opts *options.IndexOptions) (bsoncore.Document, error) {
optsDoc := bsoncore.Document{}
if opts.Background != nil {
optsDoc = bsoncore.AppendBooleanElement(optsDoc, "background", *opts.Background)
}
if opts.ExpireAfterSeconds != nil {
optsDoc = bsoncore.AppendInt32Element(optsDoc, "expireAfterSeconds", *opts.ExpireAfterSeconds)
}
if opts.Name != nil {
optsDoc = bsoncore.AppendStringElement(optsDoc, "name", *opts.Name)
}
if opts.Sparse != nil {
optsDoc = bsoncore.AppendBooleanElement(optsDoc, "sparse", *opts.Sparse)
}
if opts.StorageEngine != nil {
doc, err := transformBsoncoreDocument(iv.coll.registry, opts.StorageEngine)
if err != nil {
return nil, err
}
optsDoc = bsoncore.AppendDocumentElement(optsDoc, "storageEngine", doc)
}
if opts.Unique != nil {
optsDoc = bsoncore.AppendBooleanElement(optsDoc, "unique", *opts.Unique)
}
if opts.Version != nil {
optsDoc = bsoncore.AppendInt32Element(optsDoc, "v", *opts.Version)
}
if opts.DefaultLanguage != nil {
optsDoc = bsoncore.AppendStringElement(optsDoc, "default_language", *opts.DefaultLanguage)
}
if opts.LanguageOverride != nil {
optsDoc = bsoncore.AppendStringElement(optsDoc, "language_override", *opts.LanguageOverride)
}
if opts.TextVersion != nil {
optsDoc = bsoncore.AppendInt32Element(optsDoc, "textIndexVersion", *opts.TextVersion)
}
if opts.Weights != nil {
doc, err := transformBsoncoreDocument(iv.coll.registry, opts.Weights)
if err != nil {
return nil, err
}
optsDoc = bsoncore.AppendDocumentElement(optsDoc, "weights", doc)
}
if opts.SphereVersion != nil {
optsDoc = bsoncore.AppendInt32Element(optsDoc, "2dsphereIndexVersion", *opts.SphereVersion)
}
if opts.Bits != nil {
optsDoc = bsoncore.AppendInt32Element(optsDoc, "bits", *opts.Bits)
}
if opts.Max != nil {
optsDoc = bsoncore.AppendDoubleElement(optsDoc, "max", *opts.Max)
}
if opts.Min != nil {
optsDoc = bsoncore.AppendDoubleElement(optsDoc, "min", *opts.Min)
}
if opts.BucketSize != nil {
optsDoc = bsoncore.AppendInt32Element(optsDoc, "bucketSize", *opts.BucketSize)
}
if opts.PartialFilterExpression != nil {
doc, err := transformBsoncoreDocument(iv.coll.registry, opts.PartialFilterExpression)
if err != nil {
return nil, err
}
optsDoc = bsoncore.AppendDocumentElement(optsDoc, "partialFilterExpression", doc)
}
if opts.Collation != nil {
optsDoc = bsoncore.AppendDocumentElement(optsDoc, "collation", bsoncore.Document(opts.Collation.ToDocument()))
}
if opts.WildcardProjection != nil {
doc, err := transformBsoncoreDocument(iv.coll.registry, opts.WildcardProjection)
if err != nil {
return nil, err
}
optsDoc = bsoncore.AppendDocumentElement(optsDoc, "wildcardProjection", doc)
}
return optsDoc, nil
}
func (iv IndexView) drop(ctx context.Context, name string, opts ...*options.DropIndexesOptions) (bson.Raw, error) {
if ctx == nil {
ctx = context.Background()
}
sess := sessionFromContext(ctx)
if sess == nil && iv.coll.client.topology.SessionPool != nil {
var err error
sess, err = session.NewClientSession(iv.coll.client.topology.SessionPool, iv.coll.client.id, session.Implicit)
if err != nil {
return nil, err
}
defer sess.EndSession()
}
err := iv.coll.client.validSession(sess)
if err != nil {
return nil, err
}
wc := iv.coll.writeConcern
if sess.TransactionRunning() {
wc = nil
}
if !writeconcern.AckWrite(wc) {
sess = nil
}
selector := iv.coll.writeSelector
if sess != nil && sess.PinnedServer != nil {
selector = sess.PinnedServer
}
dio := options.MergeDropIndexesOptions(opts...)
op := operation.NewDropIndexes(name).
Session(sess).WriteConcern(wc).CommandMonitor(iv.coll.client.monitor).
ServerSelector(selector).ClusterClock(iv.coll.client.clock).
Database(iv.coll.db.name).Collection(iv.coll.name).
Deployment(iv.coll.client.topology)
if dio.MaxTime != nil {
op.MaxTimeMS(int64(*dio.MaxTime / time.Millisecond))
}
err = op.Execute(ctx)
if err != nil {
return nil, replaceErrors(err)
}
// TODO: it's weird to return a bson.Raw here because we have to convert the result back to BSON
ridx, res := bsoncore.AppendDocumentStart(nil)
res = bsoncore.AppendInt32Element(res, "nIndexesWas", op.Result().NIndexesWas)
res, _ = bsoncore.AppendDocumentEnd(res, ridx)
return res, nil
}
// DropOne drops the index with the given name from the collection.
func (iv IndexView) DropOne(ctx context.Context, name string, opts ...*options.DropIndexesOptions) (bson.Raw, error) {
if name == "*" {
return nil, ErrMultipleIndexDrop
}
return iv.drop(ctx, name, opts...)
}
// DropAll drops all indexes in the collection.
func (iv IndexView) DropAll(ctx context.Context, opts ...*options.DropIndexesOptions) (bson.Raw, error) {
return iv.drop(ctx, "*", opts...)
}
func getOrGenerateIndexName(registry *bsoncodec.Registry, model IndexModel) (string, error) {
if model.Options != nil && model.Options.Name != nil {
return *model.Options.Name, nil
}
name := bytes.NewBufferString("")
first := true
keys, err := transformDocument(registry, model.Keys)
if err != nil {
return "", err
}
for _, elem := range keys {
if !first {
_, err := name.WriteRune('_')
if err != nil {
return "", err
}
}
_, err := name.WriteString(elem.Key)
if err != nil {
return "", err
}
_, err = name.WriteRune('_')
if err != nil {
return "", err
}
var value string
switch elem.Value.Type() {
case bsontype.Int32:
value = fmt.Sprintf("%d", elem.Value.Int32())
case bsontype.Int64:
value = fmt.Sprintf("%d", elem.Value.Int64())
case bsontype.String:
value = elem.Value.StringValue()
default:
return "", ErrInvalidIndexValue
}
_, err = name.WriteString(value)
if err != nil {
return "", err
}
first = false
}
return name.String(), nil
}

375
vendor/go.mongodb.org/mongo-driver/mongo/mongo.go generated vendored Executable file
View File

@@ -0,0 +1,375 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo // import "go.mongodb.org/mongo-driver/mongo"
import (
"context"
"errors"
"fmt"
"net"
"reflect"
"strconv"
"strings"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/x/bsonx"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// Dialer is used to make network connections.
type Dialer interface {
DialContext(ctx context.Context, network, address string) (net.Conn, error)
}
// BSONAppender is an interface implemented by types that can marshal a
// provided type into BSON bytes and append those bytes to the provided []byte.
// The AppendBSON can return a non-nil error and non-nil []byte. The AppendBSON
// method may also write incomplete BSON to the []byte.
type BSONAppender interface {
AppendBSON([]byte, interface{}) ([]byte, error)
}
// BSONAppenderFunc is an adapter function that allows any function that
// satisfies the AppendBSON method signature to be used where a BSONAppender is
// used.
type BSONAppenderFunc func([]byte, interface{}) ([]byte, error)
// AppendBSON implements the BSONAppender interface
func (baf BSONAppenderFunc) AppendBSON(dst []byte, val interface{}) ([]byte, error) {
return baf(dst, val)
}
// MarshalError is returned when attempting to transform a value into a document
// results in an error.
type MarshalError struct {
Value interface{}
Err error
}
// Error implements the error interface.
func (me MarshalError) Error() string {
return fmt.Sprintf("cannot transform type %s to a BSON Document: %v", reflect.TypeOf(me.Value), me.Err)
}
// Pipeline is a type that makes creating aggregation pipelines easier. It is a
// helper and is intended for serializing to BSON.
//
// Example usage:
//
// mongo.Pipeline{
// {{"$group", bson.D{{"_id", "$state"}, {"totalPop", bson.D{{"$sum", "$pop"}}}}}},
// {{"$match", bson.D{{"totalPop", bson.D{{"$gte", 10*1000*1000}}}}}},
// }
//
type Pipeline []bson.D
// transformAndEnsureID is a hack that makes it easy to get a RawValue as the _id value. This will
// be removed when we switch from using bsonx to bsoncore for the driver package.
func transformAndEnsureID(registry *bsoncodec.Registry, val interface{}) (bsonx.Doc, interface{}, error) {
// TODO: performance is going to be pretty bad for bsonx.Doc here since we turn it into a []byte
// only to turn it back into a bsonx.Doc. We can fix this post beta1 when we refactor the driver
// package to use bsoncore.Document instead of bsonx.Doc.
if registry == nil {
registry = bson.NewRegistryBuilder().Build()
}
switch tt := val.(type) {
case nil:
return nil, nil, ErrNilDocument
case bsonx.Doc:
val = tt.Copy()
case []byte:
// Slight optimization so we'll just use MarshalBSON and not go through the codec machinery.
val = bson.Raw(tt)
}
// TODO(skriptble): Use a pool of these instead.
buf := make([]byte, 0, 256)
b, err := bson.MarshalAppendWithRegistry(registry, buf, val)
if err != nil {
return nil, nil, MarshalError{Value: val, Err: err}
}
d, err := bsonx.ReadDoc(b)
if err != nil {
return nil, nil, err
}
var id interface{}
idx := d.IndexOf("_id")
var idElem bsonx.Elem
switch idx {
case -1:
idElem = bsonx.Elem{"_id", bsonx.ObjectID(primitive.NewObjectID())}
d = append(d, bsonx.Elem{})
copy(d[1:], d)
d[0] = idElem
default:
idElem = d[idx]
copy(d[1:idx+1], d[0:idx])
d[0] = idElem
}
idBuf := make([]byte, 0, 256)
t, data, err := idElem.Value.MarshalAppendBSONValue(idBuf[:0])
if err != nil {
return nil, nil, err
}
err = bson.RawValue{Type: t, Value: data}.UnmarshalWithRegistry(registry, &id)
if err != nil {
return nil, nil, err
}
return d, id, nil
}
// transformAndEnsureIDv2 is a hack that makes it easy to get a RawValue as the _id value. This will
// be removed when we switch from using bsonx to bsoncore for the driver package.
func transformAndEnsureIDv2(registry *bsoncodec.Registry, val interface{}) (bsoncore.Document, interface{}, error) {
if registry == nil {
registry = bson.NewRegistryBuilder().Build()
}
switch tt := val.(type) {
case nil:
return nil, nil, ErrNilDocument
case bsonx.Doc:
val = tt.Copy()
case []byte:
// Slight optimization so we'll just use MarshalBSON and not go through the codec machinery.
val = bson.Raw(tt)
}
// TODO(skriptble): Use a pool of these instead.
doc := make(bsoncore.Document, 0, 256)
doc, err := bson.MarshalAppendWithRegistry(registry, doc, val)
if err != nil {
return nil, nil, MarshalError{Value: val, Err: err}
}
var id interface{}
value := doc.Lookup("_id")
switch value.Type {
case bsontype.Type(0):
value = bsoncore.Value{Type: bsontype.ObjectID, Data: bsoncore.AppendObjectID(nil, primitive.NewObjectID())}
olddoc := doc
doc = make(bsoncore.Document, 0, len(olddoc)+17) // type byte + _id + null byte + object ID
_, doc = bsoncore.ReserveLength(doc)
doc = bsoncore.AppendValueElement(doc, "_id", value)
doc = append(doc, olddoc[4:]...) // remove the length
doc = bsoncore.UpdateLength(doc, 0, int32(len(doc)))
default:
// We copy the bytes here to ensure that any bytes returned to the user aren't modified
// later.
buf := make([]byte, len(value.Data))
copy(buf, value.Data)
value.Data = buf
}
err = bson.RawValue{Type: value.Type, Value: value.Data}.UnmarshalWithRegistry(registry, &id)
if err != nil {
return nil, nil, err
}
return doc, id, nil
}
func transformDocument(registry *bsoncodec.Registry, val interface{}) (bsonx.Doc, error) {
if doc, ok := val.(bsonx.Doc); ok {
return doc.Copy(), nil
}
b, err := transformBsoncoreDocument(registry, val)
if err != nil {
return nil, err
}
return bsonx.ReadDoc(b)
}
func transformBsoncoreDocument(registry *bsoncodec.Registry, val interface{}) (bsoncore.Document, error) {
if registry == nil {
registry = bson.DefaultRegistry
}
if val == nil {
return nil, ErrNilDocument
}
if bs, ok := val.([]byte); ok {
// Slight optimization so we'll just use MarshalBSON and not go through the codec machinery.
val = bson.Raw(bs)
}
// TODO(skriptble): Use a pool of these instead.
buf := make([]byte, 0, 256)
b, err := bson.MarshalAppendWithRegistry(registry, buf[:0], val)
if err != nil {
return nil, MarshalError{Value: val, Err: err}
}
return b, nil
}
func ensureID(d bsonx.Doc) (bsonx.Doc, interface{}) {
var id interface{}
elem, err := d.LookupElementErr("_id")
switch err.(type) {
case nil:
id = elem
default:
oid := primitive.NewObjectID()
d = append(d, bsonx.Elem{"_id", bsonx.ObjectID(oid)})
id = oid
}
return d, id
}
func ensureDollarKey(doc bsonx.Doc) error {
if len(doc) == 0 {
return errors.New("update document must have at least one element")
}
if !strings.HasPrefix(doc[0].Key, "$") {
return errors.New("update document must contain key beginning with '$'")
}
return nil
}
func ensureDollarKeyv2(doc bsoncore.Document) error {
firstElem, err := doc.IndexErr(0)
if err != nil {
return errors.New("update document must have at least one element")
}
if !strings.HasPrefix(firstElem.Key(), "$") {
return errors.New("update document must contain key beginning with '$'")
}
return nil
}
func transformAggregatePipeline(registry *bsoncodec.Registry, pipeline interface{}) (bsonx.Arr, error) {
pipelineArr := bsonx.Arr{}
switch t := pipeline.(type) {
case bsoncodec.ValueMarshaler:
btype, val, err := t.MarshalBSONValue()
if err != nil {
return nil, err
}
if btype != bsontype.Array {
return nil, fmt.Errorf("ValueMarshaler returned a %v, but was expecting %v", btype, bsontype.Array)
}
err = pipelineArr.UnmarshalBSONValue(btype, val)
if err != nil {
return nil, err
}
default:
val := reflect.ValueOf(t)
if !val.IsValid() || (val.Kind() != reflect.Slice && val.Kind() != reflect.Array) {
return nil, fmt.Errorf("can only transform slices and arrays into aggregation pipelines, but got %v", val.Kind())
}
for idx := 0; idx < val.Len(); idx++ {
elem, err := transformDocument(registry, val.Index(idx).Interface())
if err != nil {
return nil, err
}
pipelineArr = append(pipelineArr, bsonx.Document(elem))
}
}
return pipelineArr, nil
}
func transformAggregatePipelinev2(registry *bsoncodec.Registry, pipeline interface{}) (bsoncore.Document, bool, error) {
switch t := pipeline.(type) {
case bsoncodec.ValueMarshaler:
btype, val, err := t.MarshalBSONValue()
if err != nil {
return nil, false, err
}
if btype != bsontype.Array {
return nil, false, fmt.Errorf("ValueMarshaler returned a %v, but was expecting %v", btype, bsontype.Array)
}
var hasDollarOut bool
pipelineDoc := bsoncore.Document(val)
if _, err := pipelineDoc.LookupErr("$out"); err == nil {
hasDollarOut = true
}
return pipelineDoc, hasDollarOut, nil
default:
val := reflect.ValueOf(t)
if !val.IsValid() || (val.Kind() != reflect.Slice && val.Kind() != reflect.Array) {
return nil, false, fmt.Errorf("can only transform slices and arrays into aggregation pipelines, but got %v", val.Kind())
}
aidx, arr := bsoncore.AppendArrayStart(nil)
var hasDollarOut bool
valLen := val.Len()
for idx := 0; idx < valLen; idx++ {
doc, err := transformBsoncoreDocument(registry, val.Index(idx).Interface())
if err != nil {
return nil, false, err
}
if idx == valLen-1 {
if elem, err := doc.IndexErr(0); err == nil && elem.Key() == "$out" {
hasDollarOut = true
}
}
arr = bsoncore.AppendDocumentElement(arr, strconv.Itoa(idx), doc)
}
arr, _ = bsoncore.AppendArrayEnd(arr, aidx)
return arr, hasDollarOut, nil
}
}
func transformValue(registry *bsoncodec.Registry, val interface{}) (bsoncore.Value, error) {
switch conv := val.(type) {
case string:
return bsoncore.Value{Type: bsontype.String, Data: bsoncore.AppendString(nil, conv)}, nil
default:
doc, err := transformBsoncoreDocument(registry, val)
if err != nil {
return bsoncore.Value{}, err
}
return bsoncore.Value{Type: bsontype.EmbeddedDocument, Data: doc}, nil
}
}
// Build the aggregation pipeline for the CountDocument command.
func countDocumentsAggregatePipeline(registry *bsoncodec.Registry, filter interface{}, opts *options.CountOptions) (bsonx.Arr, error) {
pipeline := bsonx.Arr{}
filterDoc, err := transformDocument(registry, filter)
if err != nil {
return nil, err
}
pipeline = append(pipeline, bsonx.Document(bsonx.Doc{{"$match", bsonx.Document(filterDoc)}}))
if opts != nil {
if opts.Skip != nil {
pipeline = append(pipeline, bsonx.Document(bsonx.Doc{{"$skip", bsonx.Int64(*opts.Skip)}}))
}
if opts.Limit != nil {
pipeline = append(pipeline, bsonx.Document(bsonx.Doc{{"$limit", bsonx.Int64(*opts.Limit)}}))
}
}
pipeline = append(pipeline, bsonx.Document(bsonx.Doc{
{"$group", bsonx.Document(bsonx.Doc{
{"_id", bsonx.Int32(1)},
{"n", bsonx.Document(bsonx.Doc{{"$sum", bsonx.Int32(1)}})},
})},
},
))
return pipeline, nil
}

View File

@@ -0,0 +1,119 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import "time"
// AggregateOptions represents all possible options to the Aggregate() function.
type AggregateOptions struct {
AllowDiskUse *bool // Enables writing to temporary files. When set to true, aggregation stages can write data to the _tmp subdirectory in the dbPath directory
BatchSize *int32 // The number of documents to return per batch
BypassDocumentValidation *bool // If true, allows the write to opt-out of document level validation. This only applies when the $out stage is specified
Collation *Collation // Specifies a collation
MaxTime *time.Duration // The maximum amount of time to allow the query to run
MaxAwaitTime *time.Duration // The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor query
Comment *string // Enables users to specify an arbitrary string to help trace the operation through the database profiler, currentOp and logs.
Hint interface{} // The index to use for the aggregation. The hint does not apply to $lookup and $graphLookup stages
}
// Aggregate returns a pointer to a new AggregateOptions
func Aggregate() *AggregateOptions {
return &AggregateOptions{}
}
// SetAllowDiskUse enables writing to temporary files. When set to true,
// aggregation stages can write data to the _tmp subdirectory in the
// dbPath directory
func (ao *AggregateOptions) SetAllowDiskUse(b bool) *AggregateOptions {
ao.AllowDiskUse = &b
return ao
}
// SetBatchSize specifies the number of documents to return per batch
func (ao *AggregateOptions) SetBatchSize(i int32) *AggregateOptions {
ao.BatchSize = &i
return ao
}
// SetBypassDocumentValidation allows the write to opt-out of document level
// validation. This only applies when the $out stage is specified
// Valid for server versions >= 3.2. For servers < 3.2, this option is ignored.
func (ao *AggregateOptions) SetBypassDocumentValidation(b bool) *AggregateOptions {
ao.BypassDocumentValidation = &b
return ao
}
// SetCollation specifies a collation.
// Valid for server versions >= 3.4
func (ao *AggregateOptions) SetCollation(c *Collation) *AggregateOptions {
ao.Collation = c
return ao
}
// SetMaxTime specifies the maximum amount of time to allow the query to run
func (ao *AggregateOptions) SetMaxTime(d time.Duration) *AggregateOptions {
ao.MaxTime = &d
return ao
}
// SetMaxAwaitTime specifies the maximum amount of time for the server to
// wait on new documents to satisfy a tailable cursor query
// For servers < 3.2, this option is ignored
func (ao *AggregateOptions) SetMaxAwaitTime(d time.Duration) *AggregateOptions {
ao.MaxAwaitTime = &d
return ao
}
// SetComment enables users to specify an arbitrary string to help trace the
// operation through the database profiler, currentOp and logs.
func (ao *AggregateOptions) SetComment(s string) *AggregateOptions {
ao.Comment = &s
return ao
}
// SetHint specifies the index to use for the aggregation. The hint does not
// apply to $lookup and $graphLookup stages
func (ao *AggregateOptions) SetHint(h interface{}) *AggregateOptions {
ao.Hint = h
return ao
}
// MergeAggregateOptions combines the argued AggregateOptions into a single AggregateOptions in a last-one-wins fashion
func MergeAggregateOptions(opts ...*AggregateOptions) *AggregateOptions {
aggOpts := Aggregate()
for _, ao := range opts {
if ao == nil {
continue
}
if ao.AllowDiskUse != nil {
aggOpts.AllowDiskUse = ao.AllowDiskUse
}
if ao.BatchSize != nil {
aggOpts.BatchSize = ao.BatchSize
}
if ao.BypassDocumentValidation != nil {
aggOpts.BypassDocumentValidation = ao.BypassDocumentValidation
}
if ao.Collation != nil {
aggOpts.Collation = ao.Collation
}
if ao.MaxTime != nil {
aggOpts.MaxTime = ao.MaxTime
}
if ao.MaxAwaitTime != nil {
aggOpts.MaxAwaitTime = ao.MaxAwaitTime
}
if ao.Comment != nil {
aggOpts.Comment = ao.Comment
}
if ao.Hint != nil {
aggOpts.Hint = ao.Hint
}
}
return aggOpts
}

View File

@@ -0,0 +1,55 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
// DefaultOrdered is the default order for a BulkWriteOptions struct created from BulkWrite.
var DefaultOrdered = true
// BulkWriteOptions represent all possible options for a bulkWrite operation.
type BulkWriteOptions struct {
BypassDocumentValidation *bool // If true, allows the write to opt out of document-level validation.
Ordered *bool // If true, when a write fails, return without performing remaining writes. Defaults to true.
}
// BulkWrite creates a new *BulkWriteOptions
func BulkWrite() *BulkWriteOptions {
return &BulkWriteOptions{
Ordered: &DefaultOrdered,
}
}
// SetOrdered configures the ordered option. If true, when a write fails, the function will return without attempting
// remaining writes. Defaults to true.
func (b *BulkWriteOptions) SetOrdered(ordered bool) *BulkWriteOptions {
b.Ordered = &ordered
return b
}
// SetBypassDocumentValidation specifies if the write should opt out of document-level validation.
// Valid for server versions >= 3.2. For servers < 3.2, this option is ignored.
func (b *BulkWriteOptions) SetBypassDocumentValidation(bypass bool) *BulkWriteOptions {
b.BypassDocumentValidation = &bypass
return b
}
// MergeBulkWriteOptions combines the given *BulkWriteOptions into a single *BulkWriteOptions in a last one wins fashion.
func MergeBulkWriteOptions(opts ...*BulkWriteOptions) *BulkWriteOptions {
b := BulkWrite()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.Ordered != nil {
b.Ordered = opt.Ordered
}
if opt.BypassDocumentValidation != nil {
b.BypassDocumentValidation = opt.BypassDocumentValidation
}
}
return b
}

View File

@@ -0,0 +1,110 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import (
"go.mongodb.org/mongo-driver/bson/primitive"
"time"
)
// ChangeStreamOptions represents all possible options to a change stream
type ChangeStreamOptions struct {
BatchSize *int32 // The number of documents to return per batch
Collation *Collation // Specifies a collation
FullDocument *FullDocument // When set to updateLookup, the change notification for partial updates will include both a delta describing the changes to the document, as well as a copy of the entire document that was changed from some time after the change occurred.
MaxAwaitTime *time.Duration // The maximum amount of time for the server to wait on new documents to satisfy a change stream query
ResumeAfter interface{} // Specifies the logical starting point for the new change stream
StartAtOperationTime *primitive.Timestamp // Ensures that a change stream will only provide changes that occurred after a timestamp.
StartAfter interface{} // Specifies a resume token. The started change stream will return the first notification after the token.
}
// ChangeStream returns a pointer to a new ChangeStreamOptions
func ChangeStream() *ChangeStreamOptions {
cso := &ChangeStreamOptions{}
cso.SetFullDocument(Default)
return cso
}
// SetBatchSize specifies the number of documents to return per batch
func (cso *ChangeStreamOptions) SetBatchSize(i int32) *ChangeStreamOptions {
cso.BatchSize = &i
return cso
}
// SetCollation specifies a collation
func (cso *ChangeStreamOptions) SetCollation(c Collation) *ChangeStreamOptions {
cso.Collation = &c
return cso
}
// SetFullDocument specifies the fullDocument option.
// When set to updateLookup, the change notification for partial updates will
// include both a delta describing the changes to the document, as well as a
// copy of the entire document that was changed from some time after the change
// occurred.
func (cso *ChangeStreamOptions) SetFullDocument(fd FullDocument) *ChangeStreamOptions {
cso.FullDocument = &fd
return cso
}
// SetMaxAwaitTime specifies the maximum amount of time for the server to wait on new documents to satisfy a change stream query
func (cso *ChangeStreamOptions) SetMaxAwaitTime(d time.Duration) *ChangeStreamOptions {
cso.MaxAwaitTime = &d
return cso
}
// SetResumeAfter specifies the logical starting point for the new change stream
func (cso *ChangeStreamOptions) SetResumeAfter(rt interface{}) *ChangeStreamOptions {
cso.ResumeAfter = rt
return cso
}
// SetStartAtOperationTime ensures that a change stream will only provide changes that occurred after a specified timestamp.
func (cso *ChangeStreamOptions) SetStartAtOperationTime(t *primitive.Timestamp) *ChangeStreamOptions {
cso.StartAtOperationTime = t
return cso
}
// SetStartAfter specifies a resume token. The resulting change stream will return the first notification after the token.
// Cannot be used in conjunction with ResumeAfter.
func (cso *ChangeStreamOptions) SetStartAfter(sa interface{}) *ChangeStreamOptions {
cso.StartAfter = sa
return cso
}
// MergeChangeStreamOptions combines the argued ChangeStreamOptions into a single ChangeStreamOptions in a last-one-wins fashion
func MergeChangeStreamOptions(opts ...*ChangeStreamOptions) *ChangeStreamOptions {
csOpts := ChangeStream()
for _, cso := range opts {
if cso == nil {
continue
}
if cso.BatchSize != nil {
csOpts.BatchSize = cso.BatchSize
}
if cso.Collation != nil {
csOpts.Collation = cso.Collation
}
if cso.FullDocument != nil {
csOpts.FullDocument = cso.FullDocument
}
if cso.MaxAwaitTime != nil {
csOpts.MaxAwaitTime = cso.MaxAwaitTime
}
if cso.ResumeAfter != nil {
csOpts.ResumeAfter = cso.ResumeAfter
}
if cso.StartAtOperationTime != nil {
csOpts.StartAtOperationTime = cso.StartAtOperationTime
}
if cso.StartAfter != nil {
csOpts.StartAfter = cso.StartAfter
}
}
return csOpts
}

View File

@@ -0,0 +1,633 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options // import "go.mongodb.org/mongo-driver/mongo/options"
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"net"
"strings"
"time"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/event"
"go.mongodb.org/mongo-driver/mongo/readconcern"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
"go.mongodb.org/mongo-driver/tag"
"go.mongodb.org/mongo-driver/x/mongo/driver/connstring"
)
// ContextDialer makes new network connections
type ContextDialer interface {
DialContext(ctx context.Context, network, address string) (net.Conn, error)
}
// Credential holds auth options.
//
// AuthMechanism indicates the mechanism to use for authentication.
// Supported values include "SCRAM-SHA-256", "SCRAM-SHA-1", "MONGODB-CR", "PLAIN", "GSSAPI", and "MONGODB-X509".
//
// AuthMechanismProperties specifies additional configuration options which may be used by certain
// authentication mechanisms. Supported properties are:
// SERVICE_NAME: Specifies the name of the service. Defaults to mongodb.
// CANONICALIZE_HOST_NAME: If true, tells the driver to canonicalize the given hostname. Defaults to false. This
// property may not be used on Linux and Darwin systems and may not be used at the same time as SERVICE_HOST.
// SERVICE_REALM: Specifies the realm of the service.
// SERVICE_HOST: Specifies a hostname for GSSAPI authentication if it is different from the server's address. For
// authentication mechanisms besides GSSAPI, this property is ignored.
//
// AuthSource specifies the database to authenticate against.
//
// Username specifies the username that will be authenticated.
//
// Password specifies the password used for authentication.
//
// PasswordSet specifies if the password is actually set, since an empty password is a valid password.
type Credential struct {
AuthMechanism string
AuthMechanismProperties map[string]string
AuthSource string
Username string
Password string
PasswordSet bool
}
// ClientOptions represents all possible options to configure a client.
type ClientOptions struct {
AppName *string
Auth *Credential
ConnectTimeout *time.Duration
Compressors []string
Dialer ContextDialer
HeartbeatInterval *time.Duration
Hosts []string
LocalThreshold *time.Duration
MaxConnIdleTime *time.Duration
MaxPoolSize *uint16
Monitor *event.CommandMonitor
ReadConcern *readconcern.ReadConcern
ReadPreference *readpref.ReadPref
Registry *bsoncodec.Registry
ReplicaSet *string
RetryWrites *bool
ServerSelectionTimeout *time.Duration
Direct *bool
SocketTimeout *time.Duration
TLSConfig *tls.Config
WriteConcern *writeconcern.WriteConcern
ZlibLevel *int
err error
// Adds an option for internal use only and should not be set. This option is deprecated and is
// not part of the stability guarantee. It may be removed in the future.
AuthenticateToAnything *bool
}
// Client creates a new ClientOptions instance.
func Client() *ClientOptions {
return new(ClientOptions)
}
// Validate validates the client options. This method will return the first error found.
func (c *ClientOptions) Validate() error { return c.err }
// ApplyURI parses the provided connection string and sets the values and options accordingly.
//
// Errors that occur in this method can be retrieved by calling Validate.
//
// If the URI contains ssl=true this method will overwrite TLSConfig, even if there aren't any other
// tls options specified.
func (c *ClientOptions) ApplyURI(uri string) *ClientOptions {
if c.err != nil {
return c
}
cs, err := connstring.Parse(uri)
if err != nil {
c.err = err
return c
}
if cs.AppName != "" {
c.AppName = &cs.AppName
}
if cs.AuthMechanism != "" || cs.AuthMechanismProperties != nil || cs.AuthSource != "" ||
cs.Username != "" || cs.PasswordSet {
c.Auth = &Credential{
AuthMechanism: cs.AuthMechanism,
AuthMechanismProperties: cs.AuthMechanismProperties,
AuthSource: cs.AuthSource,
Username: cs.Username,
Password: cs.Password,
PasswordSet: cs.PasswordSet,
}
}
if cs.ConnectSet {
direct := cs.Connect == connstring.SingleConnect
c.Direct = &direct
}
if cs.ConnectTimeoutSet {
c.ConnectTimeout = &cs.ConnectTimeout
}
if len(cs.Compressors) > 0 {
c.Compressors = cs.Compressors
}
if cs.HeartbeatIntervalSet {
c.HeartbeatInterval = &cs.HeartbeatInterval
}
c.Hosts = cs.Hosts
if cs.LocalThresholdSet {
c.LocalThreshold = &cs.LocalThreshold
}
if cs.MaxConnIdleTimeSet {
c.MaxConnIdleTime = &cs.MaxConnIdleTime
}
if cs.MaxPoolSizeSet {
c.MaxPoolSize = &cs.MaxPoolSize
}
if cs.ReadConcernLevel != "" {
c.ReadConcern = readconcern.New(readconcern.Level(cs.ReadConcernLevel))
}
if cs.ReadPreference != "" || len(cs.ReadPreferenceTagSets) > 0 || cs.MaxStalenessSet {
opts := make([]readpref.Option, 0, 1)
tagSets := tag.NewTagSetsFromMaps(cs.ReadPreferenceTagSets)
if len(tagSets) > 0 {
opts = append(opts, readpref.WithTagSets(tagSets...))
}
if cs.MaxStaleness != 0 {
opts = append(opts, readpref.WithMaxStaleness(cs.MaxStaleness))
}
mode, err := readpref.ModeFromString(cs.ReadPreference)
if err != nil {
c.err = err
return c
}
c.ReadPreference, c.err = readpref.New(mode, opts...)
if c.err != nil {
return c
}
}
if cs.RetryWritesSet {
c.RetryWrites = &cs.RetryWrites
}
if cs.ReplicaSet != "" {
c.ReplicaSet = &cs.ReplicaSet
}
if cs.ServerSelectionTimeoutSet {
c.ServerSelectionTimeout = &cs.ServerSelectionTimeout
}
if cs.SocketTimeoutSet {
c.SocketTimeout = &cs.SocketTimeout
}
if cs.SSL {
tlsConfig := new(tls.Config)
if cs.SSLCaFileSet {
c.err = addCACertFromFile(tlsConfig, cs.SSLCaFile)
if c.err != nil {
return c
}
}
if cs.SSLInsecure {
tlsConfig.InsecureSkipVerify = true
}
if cs.SSLClientCertificateKeyFileSet {
var keyPasswd string
if cs.SSLClientCertificateKeyPasswordSet && cs.SSLClientCertificateKeyPassword != nil {
keyPasswd = cs.SSLClientCertificateKeyPassword()
}
s, err := addClientCertFromFile(tlsConfig, cs.SSLClientCertificateKeyFile, keyPasswd)
if err != nil {
c.err = err
return c
}
// If a username wasn't specified, add one from the certificate.
if c.Auth != nil && strings.ToLower(c.Auth.AuthMechanism) == "mongodb-x509" && c.Auth.Username == "" {
// The Go x509 package gives the subject with the pairs in reverse order that we want.
pairs := strings.Split(s, ",")
for left, right := 0, len(pairs)-1; left < right; left, right = left+1, right-1 {
pairs[left], pairs[right] = pairs[right], pairs[left]
}
c.Auth.Username = strings.Join(pairs, ",")
}
}
c.TLSConfig = tlsConfig
}
if cs.JSet || cs.WString != "" || cs.WNumberSet || cs.WTimeoutSet {
opts := make([]writeconcern.Option, 0, 1)
if len(cs.WString) > 0 {
opts = append(opts, writeconcern.WTagSet(cs.WString))
} else if cs.WNumberSet {
opts = append(opts, writeconcern.W(cs.WNumber))
}
if cs.JSet {
opts = append(opts, writeconcern.J(cs.J))
}
if cs.WTimeoutSet {
opts = append(opts, writeconcern.WTimeout(cs.WTimeout))
}
c.WriteConcern = writeconcern.New(opts...)
}
if cs.ZlibLevelSet {
c.ZlibLevel = &cs.ZlibLevel
}
return c
}
// SetAppName specifies the client application name. This value is used by MongoDB when it logs
// connection information and profile information, such as slow queries.
func (c *ClientOptions) SetAppName(s string) *ClientOptions {
c.AppName = &s
return c
}
// SetAuth sets the authentication options.
func (c *ClientOptions) SetAuth(auth Credential) *ClientOptions {
c.Auth = &auth
return c
}
// SetCompressors sets the compressors that can be used when communicating with a server.
func (c *ClientOptions) SetCompressors(comps []string) *ClientOptions {
c.Compressors = comps
return c
}
// SetConnectTimeout specifies the timeout for an initial connection to a server.
// If a custom Dialer is used, this method won't be set and the user is
// responsible for setting the ConnectTimeout for connections on the dialer
// themselves.
func (c *ClientOptions) SetConnectTimeout(d time.Duration) *ClientOptions {
c.ConnectTimeout = &d
return c
}
// SetDialer specifies a custom dialer used to dial new connections to a server.
// If a custom dialer is not set, a net.Dialer with a 300 second keepalive time will be used by default.
func (c *ClientOptions) SetDialer(d ContextDialer) *ClientOptions {
c.Dialer = d
return c
}
// SetDirect specifies whether the driver should connect directly to the server instead of
// auto-discovering other servers in the cluster.
func (c *ClientOptions) SetDirect(b bool) *ClientOptions {
c.Direct = &b
return c
}
// SetHeartbeatInterval specifies the interval to wait between server monitoring checks.
func (c *ClientOptions) SetHeartbeatInterval(d time.Duration) *ClientOptions {
c.HeartbeatInterval = &d
return c
}
// SetHosts specifies the initial list of addresses from which to discover the rest of the cluster.
func (c *ClientOptions) SetHosts(s []string) *ClientOptions {
c.Hosts = s
return c
}
// SetLocalThreshold specifies how far to distribute queries, beyond the server with the fastest
// round-trip time. If a server's roundtrip time is more than LocalThreshold slower than the
// the fastest, the driver will not send queries to that server.
func (c *ClientOptions) SetLocalThreshold(d time.Duration) *ClientOptions {
c.LocalThreshold = &d
return c
}
// SetMaxConnIdleTime specifies the maximum number of milliseconds that a connection can remain idle
// in a connection pool before being removed and closed.
func (c *ClientOptions) SetMaxConnIdleTime(d time.Duration) *ClientOptions {
c.MaxConnIdleTime = &d
return c
}
// SetMaxPoolSize specifies the max size of a server's connection pool.
func (c *ClientOptions) SetMaxPoolSize(u uint16) *ClientOptions {
c.MaxPoolSize = &u
return c
}
// SetMonitor specifies a command monitor used to see commands for a client.
func (c *ClientOptions) SetMonitor(m *event.CommandMonitor) *ClientOptions {
c.Monitor = m
return c
}
// SetReadConcern specifies the read concern.
func (c *ClientOptions) SetReadConcern(rc *readconcern.ReadConcern) *ClientOptions {
c.ReadConcern = rc
return c
}
// SetReadPreference specifies the read preference.
func (c *ClientOptions) SetReadPreference(rp *readpref.ReadPref) *ClientOptions {
c.ReadPreference = rp
return c
}
// SetRegistry specifies the bsoncodec.Registry.
func (c *ClientOptions) SetRegistry(registry *bsoncodec.Registry) *ClientOptions {
c.Registry = registry
return c
}
// SetReplicaSet specifies the name of the replica set of the cluster.
func (c *ClientOptions) SetReplicaSet(s string) *ClientOptions {
c.ReplicaSet = &s
return c
}
// SetRetryWrites specifies whether the client has retryable writes enabled.
func (c *ClientOptions) SetRetryWrites(b bool) *ClientOptions {
c.RetryWrites = &b
return c
}
// SetServerSelectionTimeout specifies a timeout in milliseconds to block for server selection.
func (c *ClientOptions) SetServerSelectionTimeout(d time.Duration) *ClientOptions {
c.ServerSelectionTimeout = &d
return c
}
// SetSocketTimeout specifies the time in milliseconds to attempt to send or receive on a socket
// before the attempt times out.
func (c *ClientOptions) SetSocketTimeout(d time.Duration) *ClientOptions {
c.SocketTimeout = &d
return c
}
// SetTLSConfig sets the tls.Config.
func (c *ClientOptions) SetTLSConfig(cfg *tls.Config) *ClientOptions {
c.TLSConfig = cfg
return c
}
// SetWriteConcern sets the write concern.
func (c *ClientOptions) SetWriteConcern(wc *writeconcern.WriteConcern) *ClientOptions {
c.WriteConcern = wc
return c
}
// SetZlibLevel sets the level for the zlib compressor.
func (c *ClientOptions) SetZlibLevel(level int) *ClientOptions {
c.ZlibLevel = &level
return c
}
// MergeClientOptions combines the given connstring and *ClientOptions into a single *ClientOptions in a last one wins
// fashion. The given connstring will be used for the default options, which can be overwritten using the given
// *ClientOptions.
func MergeClientOptions(opts ...*ClientOptions) *ClientOptions {
c := Client()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.Dialer != nil {
c.Dialer = opt.Dialer
}
if opt.AppName != nil {
c.AppName = opt.AppName
}
if opt.Auth != nil {
c.Auth = opt.Auth
}
if opt.AuthenticateToAnything != nil {
c.AuthenticateToAnything = opt.AuthenticateToAnything
}
if opt.Compressors != nil {
c.Compressors = opt.Compressors
}
if opt.ConnectTimeout != nil {
c.ConnectTimeout = opt.ConnectTimeout
}
if opt.HeartbeatInterval != nil {
c.HeartbeatInterval = opt.HeartbeatInterval
}
if len(opt.Hosts) > 0 {
c.Hosts = opt.Hosts
}
if opt.LocalThreshold != nil {
c.LocalThreshold = opt.LocalThreshold
}
if opt.MaxConnIdleTime != nil {
c.MaxConnIdleTime = opt.MaxConnIdleTime
}
if opt.MaxPoolSize != nil {
c.MaxPoolSize = opt.MaxPoolSize
}
if opt.Monitor != nil {
c.Monitor = opt.Monitor
}
if opt.ReadConcern != nil {
c.ReadConcern = opt.ReadConcern
}
if opt.ReadPreference != nil {
c.ReadPreference = opt.ReadPreference
}
if opt.Registry != nil {
c.Registry = opt.Registry
}
if opt.ReplicaSet != nil {
c.ReplicaSet = opt.ReplicaSet
}
if opt.RetryWrites != nil {
c.RetryWrites = opt.RetryWrites
}
if opt.ServerSelectionTimeout != nil {
c.ServerSelectionTimeout = opt.ServerSelectionTimeout
}
if opt.Direct != nil {
c.Direct = opt.Direct
}
if opt.SocketTimeout != nil {
c.SocketTimeout = opt.SocketTimeout
}
if opt.TLSConfig != nil {
c.TLSConfig = opt.TLSConfig
}
if opt.WriteConcern != nil {
c.WriteConcern = opt.WriteConcern
}
if opt.ZlibLevel != nil {
c.ZlibLevel = opt.ZlibLevel
}
if opt.err != nil {
c.err = opt.err
}
}
return c
}
// addCACertFromFile adds a root CA certificate to the configuration given a path
// to the containing file.
func addCACertFromFile(cfg *tls.Config, file string) error {
data, err := ioutil.ReadFile(file)
if err != nil {
return err
}
certBytes, err := loadCert(data)
if err != nil {
return err
}
cert, err := x509.ParseCertificate(certBytes)
if err != nil {
return err
}
if cfg.RootCAs == nil {
cfg.RootCAs = x509.NewCertPool()
}
cfg.RootCAs.AddCert(cert)
return nil
}
func loadCert(data []byte) ([]byte, error) {
var certBlock *pem.Block
for certBlock == nil {
if data == nil || len(data) == 0 {
return nil, errors.New(".pem file must have both a CERTIFICATE and an RSA PRIVATE KEY section")
}
block, rest := pem.Decode(data)
if block == nil {
return nil, errors.New("invalid .pem file")
}
switch block.Type {
case "CERTIFICATE":
if certBlock != nil {
return nil, errors.New("multiple CERTIFICATE sections in .pem file")
}
certBlock = block
}
data = rest
}
return certBlock.Bytes, nil
}
// addClientCertFromFile adds a client certificate to the configuration given a path to the
// containing file and returns the certificate's subject name.
func addClientCertFromFile(cfg *tls.Config, clientFile, keyPasswd string) (string, error) {
data, err := ioutil.ReadFile(clientFile)
if err != nil {
return "", err
}
var currentBlock *pem.Block
var certBlock, certDecodedBlock, keyBlock []byte
remaining := data
start := 0
for {
currentBlock, remaining = pem.Decode(remaining)
if currentBlock == nil {
break
}
if currentBlock.Type == "CERTIFICATE" {
certBlock = data[start : len(data)-len(remaining)]
certDecodedBlock = currentBlock.Bytes
start += len(certBlock)
} else if strings.HasSuffix(currentBlock.Type, "PRIVATE KEY") {
if keyPasswd != "" && x509.IsEncryptedPEMBlock(currentBlock) {
var encoded bytes.Buffer
buf, err := x509.DecryptPEMBlock(currentBlock, []byte(keyPasswd))
if err != nil {
return "", err
}
pem.Encode(&encoded, &pem.Block{Type: currentBlock.Type, Bytes: buf})
keyBlock = encoded.Bytes()
start = len(data) - len(remaining)
} else {
keyBlock = data[start : len(data)-len(remaining)]
start += len(keyBlock)
}
}
}
if len(certBlock) == 0 {
return "", fmt.Errorf("failed to find CERTIFICATE")
}
if len(keyBlock) == 0 {
return "", fmt.Errorf("failed to find PRIVATE KEY")
}
cert, err := tls.X509KeyPair(certBlock, keyBlock)
if err != nil {
return "", err
}
cfg.Certificates = append(cfg.Certificates, cert)
// The documentation for the tls.X509KeyPair indicates that the Leaf certificate is not
// retained.
crt, err := x509.ParseCertificate(certDecodedBlock)
if err != nil {
return "", err
}
return x509CertSubject(crt), nil
}

View File

@@ -0,0 +1,9 @@
// +build go1.10
package options
import "crypto/x509"
func x509CertSubject(cert *x509.Certificate) string {
return cert.Subject.String()
}

View File

@@ -0,0 +1,13 @@
// +build !go1.10
package options
import (
"crypto/x509"
)
// We don't support version less then 1.10, but Evergreen needs to be able to compile the driver
// using version 1.8.
func x509CertSubject(cert *x509.Certificate) string {
return ""
}

View File

@@ -0,0 +1,77 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import (
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/mongo/readconcern"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
)
// CollectionOptions represent all possible options to configure a Collection.
type CollectionOptions struct {
ReadConcern *readconcern.ReadConcern // The read concern for operations in the collection.
WriteConcern *writeconcern.WriteConcern // The write concern for operations in the collection.
ReadPreference *readpref.ReadPref // The read preference for operations in the collection.
Registry *bsoncodec.Registry // The registry to be used to construct BSON encoders and decoders for the collection.
}
// Collection creates a new CollectionOptions instance
func Collection() *CollectionOptions {
return &CollectionOptions{}
}
// SetReadConcern sets the read concern for the collection.
func (c *CollectionOptions) SetReadConcern(rc *readconcern.ReadConcern) *CollectionOptions {
c.ReadConcern = rc
return c
}
// SetWriteConcern sets the write concern for the collection.
func (c *CollectionOptions) SetWriteConcern(wc *writeconcern.WriteConcern) *CollectionOptions {
c.WriteConcern = wc
return c
}
// SetReadPreference sets the read preference for the collection.
func (c *CollectionOptions) SetReadPreference(rp *readpref.ReadPref) *CollectionOptions {
c.ReadPreference = rp
return c
}
// SetRegistry sets the bsoncodec Registry for the collection.
func (c *CollectionOptions) SetRegistry(r *bsoncodec.Registry) *CollectionOptions {
c.Registry = r
return c
}
// MergeCollectionOptions combines the *CollectionOptions arguments into a single *CollectionOptions in a last one wins
// fashion.
func MergeCollectionOptions(opts ...*CollectionOptions) *CollectionOptions {
c := Collection()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.ReadConcern != nil {
c.ReadConcern = opt.ReadConcern
}
if opt.WriteConcern != nil {
c.WriteConcern = opt.WriteConcern
}
if opt.ReadPreference != nil {
c.ReadPreference = opt.ReadPreference
}
if opt.Registry != nil {
c.Registry = opt.Registry
}
}
return c
}

View File

@@ -0,0 +1,81 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import "time"
// CountOptions represents all possible options to the Count() function.
type CountOptions struct {
Collation *Collation // Specifies a collation
Hint interface{} // The index to use
Limit *int64 // The maximum number of documents to count
MaxTime *time.Duration // The maximum amount of time to allow the operation to run
Skip *int64 // The number of documents to skip before counting
}
// Count returns a pointer to a new CountOptions
func Count() *CountOptions {
return &CountOptions{}
}
// SetCollation specifies a collation
// Valid for server versions >= 3.4
func (co *CountOptions) SetCollation(c *Collation) *CountOptions {
co.Collation = c
return co
}
// SetHint specifies the index to use
func (co *CountOptions) SetHint(h interface{}) *CountOptions {
co.Hint = h
return co
}
// SetLimit specifies the maximum number of documents to count
func (co *CountOptions) SetLimit(i int64) *CountOptions {
co.Limit = &i
return co
}
// SetMaxTime specifies the maximum amount of time to allow the operation to run
func (co *CountOptions) SetMaxTime(d time.Duration) *CountOptions {
co.MaxTime = &d
return co
}
// SetSkip specifies the number of documents to skip before counting
func (co *CountOptions) SetSkip(i int64) *CountOptions {
co.Skip = &i
return co
}
// MergeCountOptions combines the argued CountOptions into a single CountOptions in a last-one-wins fashion
func MergeCountOptions(opts ...*CountOptions) *CountOptions {
countOpts := Count()
for _, co := range opts {
if co == nil {
continue
}
if co.Collation != nil {
countOpts.Collation = co.Collation
}
if co.Hint != nil {
countOpts.Hint = co.Hint
}
if co.Limit != nil {
countOpts.Limit = co.Limit
}
if co.MaxTime != nil {
countOpts.MaxTime = co.MaxTime
}
if co.Skip != nil {
countOpts.Skip = co.Skip
}
}
return countOpts
}

View File

@@ -0,0 +1,77 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import (
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/mongo/readconcern"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
)
// DatabaseOptions represent all possible options to configure a Database.
type DatabaseOptions struct {
ReadConcern *readconcern.ReadConcern // The read concern for operations in the database.
WriteConcern *writeconcern.WriteConcern // The write concern for operations in the database.
ReadPreference *readpref.ReadPref // The read preference for operations in the database.
Registry *bsoncodec.Registry // The registry to be used to construct BSON encoders and decoders for the database.
}
// Database creates a new DatabaseOptions instance
func Database() *DatabaseOptions {
return &DatabaseOptions{}
}
// SetReadConcern sets the read concern for the database.
func (d *DatabaseOptions) SetReadConcern(rc *readconcern.ReadConcern) *DatabaseOptions {
d.ReadConcern = rc
return d
}
// SetWriteConcern sets the write concern for the database.
func (d *DatabaseOptions) SetWriteConcern(wc *writeconcern.WriteConcern) *DatabaseOptions {
d.WriteConcern = wc
return d
}
// SetReadPreference sets the read preference for the database.
func (d *DatabaseOptions) SetReadPreference(rp *readpref.ReadPref) *DatabaseOptions {
d.ReadPreference = rp
return d
}
// SetRegistry sets the bsoncodec Registry for the database.
func (d *DatabaseOptions) SetRegistry(r *bsoncodec.Registry) *DatabaseOptions {
d.Registry = r
return d
}
// MergeDatabaseOptions combines the *DatabaseOptions arguments into a single *DatabaseOptions in a last one wins
// fashion.
func MergeDatabaseOptions(opts ...*DatabaseOptions) *DatabaseOptions {
d := Database()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.ReadConcern != nil {
d.ReadConcern = opt.ReadConcern
}
if opt.WriteConcern != nil {
d.WriteConcern = opt.WriteConcern
}
if opt.ReadPreference != nil {
d.ReadPreference = opt.ReadPreference
}
if opt.Registry != nil {
d.Registry = opt.Registry
}
}
return d
}

View File

@@ -0,0 +1,39 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
// DeleteOptions represents all possible options to the DeleteOne() and DeleteMany() functions.
type DeleteOptions struct {
Collation *Collation // Specifies a collation
}
// Delete returns a pointer to a new DeleteOptions
func Delete() *DeleteOptions {
return &DeleteOptions{}
}
// SetCollation specifies a collation
// Valid for servers >= 3.4.
func (do *DeleteOptions) SetCollation(c *Collation) *DeleteOptions {
do.Collation = c
return do
}
// MergeDeleteOptions combines the argued DeleteOptions into a single DeleteOptions in a last-one-wins fashion
func MergeDeleteOptions(opts ...*DeleteOptions) *DeleteOptions {
dOpts := Delete()
for _, do := range opts {
if do == nil {
continue
}
if do.Collation != nil {
dOpts.Collation = do.Collation
}
}
return dOpts
}

View File

@@ -0,0 +1,51 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import "time"
// DistinctOptions represents all possible options to the Distinct() function.
type DistinctOptions struct {
Collation *Collation // Specifies a collation
MaxTime *time.Duration // The maximum amount of time to allow the operation to run
}
// Distinct returns a pointer to a new DistinctOptions
func Distinct() *DistinctOptions {
return &DistinctOptions{}
}
// SetCollation specifies a collation
// Valid for server versions >= 3.4
func (do *DistinctOptions) SetCollation(c *Collation) *DistinctOptions {
do.Collation = c
return do
}
// SetMaxTime specifies the maximum amount of time to allow the operation to run
func (do *DistinctOptions) SetMaxTime(d time.Duration) *DistinctOptions {
do.MaxTime = &d
return do
}
// MergeDistinctOptions combines the argued DistinctOptions into a single DistinctOptions in a last-one-wins fashion
func MergeDistinctOptions(opts ...*DistinctOptions) *DistinctOptions {
distinctOpts := Distinct()
for _, do := range opts {
if do == nil {
continue
}
if do.Collation != nil {
distinctOpts.Collation = do.Collation
}
if do.MaxTime != nil {
distinctOpts.MaxTime = do.MaxTime
}
}
return distinctOpts
}

View File

@@ -0,0 +1,42 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import "time"
// EstimatedDocumentCountOptions represents all possible options to the EstimatedDocumentCount() function.
type EstimatedDocumentCountOptions struct {
MaxTime *time.Duration // The maximum amount of time to allow the operation to run
}
// EstimatedDocumentCount returns a pointer to a new EstimatedDocumentCountOptions
func EstimatedDocumentCount() *EstimatedDocumentCountOptions {
return &EstimatedDocumentCountOptions{}
}
// SetMaxTime specifies the maximum amount of time to allow the operation to run
func (eco *EstimatedDocumentCountOptions) SetMaxTime(d time.Duration) *EstimatedDocumentCountOptions {
eco.MaxTime = &d
return eco
}
// MergeEstimatedDocumentCountOptions combines the given *EstimatedDocumentCountOptions into a single
// *EstimatedDocumentCountOptions in a last one wins fashion.
func MergeEstimatedDocumentCountOptions(opts ...*EstimatedDocumentCountOptions) *EstimatedDocumentCountOptions {
e := EstimatedDocumentCount()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.MaxTime != nil {
e.MaxTime = opt.MaxTime
}
}
return e
}

View File

@@ -0,0 +1,693 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import (
"time"
)
// FindOptions represent all possible options to the Find() function.
type FindOptions struct {
AllowPartialResults *bool // If true, allows partial results to be returned if some shards are down.
BatchSize *int32 // Specifies the number of documents to return in every batch.
Collation *Collation // Specifies a collation to be used
Comment *string // Specifies a string to help trace the operation through the database.
CursorType *CursorType // Specifies the type of cursor to use
Hint interface{} // Specifies the index to use.
Limit *int64 // Sets a limit on the number of results to return.
Max interface{} // Sets an exclusive upper bound for a specific index
MaxAwaitTime *time.Duration // Specifies the maximum amount of time for the server to wait on new documents.
MaxTime *time.Duration // Specifies the maximum amount of time to allow the query to run.
Min interface{} // Specifies the inclusive lower bound for a specific index.
NoCursorTimeout *bool // If true, prevents cursors from timing out after an inactivity period.
OplogReplay *bool // Adds an option for internal use only and should not be set.
Projection interface{} // Limits the fields returned for all documents.
ReturnKey *bool // If true, only returns index keys for all result documents.
ShowRecordID *bool // If true, a $recordId field with the record identifier will be added to the returned documents.
Skip *int64 // Specifies the number of documents to skip before returning
Snapshot *bool // If true, prevents the cursor from returning a document more than once because of an intervening write operation.
Sort interface{} // Specifies the order in which to return results.
}
// Find creates a new FindOptions instance.
func Find() *FindOptions {
return &FindOptions{}
}
// SetAllowPartialResults sets whether partial results can be returned if some shards are down.
// For server versions < 3.2, this defaults to false.
func (f *FindOptions) SetAllowPartialResults(b bool) *FindOptions {
f.AllowPartialResults = &b
return f
}
// SetBatchSize sets the number of documents to return in each batch.
func (f *FindOptions) SetBatchSize(i int32) *FindOptions {
f.BatchSize = &i
return f
}
// SetCollation specifies a Collation to use for the Find operation.
// Valid for server versions >= 3.4
func (f *FindOptions) SetCollation(collation *Collation) *FindOptions {
f.Collation = collation
return f
}
// SetComment specifies a string to help trace the operation through the database.
func (f *FindOptions) SetComment(comment string) *FindOptions {
f.Comment = &comment
return f
}
// SetCursorType specifes the type of cursor to use.
func (f *FindOptions) SetCursorType(ct CursorType) *FindOptions {
f.CursorType = &ct
return f
}
// SetHint specifies the index to use.
func (f *FindOptions) SetHint(hint interface{}) *FindOptions {
f.Hint = hint
return f
}
// SetLimit specifies a limit on the number of results.
// A negative limit implies that only 1 batch should be returned.
func (f *FindOptions) SetLimit(i int64) *FindOptions {
f.Limit = &i
return f
}
// SetMax specifies an exclusive upper bound for a specific index.
func (f *FindOptions) SetMax(max interface{}) *FindOptions {
f.Max = max
return f
}
// SetMaxAwaitTime specifies the max amount of time for the server to wait on new documents.
// If the cursor type is not TailableAwait, this option is ignored.
// For server versions < 3.2, this option is ignored.
func (f *FindOptions) SetMaxAwaitTime(d time.Duration) *FindOptions {
f.MaxAwaitTime = &d
return f
}
// SetMaxTime specifies the max time to allow the query to run.
func (f *FindOptions) SetMaxTime(d time.Duration) *FindOptions {
f.MaxTime = &d
return f
}
// SetMin specifies the inclusive lower bound for a specific index.
func (f *FindOptions) SetMin(min interface{}) *FindOptions {
f.Min = min
return f
}
// SetNoCursorTimeout specifies whether or not cursors should time out after a period of inactivity.
// For server versions < 3.2, this defaults to false.
func (f *FindOptions) SetNoCursorTimeout(b bool) *FindOptions {
f.NoCursorTimeout = &b
return f
}
// SetOplogReplay adds an option for internal use only and should not be set.
// For server versions < 3.2, this defaults to false.
func (f *FindOptions) SetOplogReplay(b bool) *FindOptions {
f.OplogReplay = &b
return f
}
// SetProjection adds an option to limit the fields returned for all documents.
func (f *FindOptions) SetProjection(projection interface{}) *FindOptions {
f.Projection = projection
return f
}
// SetReturnKey adds an option to only return index keys for all result documents.
func (f *FindOptions) SetReturnKey(b bool) *FindOptions {
f.ReturnKey = &b
return f
}
// SetShowRecordID adds an option to determine whether to return the record identifier for each document.
// If true, a $recordId field will be added to each returned document.
func (f *FindOptions) SetShowRecordID(b bool) *FindOptions {
f.ShowRecordID = &b
return f
}
// SetSkip specifies the number of documents to skip before returning.
// For server versions < 3.2, this defaults to 0.
func (f *FindOptions) SetSkip(i int64) *FindOptions {
f.Skip = &i
return f
}
// SetSnapshot prevents the cursor from returning a document more than once because of an intervening write operation.
func (f *FindOptions) SetSnapshot(b bool) *FindOptions {
f.Snapshot = &b
return f
}
// SetSort specifies the order in which to return documents.
func (f *FindOptions) SetSort(sort interface{}) *FindOptions {
f.Sort = sort
return f
}
// MergeFindOptions combines the argued FindOptions into a single FindOptions in a last-one-wins fashion
func MergeFindOptions(opts ...*FindOptions) *FindOptions {
fo := Find()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.AllowPartialResults != nil {
fo.AllowPartialResults = opt.AllowPartialResults
}
if opt.BatchSize != nil {
fo.BatchSize = opt.BatchSize
}
if opt.Collation != nil {
fo.Collation = opt.Collation
}
if opt.Comment != nil {
fo.Comment = opt.Comment
}
if opt.CursorType != nil {
fo.CursorType = opt.CursorType
}
if opt.Hint != nil {
fo.Hint = opt.Hint
}
if opt.Limit != nil {
fo.Limit = opt.Limit
}
if opt.Max != nil {
fo.Max = opt.Max
}
if opt.MaxAwaitTime != nil {
fo.MaxAwaitTime = opt.MaxAwaitTime
}
if opt.MaxTime != nil {
fo.MaxTime = opt.MaxTime
}
if opt.Min != nil {
fo.Min = opt.Min
}
if opt.NoCursorTimeout != nil {
fo.NoCursorTimeout = opt.NoCursorTimeout
}
if opt.OplogReplay != nil {
fo.OplogReplay = opt.OplogReplay
}
if opt.Projection != nil {
fo.Projection = opt.Projection
}
if opt.ReturnKey != nil {
fo.ReturnKey = opt.ReturnKey
}
if opt.ShowRecordID != nil {
fo.ShowRecordID = opt.ShowRecordID
}
if opt.Skip != nil {
fo.Skip = opt.Skip
}
if opt.Snapshot != nil {
fo.Snapshot = opt.Snapshot
}
if opt.Sort != nil {
fo.Sort = opt.Sort
}
}
return fo
}
// FindOneOptions represent all possible options to the FindOne() function.
type FindOneOptions struct {
AllowPartialResults *bool // If true, allows partial results to be returned if some shards are down.
BatchSize *int32 // Specifies the number of documents to return in every batch.
Collation *Collation // Specifies a collation to be used
Comment *string // Specifies a string to help trace the operation through the database.
CursorType *CursorType // Specifies the type of cursor to use
Hint interface{} // Specifies the index to use.
Max interface{} // Sets an exclusive upper bound for a specific index
MaxAwaitTime *time.Duration // Specifies the maximum amount of time for the server to wait on new documents.
MaxTime *time.Duration // Specifies the maximum amount of time to allow the query to run.
Min interface{} // Specifies the inclusive lower bound for a specific index.
NoCursorTimeout *bool // If true, prevents cursors from timing out after an inactivity period.
OplogReplay *bool // Adds an option for internal use only and should not be set.
Projection interface{} // Limits the fields returned for all documents.
ReturnKey *bool // If true, only returns index keys for all result documents.
ShowRecordID *bool // If true, a $recordId field with the record identifier will be added to the returned documents.
Skip *int64 // Specifies the number of documents to skip before returning
Snapshot *bool // If true, prevents the cursor from returning a document more than once because of an intervening write operation.
Sort interface{} // Specifies the order in which to return results.
}
// FindOne creates a new FindOneOptions instance.
func FindOne() *FindOneOptions {
return &FindOneOptions{}
}
// SetAllowPartialResults sets whether partial results can be returned if some shards are down.
func (f *FindOneOptions) SetAllowPartialResults(b bool) *FindOneOptions {
f.AllowPartialResults = &b
return f
}
// SetBatchSize sets the number of documents to return in each batch.
func (f *FindOneOptions) SetBatchSize(i int32) *FindOneOptions {
f.BatchSize = &i
return f
}
// SetCollation specifies a Collation to use for the Find operation.
func (f *FindOneOptions) SetCollation(collation *Collation) *FindOneOptions {
f.Collation = collation
return f
}
// SetComment specifies a string to help trace the operation through the database.
func (f *FindOneOptions) SetComment(comment string) *FindOneOptions {
f.Comment = &comment
return f
}
// SetCursorType specifes the type of cursor to use.
func (f *FindOneOptions) SetCursorType(ct CursorType) *FindOneOptions {
f.CursorType = &ct
return f
}
// SetHint specifies the index to use.
func (f *FindOneOptions) SetHint(hint interface{}) *FindOneOptions {
f.Hint = hint
return f
}
// SetMax specifies an exclusive upper bound for a specific index.
func (f *FindOneOptions) SetMax(max interface{}) *FindOneOptions {
f.Max = max
return f
}
// SetMaxAwaitTime specifies the max amount of time for the server to wait on new documents.
// For server versions < 3.2, this option is ignored.
func (f *FindOneOptions) SetMaxAwaitTime(d time.Duration) *FindOneOptions {
f.MaxAwaitTime = &d
return f
}
// SetMaxTime specifies the max time to allow the query to run.
func (f *FindOneOptions) SetMaxTime(d time.Duration) *FindOneOptions {
f.MaxTime = &d
return f
}
// SetMin specifies the inclusive lower bound for a specific index.
func (f *FindOneOptions) SetMin(min interface{}) *FindOneOptions {
f.Min = min
return f
}
// SetNoCursorTimeout specifies whether or not cursors should time out after a period of inactivity.
func (f *FindOneOptions) SetNoCursorTimeout(b bool) *FindOneOptions {
f.NoCursorTimeout = &b
return f
}
// SetOplogReplay adds an option for internal use only and should not be set.
func (f *FindOneOptions) SetOplogReplay(b bool) *FindOneOptions {
f.OplogReplay = &b
return f
}
// SetProjection adds an option to limit the fields returned for all documents.
func (f *FindOneOptions) SetProjection(projection interface{}) *FindOneOptions {
f.Projection = projection
return f
}
// SetReturnKey adds an option to only return index keys for all result documents.
func (f *FindOneOptions) SetReturnKey(b bool) *FindOneOptions {
f.ReturnKey = &b
return f
}
// SetShowRecordID adds an option to determine whether to return the record identifier for each document.
// If true, a $recordId field will be added to each returned document.
func (f *FindOneOptions) SetShowRecordID(b bool) *FindOneOptions {
f.ShowRecordID = &b
return f
}
// SetSkip specifies the number of documents to skip before returning.
func (f *FindOneOptions) SetSkip(i int64) *FindOneOptions {
f.Skip = &i
return f
}
// SetSnapshot prevents the cursor from returning a document more than once because of an intervening write operation.
func (f *FindOneOptions) SetSnapshot(b bool) *FindOneOptions {
f.Snapshot = &b
return f
}
// SetSort specifies the order in which to return documents.
func (f *FindOneOptions) SetSort(sort interface{}) *FindOneOptions {
f.Sort = sort
return f
}
// MergeFindOneOptions combines the argued FindOneOptions into a single FindOneOptions in a last-one-wins fashion
func MergeFindOneOptions(opts ...*FindOneOptions) *FindOneOptions {
fo := FindOne()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.AllowPartialResults != nil {
fo.AllowPartialResults = opt.AllowPartialResults
}
if opt.BatchSize != nil {
fo.BatchSize = opt.BatchSize
}
if opt.Collation != nil {
fo.Collation = opt.Collation
}
if opt.Comment != nil {
fo.Comment = opt.Comment
}
if opt.CursorType != nil {
fo.CursorType = opt.CursorType
}
if opt.Hint != nil {
fo.Hint = opt.Hint
}
if opt.Max != nil {
fo.Max = opt.Max
}
if opt.MaxAwaitTime != nil {
fo.MaxAwaitTime = opt.MaxAwaitTime
}
if opt.MaxTime != nil {
fo.MaxTime = opt.MaxTime
}
if opt.Min != nil {
fo.Min = opt.Min
}
if opt.NoCursorTimeout != nil {
fo.NoCursorTimeout = opt.NoCursorTimeout
}
if opt.OplogReplay != nil {
fo.OplogReplay = opt.OplogReplay
}
if opt.Projection != nil {
fo.Projection = opt.Projection
}
if opt.ReturnKey != nil {
fo.ReturnKey = opt.ReturnKey
}
if opt.ShowRecordID != nil {
fo.ShowRecordID = opt.ShowRecordID
}
if opt.Skip != nil {
fo.Skip = opt.Skip
}
if opt.Snapshot != nil {
fo.Snapshot = opt.Snapshot
}
if opt.Sort != nil {
fo.Sort = opt.Sort
}
}
return fo
}
// FindOneAndReplaceOptions represent all possible options to the FindOneAndReplace() function.
type FindOneAndReplaceOptions struct {
BypassDocumentValidation *bool // If true, allows the write to opt out of document-level validation.
Collation *Collation // Specifies a collation to be used
MaxTime *time.Duration // Specifies the maximum amount of time to allow the query to run.
Projection interface{} // Limits the fields returned for all documents.
ReturnDocument *ReturnDocument // Specifies whether the original or updated document should be returned.
Sort interface{} // Specifies the order in which to return results.
Upsert *bool // If true, creates a a new document if no document matches the query.
}
// FindOneAndReplace creates a new FindOneAndReplaceOptions instance.
func FindOneAndReplace() *FindOneAndReplaceOptions {
return &FindOneAndReplaceOptions{}
}
// SetBypassDocumentValidation specifies whether or not the write should opt out of document-level validation.
// Valid for server versions >= 3.2. For servers < 3.2, this option is ignored.
func (f *FindOneAndReplaceOptions) SetBypassDocumentValidation(b bool) *FindOneAndReplaceOptions {
f.BypassDocumentValidation = &b
return f
}
// SetCollation specifies a Collation to use for the Find operation.
func (f *FindOneAndReplaceOptions) SetCollation(collation *Collation) *FindOneAndReplaceOptions {
f.Collation = collation
return f
}
// SetMaxTime specifies the max time to allow the query to run.
func (f *FindOneAndReplaceOptions) SetMaxTime(d time.Duration) *FindOneAndReplaceOptions {
f.MaxTime = &d
return f
}
// SetProjection adds an option to limit the fields returned for all documents.
func (f *FindOneAndReplaceOptions) SetProjection(projection interface{}) *FindOneAndReplaceOptions {
f.Projection = projection
return f
}
// SetReturnDocument specifies whether the original or updated document should be returned.
// If set to Before, the original document will be returned. If set to After, the updated document
// will be returned.
func (f *FindOneAndReplaceOptions) SetReturnDocument(rd ReturnDocument) *FindOneAndReplaceOptions {
f.ReturnDocument = &rd
return f
}
// SetSort specifies the order in which to return documents.
func (f *FindOneAndReplaceOptions) SetSort(sort interface{}) *FindOneAndReplaceOptions {
f.Sort = sort
return f
}
// SetUpsert specifies if a new document should be created if no document matches the query.
func (f *FindOneAndReplaceOptions) SetUpsert(b bool) *FindOneAndReplaceOptions {
f.Upsert = &b
return f
}
// MergeFindOneAndReplaceOptions combines the argued FindOneAndReplaceOptions into a single FindOneAndReplaceOptions in a last-one-wins fashion
func MergeFindOneAndReplaceOptions(opts ...*FindOneAndReplaceOptions) *FindOneAndReplaceOptions {
fo := FindOneAndReplace()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.BypassDocumentValidation != nil {
fo.BypassDocumentValidation = opt.BypassDocumentValidation
}
if opt.Collation != nil {
fo.Collation = opt.Collation
}
if opt.MaxTime != nil {
fo.MaxTime = opt.MaxTime
}
if opt.Projection != nil {
fo.Projection = opt.Projection
}
if opt.ReturnDocument != nil {
fo.ReturnDocument = opt.ReturnDocument
}
if opt.Sort != nil {
fo.Sort = opt.Sort
}
if opt.Upsert != nil {
fo.Upsert = opt.Upsert
}
}
return fo
}
// FindOneAndUpdateOptions represent all possible options to the FindOneAndUpdate() function.
type FindOneAndUpdateOptions struct {
ArrayFilters *ArrayFilters // A set of filters specifying to which array elements an update should apply.
BypassDocumentValidation *bool // If true, allows the write to opt out of document-level validation.
Collation *Collation // Specifies a collation to be used
MaxTime *time.Duration // Specifies the maximum amount of time to allow the query to run.
Projection interface{} // Limits the fields returned for all documents.
ReturnDocument *ReturnDocument // Specifies whether the original or updated document should be returned.
Sort interface{} // Specifies the order in which to return results.
Upsert *bool // If true, creates a a new document if no document matches the query.
}
// FindOneAndUpdate creates a new FindOneAndUpdateOptions instance.
func FindOneAndUpdate() *FindOneAndUpdateOptions {
return &FindOneAndUpdateOptions{}
}
// SetBypassDocumentValidation sets filters that specify to which array elements an update should apply.
func (f *FindOneAndUpdateOptions) SetBypassDocumentValidation(b bool) *FindOneAndUpdateOptions {
f.BypassDocumentValidation = &b
return f
}
// SetArrayFilters specifies a set of filters, which
func (f *FindOneAndUpdateOptions) SetArrayFilters(filters ArrayFilters) *FindOneAndUpdateOptions {
f.ArrayFilters = &filters
return f
}
// SetCollation specifies a Collation to use for the Find operation.
func (f *FindOneAndUpdateOptions) SetCollation(collation *Collation) *FindOneAndUpdateOptions {
f.Collation = collation
return f
}
// SetMaxTime specifies the max time to allow the query to run.
func (f *FindOneAndUpdateOptions) SetMaxTime(d time.Duration) *FindOneAndUpdateOptions {
f.MaxTime = &d
return f
}
// SetProjection adds an option to limit the fields returned for all documents.
func (f *FindOneAndUpdateOptions) SetProjection(projection interface{}) *FindOneAndUpdateOptions {
f.Projection = projection
return f
}
// SetReturnDocument specifies whether the original or updated document should be returned.
// If set to Before, the original document will be returned. If set to After, the updated document
// will be returned.
func (f *FindOneAndUpdateOptions) SetReturnDocument(rd ReturnDocument) *FindOneAndUpdateOptions {
f.ReturnDocument = &rd
return f
}
// SetSort specifies the order in which to return documents.
func (f *FindOneAndUpdateOptions) SetSort(sort interface{}) *FindOneAndUpdateOptions {
f.Sort = sort
return f
}
// SetUpsert specifies if a new document should be created if no document matches the query.
func (f *FindOneAndUpdateOptions) SetUpsert(b bool) *FindOneAndUpdateOptions {
f.Upsert = &b
return f
}
// MergeFindOneAndUpdateOptions combines the argued FindOneAndUpdateOptions into a single FindOneAndUpdateOptions in a last-one-wins fashion
func MergeFindOneAndUpdateOptions(opts ...*FindOneAndUpdateOptions) *FindOneAndUpdateOptions {
fo := FindOneAndUpdate()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.ArrayFilters != nil {
fo.ArrayFilters = opt.ArrayFilters
}
if opt.BypassDocumentValidation != nil {
fo.BypassDocumentValidation = opt.BypassDocumentValidation
}
if opt.Collation != nil {
fo.Collation = opt.Collation
}
if opt.MaxTime != nil {
fo.MaxTime = opt.MaxTime
}
if opt.Projection != nil {
fo.Projection = opt.Projection
}
if opt.ReturnDocument != nil {
fo.ReturnDocument = opt.ReturnDocument
}
if opt.Sort != nil {
fo.Sort = opt.Sort
}
if opt.Upsert != nil {
fo.Upsert = opt.Upsert
}
}
return fo
}
// FindOneAndDeleteOptions represent all possible options to the FindOneAndDelete() function.
type FindOneAndDeleteOptions struct {
Collation *Collation // Specifies a collation to be used
MaxTime *time.Duration // Specifies the maximum amount of time to allow the query to run.
Projection interface{} // Limits the fields returned for all documents.
Sort interface{} // Specifies the order in which to return results.
}
// FindOneAndDelete creates a new FindOneAndDeleteOptions instance.
func FindOneAndDelete() *FindOneAndDeleteOptions {
return &FindOneAndDeleteOptions{}
}
// SetCollation specifies a Collation to use for the Find operation.
// Valid for server versions >= 3.4
func (f *FindOneAndDeleteOptions) SetCollation(collation *Collation) *FindOneAndDeleteOptions {
f.Collation = collation
return f
}
// SetMaxTime specifies the max time to allow the query to run.
func (f *FindOneAndDeleteOptions) SetMaxTime(d time.Duration) *FindOneAndDeleteOptions {
f.MaxTime = &d
return f
}
// SetProjection adds an option to limit the fields returned for all documents.
func (f *FindOneAndDeleteOptions) SetProjection(projection interface{}) *FindOneAndDeleteOptions {
f.Projection = projection
return f
}
// SetSort specifies the order in which to return documents.
func (f *FindOneAndDeleteOptions) SetSort(sort interface{}) *FindOneAndDeleteOptions {
f.Sort = sort
return f
}
// MergeFindOneAndDeleteOptions combines the argued FindOneAndDeleteOptions into a single FindOneAndDeleteOptions in a last-one-wins fashion
func MergeFindOneAndDeleteOptions(opts ...*FindOneAndDeleteOptions) *FindOneAndDeleteOptions {
fo := FindOneAndDelete()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.Collation != nil {
fo.Collation = opt.Collation
}
if opt.MaxTime != nil {
fo.MaxTime = opt.MaxTime
}
if opt.Projection != nil {
fo.Projection = opt.Projection
}
if opt.Sort != nil {
fo.Sort = opt.Sort
}
}
return fo
}

View File

@@ -0,0 +1,274 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import (
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/mongo/readconcern"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
)
// DefaultName is the default name for a GridFS bucket.
var DefaultName = "fs"
// DefaultChunkSize is the default size of each file chunk in bytes.
var DefaultChunkSize int32 = 255 * 1024 // 255 KiB
// DefaultRevision is the default revision number for a download by name operation.
var DefaultRevision int32 = -1
// BucketOptions represents all possible options to configure a GridFS bucket.
type BucketOptions struct {
Name *string // The bucket name. Defaults to "fs".
ChunkSizeBytes *int32 // The chunk size in bytes. Defaults to 255KB.
WriteConcern *writeconcern.WriteConcern // The write concern for the bucket. Defaults to the write concern of the database.
ReadConcern *readconcern.ReadConcern // The read concern for the bucket. Defaults to the read concern of the database.
ReadPreference *readpref.ReadPref // The read preference for the bucket. Defaults to the read preference of the database.
}
// GridFSBucket creates a new *BucketOptions
func GridFSBucket() *BucketOptions {
return &BucketOptions{
Name: &DefaultName,
ChunkSizeBytes: &DefaultChunkSize,
}
}
// SetName sets the name for the bucket. Defaults to "fs" if not set.
func (b *BucketOptions) SetName(name string) *BucketOptions {
b.Name = &name
return b
}
// SetChunkSizeBytes sets the chunk size in bytes for the bucket. Defaults to 255KB if not set.
func (b *BucketOptions) SetChunkSizeBytes(i int32) *BucketOptions {
b.ChunkSizeBytes = &i
return b
}
// SetWriteConcern sets the write concern for the bucket.
func (b *BucketOptions) SetWriteConcern(wc *writeconcern.WriteConcern) *BucketOptions {
b.WriteConcern = wc
return b
}
// SetReadConcern sets the read concern for the bucket.
func (b *BucketOptions) SetReadConcern(rc *readconcern.ReadConcern) *BucketOptions {
b.ReadConcern = rc
return b
}
// SetReadPreference sets the read preference for the bucket.
func (b *BucketOptions) SetReadPreference(rp *readpref.ReadPref) *BucketOptions {
b.ReadPreference = rp
return b
}
// MergeBucketOptions combines the given *BucketOptions into a single *BucketOptions.
// If the name or chunk size is not set in any of the given *BucketOptions, the resulting *BucketOptions will have
// name "fs" and chunk size 255KB.
func MergeBucketOptions(opts ...*BucketOptions) *BucketOptions {
b := GridFSBucket()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.Name != nil {
b.Name = opt.Name
}
if opt.ChunkSizeBytes != nil {
b.ChunkSizeBytes = opt.ChunkSizeBytes
}
if opt.WriteConcern != nil {
b.WriteConcern = opt.WriteConcern
}
if opt.ReadConcern != nil {
b.ReadConcern = opt.ReadConcern
}
if opt.ReadPreference != nil {
b.ReadPreference = opt.ReadPreference
}
}
return b
}
// UploadOptions represents all possible options for a GridFS upload operation. If a registry is nil, bson.DefaultRegistry
// will be used when converting the Metadata interface to BSON.
type UploadOptions struct {
ChunkSizeBytes *int32 // Chunk size in bytes. Defaults to the chunk size of the bucket.
Metadata interface{} // User data for the 'metadata' field of the files collection document.
Registry *bsoncodec.Registry // The registry to use for converting filters. Defaults to bson.DefaultRegistry.
}
// GridFSUpload creates a new *UploadOptions
func GridFSUpload() *UploadOptions {
return &UploadOptions{Registry: bson.DefaultRegistry}
}
// SetChunkSizeBytes sets the chunk size in bytes for the upload. Defaults to 255KB if not set.
func (u *UploadOptions) SetChunkSizeBytes(i int32) *UploadOptions {
u.ChunkSizeBytes = &i
return u
}
// SetMetadata specfies the metadata for the upload.
func (u *UploadOptions) SetMetadata(doc interface{}) *UploadOptions {
u.Metadata = doc
return u
}
// MergeUploadOptions combines the given *UploadOptions into a single *UploadOptions.
// If the chunk size is not set in any of the given *UploadOptions, the resulting *UploadOptions will have chunk size
// 255KB.
func MergeUploadOptions(opts ...*UploadOptions) *UploadOptions {
u := GridFSUpload()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.ChunkSizeBytes != nil {
u.ChunkSizeBytes = opt.ChunkSizeBytes
}
if opt.Metadata != nil {
u.Metadata = opt.Metadata
}
if opt.Registry != nil {
u.Registry = opt.Registry
}
}
return u
}
// NameOptions represents all options that can be used for a GridFS download by name operation.
type NameOptions struct {
Revision *int32 // Which revision (documents with the same filename and different uploadDate). Defaults to -1 (the most recent revision).
}
// GridFSName creates a new *NameOptions
func GridFSName() *NameOptions {
return &NameOptions{}
}
// SetRevision specifies which revision of the file to retrieve. Defaults to -1.
// * Revision numbers are defined as follows:
// * 0 = the original stored file
// * 1 = the first revision
// * 2 = the second revision
// * etc…
// * -2 = the second most recent revision
// * -1 = the most recent revision
func (n *NameOptions) SetRevision(r int32) *NameOptions {
n.Revision = &r
return n
}
// MergeNameOptions combines the given *NameOptions into a single *NameOptions in a last one wins fashion.
func MergeNameOptions(opts ...*NameOptions) *NameOptions {
n := GridFSName()
n.Revision = &DefaultRevision
for _, opt := range opts {
if opt == nil {
continue
}
if opt.Revision != nil {
n.Revision = opt.Revision
}
}
return n
}
// GridFSFindOptions represents all options for a GridFS find operation.
type GridFSFindOptions struct {
BatchSize *int32
Limit *int32
MaxTime *time.Duration
NoCursorTimeout *bool
Skip *int32
Sort interface{}
}
// GridFSFind creates a new GridFSFindOptions instance.
func GridFSFind() *GridFSFindOptions {
return &GridFSFindOptions{}
}
// SetBatchSize sets the number of documents to return in each batch.
func (f *GridFSFindOptions) SetBatchSize(i int32) *GridFSFindOptions {
f.BatchSize = &i
return f
}
// SetLimit specifies a limit on the number of results.
// A negative limit implies that only 1 batch should be returned.
func (f *GridFSFindOptions) SetLimit(i int32) *GridFSFindOptions {
f.Limit = &i
return f
}
// SetMaxTime specifies the max time to allow the query to run.
func (f *GridFSFindOptions) SetMaxTime(d time.Duration) *GridFSFindOptions {
f.MaxTime = &d
return f
}
// SetNoCursorTimeout specifies whether or not cursors should time out after a period of inactivity.
func (f *GridFSFindOptions) SetNoCursorTimeout(b bool) *GridFSFindOptions {
f.NoCursorTimeout = &b
return f
}
// SetSkip specifies the number of documents to skip before returning.
func (f *GridFSFindOptions) SetSkip(i int32) *GridFSFindOptions {
f.Skip = &i
return f
}
// SetSort specifies the order in which to return documents.
func (f *GridFSFindOptions) SetSort(sort interface{}) *GridFSFindOptions {
f.Sort = sort
return f
}
// MergeGridFSFindOptions combines the argued GridFSFindOptions into a single GridFSFindOptions in a last-one-wins fashion
func MergeGridFSFindOptions(opts ...*GridFSFindOptions) *GridFSFindOptions {
fo := GridFSFind()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.BatchSize != nil {
fo.BatchSize = opt.BatchSize
}
if opt.Limit != nil {
fo.Limit = opt.Limit
}
if opt.MaxTime != nil {
fo.MaxTime = opt.MaxTime
}
if opt.NoCursorTimeout != nil {
fo.NoCursorTimeout = opt.NoCursorTimeout
}
if opt.Skip != nil {
fo.Skip = opt.Skip
}
if opt.Sort != nil {
fo.Sort = opt.Sort
}
}
return fo
}

View File

@@ -0,0 +1,336 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import (
"time"
)
// CreateIndexesOptions represents all possible options for the CreateOne() and CreateMany() functions.
type CreateIndexesOptions struct {
MaxTime *time.Duration // The maximum amount of time to allow the query to run.
}
// CreateIndexes creates a new CreateIndexesOptions instance.
func CreateIndexes() *CreateIndexesOptions {
return &CreateIndexesOptions{}
}
// SetMaxTime specifies the maximum amount of time to allow the query to run.
func (c *CreateIndexesOptions) SetMaxTime(d time.Duration) *CreateIndexesOptions {
c.MaxTime = &d
return c
}
// MergeCreateIndexesOptions combines the given *CreateIndexesOptions into a single *CreateIndexesOptions in a last one
// wins fashion.
func MergeCreateIndexesOptions(opts ...*CreateIndexesOptions) *CreateIndexesOptions {
c := CreateIndexes()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.MaxTime != nil {
c.MaxTime = opt.MaxTime
}
}
return c
}
// DropIndexesOptions represents all possible options for the DropIndexes() function.
type DropIndexesOptions struct {
MaxTime *time.Duration
}
// DropIndexes creates a new DropIndexesOptions instance.
func DropIndexes() *DropIndexesOptions {
return &DropIndexesOptions{}
}
// SetMaxTime specifies the maximum amount of time to allow the query to run.
func (d *DropIndexesOptions) SetMaxTime(duration time.Duration) *DropIndexesOptions {
d.MaxTime = &duration
return d
}
// MergeDropIndexesOptions combines the given *DropIndexesOptions into a single *DropIndexesOptions in a last one
// wins fashion.
func MergeDropIndexesOptions(opts ...*DropIndexesOptions) *DropIndexesOptions {
c := DropIndexes()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.MaxTime != nil {
c.MaxTime = opt.MaxTime
}
}
return c
}
// ListIndexesOptions represents all possible options for the ListIndexes() function.
type ListIndexesOptions struct {
BatchSize *int32
MaxTime *time.Duration
}
// ListIndexes creates a new ListIndexesOptions instance.
func ListIndexes() *ListIndexesOptions {
return &ListIndexesOptions{}
}
// SetBatchSize specifies the number of documents to return in every batch.
func (l *ListIndexesOptions) SetBatchSize(i int32) *ListIndexesOptions {
l.BatchSize = &i
return l
}
// SetMaxTime specifies the maximum amount of time to allow the query to run.
func (l *ListIndexesOptions) SetMaxTime(d time.Duration) *ListIndexesOptions {
l.MaxTime = &d
return l
}
// MergeListIndexesOptions combines the given *ListIndexesOptions into a single *ListIndexesOptions in a last one
// wins fashion.
func MergeListIndexesOptions(opts ...*ListIndexesOptions) *ListIndexesOptions {
c := ListIndexes()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.MaxTime != nil {
c.MaxTime = opt.MaxTime
}
}
return c
}
// IndexOptions represents all possible options to configure a new index.
type IndexOptions struct {
Background *bool
ExpireAfterSeconds *int32
Name *string
Sparse *bool
StorageEngine interface{}
Unique *bool
Version *int32
DefaultLanguage *string
LanguageOverride *string
TextVersion *int32
Weights interface{}
SphereVersion *int32
Bits *int32
Max *float64
Min *float64
BucketSize *int32
PartialFilterExpression interface{}
Collation *Collation
WildcardProjection interface{}
}
// Index creates a new *IndexOptions
func Index() *IndexOptions {
return &IndexOptions{}
}
// SetBackground sets the background option. If true, the server will create the index in the background and not block
// other tasks
func (i *IndexOptions) SetBackground(background bool) *IndexOptions {
i.Background = &background
return i
}
// SetExpireAfterSeconds specifies the number of seconds for a document to remain in a collection.
func (i *IndexOptions) SetExpireAfterSeconds(seconds int32) *IndexOptions {
i.ExpireAfterSeconds = &seconds
return i
}
// SetName specifies a name for the index.
// If not set, a name will be generated in the format "[field]_[direction]".
// If multiple indexes are created for the same key pattern with different collations, a name must be provided to avoid
// ambiguity.
func (i *IndexOptions) SetName(name string) *IndexOptions {
i.Name = &name
return i
}
// SetSparse sets the sparse option.
// If true, the index will only reference documents with the specified field in the index.
func (i *IndexOptions) SetSparse(sparse bool) *IndexOptions {
i.Sparse = &sparse
return i
}
// SetStorageEngine specifies the storage engine to use.
// Valid for server versions >= 3.0
func (i *IndexOptions) SetStorageEngine(engine interface{}) *IndexOptions {
i.StorageEngine = engine
return i
}
// SetUnique forces the index to be unique.
func (i *IndexOptions) SetUnique(unique bool) *IndexOptions {
i.Unique = &unique
return i
}
// SetVersion specifies the index version number, either 0 or 1.
func (i *IndexOptions) SetVersion(version int32) *IndexOptions {
i.Version = &version
return i
}
// SetDefaultLanguage specifies the default language for text indexes.
// If not set, this will default to english.
func (i *IndexOptions) SetDefaultLanguage(language string) *IndexOptions {
i.DefaultLanguage = &language
return i
}
// SetLanguageOverride specifies the field in the document to override the language.
func (i *IndexOptions) SetLanguageOverride(override string) *IndexOptions {
i.LanguageOverride = &override
return i
}
// SetTextVersion specifies the text index version number.
// MongoDB version 2.4 can only support version 1.
// MongoDB versions 2.6 and higher can support versions 1 or 2.
func (i *IndexOptions) SetTextVersion(version int32) *IndexOptions {
i.TextVersion = &version
return i
}
// SetWeights specifies fields in the index and their corresponding weight values.
func (i *IndexOptions) SetWeights(weights interface{}) *IndexOptions {
i.Weights = weights
return i
}
// SetSphereVersion specifies the 2dsphere index version number.
// MongoDB version 2.4 can only support version 1.
// MongoDB versions 2.6 and higher can support versions 1 or 2.
func (i *IndexOptions) SetSphereVersion(version int32) *IndexOptions {
i.SphereVersion = &version
return i
}
// SetBits specifies the precision of the stored geo hash in the 2d index, from 1 to 32.
func (i *IndexOptions) SetBits(bits int32) *IndexOptions {
i.Bits = &bits
return i
}
// SetMax specifies the maximum boundary for latitude and longitude in the 2d index.
func (i *IndexOptions) SetMax(max float64) *IndexOptions {
i.Max = &max
return i
}
// SetMin specifies the minimum boundary for latitude and longitude in the 2d index.
func (i *IndexOptions) SetMin(min float64) *IndexOptions {
i.Min = &min
return i
}
// SetBucketSize specifies number of units within which to group the location values in a geo haystack index.
func (i *IndexOptions) SetBucketSize(bucketSize int32) *IndexOptions {
i.BucketSize = &bucketSize
return i
}
// SetPartialFilterExpression specifies a filter for use in a partial index. Only documents that match the filter
// expression are included in the index.
func (i *IndexOptions) SetPartialFilterExpression(expression interface{}) *IndexOptions {
i.PartialFilterExpression = expression
return i
}
// SetCollation specifies a Collation to use for the operation.
// Valid for server versions >= 3.4
func (i *IndexOptions) SetCollation(collation *Collation) *IndexOptions {
i.Collation = collation
return i
}
// SetWildcardProjection specifies a wildcard projection for a wildcard index.
func (i *IndexOptions) SetWildcardProjection(wildcardProjection interface{}) *IndexOptions {
i.WildcardProjection = wildcardProjection
return i
}
// MergeIndexOptions combines the given *IndexOptions into a single *IndexOptions in a last one wins fashion.
func MergeIndexOptions(opts ...*IndexOptions) *IndexOptions {
i := Index()
for _, opt := range opts {
if opt.Background != nil {
i.Background = opt.Background
}
if opt.ExpireAfterSeconds != nil {
i.ExpireAfterSeconds = opt.ExpireAfterSeconds
}
if opt.Name != nil {
i.Name = opt.Name
}
if opt.Sparse != nil {
i.Sparse = opt.Sparse
}
if opt.StorageEngine != nil {
i.StorageEngine = opt.StorageEngine
}
if opt.Unique != nil {
i.Unique = opt.Unique
}
if opt.Version != nil {
i.Version = opt.Version
}
if opt.DefaultLanguage != nil {
i.DefaultLanguage = opt.DefaultLanguage
}
if opt.LanguageOverride != nil {
i.LanguageOverride = opt.LanguageOverride
}
if opt.TextVersion != nil {
i.TextVersion = opt.TextVersion
}
if opt.Weights != nil {
i.Weights = opt.Weights
}
if opt.SphereVersion != nil {
i.SphereVersion = opt.SphereVersion
}
if opt.Bits != nil {
i.Bits = opt.Bits
}
if opt.Max != nil {
i.Max = opt.Max
}
if opt.Min != nil {
i.Min = opt.Min
}
if opt.BucketSize != nil {
i.BucketSize = opt.BucketSize
}
if opt.PartialFilterExpression != nil {
i.PartialFilterExpression = opt.PartialFilterExpression
}
if opt.Collation != nil {
i.Collation = opt.Collation
}
if opt.WildcardProjection != nil {
i.WildcardProjection = opt.WildcardProjection
}
}
return i
}

View File

@@ -0,0 +1,84 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
// InsertOneOptions represents all possible options to the InsertOne() function.
type InsertOneOptions struct {
BypassDocumentValidation *bool // If true, allows the write to opt-out of document level validation
}
// InsertOne returns a pointer to a new InsertOneOptions
func InsertOne() *InsertOneOptions {
return &InsertOneOptions{}
}
// SetBypassDocumentValidation allows the write to opt-out of document level validation.
// Valid for server versions >= 3.2. For servers < 3.2, this option is ignored.
func (ioo *InsertOneOptions) SetBypassDocumentValidation(b bool) *InsertOneOptions {
ioo.BypassDocumentValidation = &b
return ioo
}
// MergeInsertOneOptions combines the argued InsertOneOptions into a single InsertOneOptions in a last-one-wins fashion
func MergeInsertOneOptions(opts ...*InsertOneOptions) *InsertOneOptions {
ioOpts := InsertOne()
for _, ioo := range opts {
if ioo == nil {
continue
}
if ioo.BypassDocumentValidation != nil {
ioOpts.BypassDocumentValidation = ioo.BypassDocumentValidation
}
}
return ioOpts
}
// InsertManyOptions represents all possible options to the InsertMany() function.
type InsertManyOptions struct {
BypassDocumentValidation *bool // If true, allows the write to opt-out of document level validation
Ordered *bool // If true, when an insert fails, return without performing the remaining inserts. Defaults to true.
}
// InsertMany returns a pointer to a new InsertManyOptions
func InsertMany() *InsertManyOptions {
return &InsertManyOptions{
Ordered: &DefaultOrdered,
}
}
// SetBypassDocumentValidation allows the write to opt-out of document level validation.
// Valid for server versions >= 3.2. For servers < 3.2, this option is ignored.
func (imo *InsertManyOptions) SetBypassDocumentValidation(b bool) *InsertManyOptions {
imo.BypassDocumentValidation = &b
return imo
}
// SetOrdered configures the ordered option. If true, when a write fails, the function will return without attempting
// remaining writes. Defaults to true.
func (imo *InsertManyOptions) SetOrdered(b bool) *InsertManyOptions {
imo.Ordered = &b
return imo
}
// MergeInsertManyOptions combines the argued InsertManyOptions into a single InsertManyOptions in a last-one-wins fashion
func MergeInsertManyOptions(opts ...*InsertManyOptions) *InsertManyOptions {
imOpts := InsertMany()
for _, imo := range opts {
if imo == nil {
continue
}
if imo.BypassDocumentValidation != nil {
imOpts.BypassDocumentValidation = imo.BypassDocumentValidation
}
if imo.Ordered != nil {
imOpts.Ordered = imo.Ordered
}
}
return imOpts
}

View File

@@ -0,0 +1,39 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
// ListCollectionsOptions represents all possible options for a listCollections command.
type ListCollectionsOptions struct {
NameOnly *bool // If true, only the collection names will be returned.
}
// ListCollections creates a new *ListCollectionsOptions
func ListCollections() *ListCollectionsOptions {
return &ListCollectionsOptions{}
}
// SetNameOnly specifies whether to return only the collection names.
func (lc *ListCollectionsOptions) SetNameOnly(b bool) *ListCollectionsOptions {
lc.NameOnly = &b
return lc
}
// MergeListCollectionsOptions combines the given *ListCollectionsOptions into a single *ListCollectionsOptions in a
// last one wins fashion.
func MergeListCollectionsOptions(opts ...*ListCollectionsOptions) *ListCollectionsOptions {
lc := ListCollections()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.NameOnly != nil {
lc.NameOnly = opt.NameOnly
}
}
return lc
}

View File

@@ -0,0 +1,39 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
// ListDatabasesOptions represents all possible options for a listDatabases command.
type ListDatabasesOptions struct {
NameOnly *bool // If true, only the database names will be returned.
}
// ListDatabases creates a new *ListDatabasesOptions
func ListDatabases() *ListDatabasesOptions {
return &ListDatabasesOptions{}
}
// SetNameOnly specifies whether to return only the database names.
func (ld *ListDatabasesOptions) SetNameOnly(b bool) *ListDatabasesOptions {
ld.NameOnly = &b
return ld
}
// MergeListDatabasesOptions combines the given *ListDatabasesOptions into a single *ListDatabasesOptions in a last one
// wins fashion.
func MergeListDatabasesOptions(opts ...*ListDatabasesOptions) *ListDatabasesOptions {
ld := ListDatabases()
for _, opt := range opts {
if opts == nil {
continue
}
if opt.NameOnly != nil {
ld.NameOnly = opt.NameOnly
}
}
return ld
}

View File

@@ -0,0 +1,161 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import (
"fmt"
"reflect"
"strconv"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)
// Collation allows users to specify language-specific rules for string comparison, such as
// rules for lettercase and accent marks.
type Collation struct {
Locale string `bson:",omitempty"` // The locale
CaseLevel bool `bson:",omitempty"` // The case level
CaseFirst string `bson:",omitempty"` // The case ordering
Strength int `bson:",omitempty"` // The number of comparision levels to use
NumericOrdering bool `bson:",omitempty"` // Whether to order numbers based on numerical order and not collation order
Alternate string `bson:",omitempty"` // Whether spaces and punctuation are considered base characters
MaxVariable string `bson:",omitempty"` // Which characters are affected by alternate: "shifted"
Normalization bool `bson:",omitempty"` // Causes text to be normalized into Unicode NFD
Backwards bool `bson:",omitempty"` // Causes secondary differences to be considered in reverse order, as it is done in the French language
}
// ToDocument converts the Collation to a bson.Raw.
func (co *Collation) ToDocument() bson.Raw {
idx, doc := bsoncore.AppendDocumentStart(nil)
if co.Locale != "" {
doc = bsoncore.AppendStringElement(doc, "locale", co.Locale)
}
if co.CaseLevel {
doc = bsoncore.AppendBooleanElement(doc, "caseLevel", true)
}
if co.CaseFirst != "" {
doc = bsoncore.AppendStringElement(doc, "caseFirst", co.CaseFirst)
}
if co.Strength != 0 {
doc = bsoncore.AppendInt32Element(doc, "strength", int32(co.Strength))
}
if co.NumericOrdering {
doc = bsoncore.AppendBooleanElement(doc, "numericOrdering", true)
}
if co.Alternate != "" {
doc = bsoncore.AppendStringElement(doc, "alternate", co.Alternate)
}
if co.MaxVariable != "" {
doc = bsoncore.AppendStringElement(doc, "maxVariable", co.MaxVariable)
}
if co.Normalization {
doc = bsoncore.AppendBooleanElement(doc, "normalization", true)
}
if co.Backwards {
doc = bsoncore.AppendBooleanElement(doc, "backwards", true)
}
doc, _ = bsoncore.AppendDocumentEnd(doc, idx)
return doc
}
// CursorType specifies whether a cursor should close when the last data is retrieved. See
// NonTailable, Tailable, and TailableAwait.
type CursorType int8
const (
// NonTailable specifies that a cursor should close after retrieving the last data.
NonTailable CursorType = iota
// Tailable specifies that a cursor should not close when the last data is retrieved and can be resumed later.
Tailable
// TailableAwait specifies that a cursor should not close when the last data is retrieved and
// that it should block for a certain amount of time for new data before returning no data.
TailableAwait
)
// ReturnDocument specifies whether a findAndUpdate operation should return the document as it was
// before the update or as it is after the update.
type ReturnDocument int8
const (
// Before specifies that findAndUpdate should return the document as it was before the update.
Before ReturnDocument = iota
// After specifies that findAndUpdate should return the document as it is after the update.
After
)
// FullDocument specifies whether a change stream should include a copy of the entire document that was changed from
// some time after the change occurred.
type FullDocument string
const (
// Default does not include a document copy
Default FullDocument = "default"
// UpdateLookup includes a delta describing the changes to the document and a copy of the entire document that
// was changed
UpdateLookup FullDocument = "updateLookup"
)
// ArrayFilters is used to hold filters for the array filters CRUD option. If a registry is nil, bson.DefaultRegistry
// will be used when converting the filter interfaces to BSON.
type ArrayFilters struct {
Registry *bsoncodec.Registry // The registry to use for converting filters. Defaults to bson.DefaultRegistry.
Filters []interface{} // The filters to apply
}
// ToArray builds a []bson.Raw from the provided ArrayFilters.
func (af *ArrayFilters) ToArray() ([]bson.Raw, error) {
registry := af.Registry
if registry == nil {
registry = bson.DefaultRegistry
}
filters := make([]bson.Raw, 0, len(af.Filters))
for _, f := range af.Filters {
filter, err := bson.MarshalWithRegistry(registry, f)
if err != nil {
return nil, err
}
filters = append(filters, filter)
}
return filters, nil
}
// ToArrayDocument builds a BSON array for the array filters CRUD option. If the registry for af is nil,
// bson.DefaultRegistry will be used when converting the filter interfaces to BSON.
func (af *ArrayFilters) ToArrayDocument() (bson.Raw, error) {
registry := af.Registry
if registry == nil {
registry = bson.DefaultRegistry
}
idx, arr := bsoncore.AppendArrayStart(nil)
for i, f := range af.Filters {
filter, err := bson.MarshalWithRegistry(registry, f)
if err != nil {
return nil, err
}
arr = bsoncore.AppendDocumentElement(arr, strconv.Itoa(i), filter)
}
arr, _ = bsoncore.AppendArrayEnd(arr, idx)
return arr, nil
}
// MarshalError is returned when attempting to transform a value into a document
// results in an error.
type MarshalError struct {
Value interface{}
Err error
}
// Error implements the error interface.
func (me MarshalError) Error() string {
return fmt.Sprintf("cannot transform type %s to a bson.Raw", reflect.TypeOf(me.Value))
}
var defaultRegistry = bson.DefaultRegistry

View File

@@ -0,0 +1,60 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
// ReplaceOptions represents all possible options to the ReplaceOne() function.
type ReplaceOptions struct {
BypassDocumentValidation *bool // If true, allows the write to opt-out of document level validation
Collation *Collation // Specifies a collation
Upsert *bool // When true, creates a new document if no document matches the query
}
// Replace returns a pointer to a new ReplaceOptions
func Replace() *ReplaceOptions {
return &ReplaceOptions{}
}
// SetBypassDocumentValidation allows the write to opt-out of document level validation.
// Valid for server versions >= 3.2. For servers < 3.2, this option is ignored.
func (ro *ReplaceOptions) SetBypassDocumentValidation(b bool) *ReplaceOptions {
ro.BypassDocumentValidation = &b
return ro
}
// SetCollation specifies a collation.
// Valid for servers >= 3.4
func (ro *ReplaceOptions) SetCollation(c *Collation) *ReplaceOptions {
ro.Collation = c
return ro
}
// SetUpsert allows the creation of a new document if not document matches the query
func (ro *ReplaceOptions) SetUpsert(b bool) *ReplaceOptions {
ro.Upsert = &b
return ro
}
// MergeReplaceOptions combines the argued ReplaceOptions into a single ReplaceOptions in a last-one-wins fashion
func MergeReplaceOptions(opts ...*ReplaceOptions) *ReplaceOptions {
rOpts := Replace()
for _, ro := range opts {
if ro == nil {
continue
}
if ro.BypassDocumentValidation != nil {
rOpts.BypassDocumentValidation = ro.BypassDocumentValidation
}
if ro.Collation != nil {
rOpts.Collation = ro.Collation
}
if ro.Upsert != nil {
rOpts.Upsert = ro.Upsert
}
}
return rOpts
}

View File

@@ -0,0 +1,40 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import "go.mongodb.org/mongo-driver/mongo/readpref"
// RunCmdOptions represents all possible options for a runCommand operation.
type RunCmdOptions struct {
ReadPreference *readpref.ReadPref // The read preference for the operation.
}
// RunCmd creates a new *RunCmdOptions
func RunCmd() *RunCmdOptions {
return &RunCmdOptions{}
}
// SetReadPreference sets the read preference for the operation.
func (rc *RunCmdOptions) SetReadPreference(rp *readpref.ReadPref) *RunCmdOptions {
rc.ReadPreference = rp
return rc
}
// MergeRunCmdOptions combines the given *RunCmdOptions into one *RunCmdOptions in a last one wins fashion.
func MergeRunCmdOptions(opts ...*RunCmdOptions) *RunCmdOptions {
rc := RunCmd()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.ReadPreference != nil {
rc.ReadPreference = opt.ReadPreference
}
}
return rc
}

View File

@@ -0,0 +1,79 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import (
"go.mongodb.org/mongo-driver/mongo/readconcern"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
)
// DefaultCausalConsistency is the default value for the CausalConsistency option.
var DefaultCausalConsistency = true
// SessionOptions represents all possible options for creating a new session.
type SessionOptions struct {
CausalConsistency *bool // Specifies if reads should be causally consistent. Defaults to true.
DefaultReadConcern *readconcern.ReadConcern // The default read concern for transactions started in the session.
DefaultReadPreference *readpref.ReadPref // The default read preference for transactions started in the session.
DefaultWriteConcern *writeconcern.WriteConcern // The default write concern for transactions started in the session.
}
// Session creates a new *SessionOptions
func Session() *SessionOptions {
return &SessionOptions{
CausalConsistency: &DefaultCausalConsistency,
}
}
// SetCausalConsistency specifies if a session should be causally consistent. Defaults to true.
func (s *SessionOptions) SetCausalConsistency(b bool) *SessionOptions {
s.CausalConsistency = &b
return s
}
// SetDefaultReadConcern sets the default read concern for transactions started in a session.
func (s *SessionOptions) SetDefaultReadConcern(rc *readconcern.ReadConcern) *SessionOptions {
s.DefaultReadConcern = rc
return s
}
// SetDefaultReadPreference sets the default read preference for transactions started in a session.
func (s *SessionOptions) SetDefaultReadPreference(rp *readpref.ReadPref) *SessionOptions {
s.DefaultReadPreference = rp
return s
}
// SetDefaultWriteConcern sets the default write concern for transactions started in a session.
func (s *SessionOptions) SetDefaultWriteConcern(wc *writeconcern.WriteConcern) *SessionOptions {
s.DefaultWriteConcern = wc
return s
}
// MergeSessionOptions combines the given *SessionOptions into a single *SessionOptions in a last one wins fashion.
func MergeSessionOptions(opts ...*SessionOptions) *SessionOptions {
s := Session()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.CausalConsistency != nil {
s.CausalConsistency = opt.CausalConsistency
}
if opt.DefaultReadConcern != nil {
s.DefaultReadConcern = opt.DefaultReadConcern
}
if opt.DefaultReadPreference != nil {
s.DefaultReadPreference = opt.DefaultReadPreference
}
if opt.DefaultWriteConcern != nil {
s.DefaultWriteConcern = opt.DefaultWriteConcern
}
}
return s
}

View File

@@ -0,0 +1,65 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
import (
"go.mongodb.org/mongo-driver/mongo/readconcern"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/mongo/writeconcern"
)
// TransactionOptions represents all possible options for starting a transaction.
type TransactionOptions struct {
ReadConcern *readconcern.ReadConcern // The read concern for the transaction. Defaults to the session's read concern.
ReadPreference *readpref.ReadPref // The read preference for the transaction. Defaults to the session's read preference.
WriteConcern *writeconcern.WriteConcern // The write concern for the transaction. Defaults to the session's write concern.
}
// Transaction creates a new *TransactionOptions
func Transaction() *TransactionOptions {
return &TransactionOptions{}
}
// SetReadConcern sets the read concern for the transaction.
func (t *TransactionOptions) SetReadConcern(rc *readconcern.ReadConcern) *TransactionOptions {
t.ReadConcern = rc
return t
}
// SetReadPreference sets the read preference for the transaction.
func (t *TransactionOptions) SetReadPreference(rp *readpref.ReadPref) *TransactionOptions {
t.ReadPreference = rp
return t
}
// SetWriteConcern sets the write concern for the transaction.
func (t *TransactionOptions) SetWriteConcern(wc *writeconcern.WriteConcern) *TransactionOptions {
t.WriteConcern = wc
return t
}
// MergeTransactionOptions combines the given *TransactionOptions into a single *TransactionOptions in a last one wins
// fashion.
func MergeTransactionOptions(opts ...*TransactionOptions) *TransactionOptions {
t := Transaction()
for _, opt := range opts {
if opt == nil {
continue
}
if opt.ReadConcern != nil {
t.ReadConcern = opt.ReadConcern
}
if opt.ReadPreference != nil {
t.ReadPreference = opt.ReadPreference
}
if opt.WriteConcern != nil {
t.WriteConcern = opt.WriteConcern
}
}
return t
}

View File

@@ -0,0 +1,71 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package options
// UpdateOptions represents all possible options to the UpdateOne() and UpdateMany() functions.
type UpdateOptions struct {
ArrayFilters *ArrayFilters // A set of filters specifying to which array elements an update should apply
BypassDocumentValidation *bool // If true, allows the write to opt-out of document level validation
Collation *Collation // Specifies a collation
Upsert *bool // When true, creates a new document if no document matches the query
}
// Update returns a pointer to a new UpdateOptions
func Update() *UpdateOptions {
return &UpdateOptions{}
}
// SetArrayFilters specifies a set of filters specifying to which array elements an update should apply
// Valid for server versions >= 3.6.
func (uo *UpdateOptions) SetArrayFilters(af ArrayFilters) *UpdateOptions {
uo.ArrayFilters = &af
return uo
}
// SetBypassDocumentValidation allows the write to opt-out of document level validation.
// Valid for server versions >= 3.2. For servers < 3.2, this option is ignored.
func (uo *UpdateOptions) SetBypassDocumentValidation(b bool) *UpdateOptions {
uo.BypassDocumentValidation = &b
return uo
}
// SetCollation specifies a collation.
// Valid for server versions >= 3.4.
func (uo *UpdateOptions) SetCollation(c *Collation) *UpdateOptions {
uo.Collation = c
return uo
}
// SetUpsert allows the creation of a new document if not document matches the query
func (uo *UpdateOptions) SetUpsert(b bool) *UpdateOptions {
uo.Upsert = &b
return uo
}
// MergeUpdateOptions combines the argued UpdateOptions into a single UpdateOptions in a last-one-wins fashion
func MergeUpdateOptions(opts ...*UpdateOptions) *UpdateOptions {
uOpts := Update()
for _, uo := range opts {
if uo == nil {
continue
}
if uo.ArrayFilters != nil {
uOpts.ArrayFilters = uo.ArrayFilters
}
if uo.BypassDocumentValidation != nil {
uOpts.BypassDocumentValidation = uo.BypassDocumentValidation
}
if uo.Collation != nil {
uOpts.Collation = uo.Collation
}
if uo.Upsert != nil {
uOpts.Upsert = uo.Upsert
}
}
return uOpts
}

View File

@@ -0,0 +1,77 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package readconcern // import "go.mongodb.org/mongo-driver/mongo/readconcern"
import (
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)
// ReadConcern for replica sets and replica set shards determines which data to return from a query.
type ReadConcern struct {
level string
}
// Option is an option to provide when creating a ReadConcern.
type Option func(concern *ReadConcern)
// Level creates an option that sets the level of a ReadConcern.
func Level(level string) Option {
return func(concern *ReadConcern) {
concern.level = level
}
}
// Local specifies that the query should return the instances most recent data.
func Local() *ReadConcern {
return New(Level("local"))
}
// Majority specifies that the query should return the instances most recent data acknowledged as
// having been written to a majority of members in the replica set.
func Majority() *ReadConcern {
return New(Level("majority"))
}
// Linearizable specifies that the query should return data that reflects all successful writes
// issued with a write concern of "majority" and acknowledged prior to the start of the read operation.
func Linearizable() *ReadConcern {
return New(Level("linearizable"))
}
// Available specifies that the query should return data from the instance with no guarantee
// that the data has been written to a majority of the replica set members (i.e. may be rolled back).
func Available() *ReadConcern {
return New(Level("available"))
}
// Snapshot is only available for operations within multi-document transactions.
func Snapshot() *ReadConcern {
return New(Level("snapshot"))
}
// New constructs a new read concern from the given string.
func New(options ...Option) *ReadConcern {
concern := &ReadConcern{}
for _, option := range options {
option(concern)
}
return concern
}
// MarshalBSONValue implements the bson.ValueMarshaler interface.
func (rc *ReadConcern) MarshalBSONValue() (bsontype.Type, []byte, error) {
var elems []byte
if len(rc.level) > 0 {
elems = bsoncore.AppendStringElement(elems, "level", rc.level)
}
return bsontype.EmbeddedDocument, bsoncore.BuildDocument(nil, elems), nil
}

56
vendor/go.mongodb.org/mongo-driver/mongo/readpref/mode.go generated vendored Executable file
View File

@@ -0,0 +1,56 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package readpref
import (
"fmt"
"strings"
)
// Mode indicates the user's preference on reads.
type Mode uint8
// Mode constants
const (
_ Mode = iota
// PrimaryMode indicates that only a primary is
// considered for reading. This is the default
// mode.
PrimaryMode
// PrimaryPreferredMode indicates that if a primary
// is available, use it; otherwise, eligible
// secondaries will be considered.
PrimaryPreferredMode
// SecondaryMode indicates that only secondaries
// should be considered.
SecondaryMode
// SecondaryPreferredMode indicates that only secondaries
// should be considered when one is available. If none
// are available, then a primary will be considered.
SecondaryPreferredMode
// NearestMode indicates that all primaries and secondaries
// will be considered.
NearestMode
)
// ModeFromString returns a mode corresponding to
// mode.
func ModeFromString(mode string) (Mode, error) {
switch strings.ToLower(mode) {
case "primary":
return PrimaryMode, nil
case "primarypreferred":
return PrimaryPreferredMode, nil
case "secondary":
return SecondaryMode, nil
case "secondarypreferred":
return SecondaryPreferredMode, nil
case "nearest":
return NearestMode, nil
}
return Mode(0), fmt.Errorf("unknown read preference %v", mode)
}

60
vendor/go.mongodb.org/mongo-driver/mongo/readpref/options.go generated vendored Executable file
View File

@@ -0,0 +1,60 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package readpref
import (
"errors"
"time"
"go.mongodb.org/mongo-driver/tag"
)
// ErrInvalidTagSet indicates that an invalid set of tags was specified.
var ErrInvalidTagSet = errors.New("an even number of tags must be specified")
// Option configures a read preference
type Option func(*ReadPref) error
// WithMaxStaleness sets the maximum staleness a
// server is allowed.
func WithMaxStaleness(ms time.Duration) Option {
return func(rp *ReadPref) error {
rp.maxStaleness = ms
rp.maxStalenessSet = true
return nil
}
}
// WithTags sets a single tag set used to match
// a server. The last call to WithTags or WithTagSets
// overrides all previous calls to either method.
func WithTags(tags ...string) Option {
return func(rp *ReadPref) error {
length := len(tags)
if length < 2 || length%2 != 0 {
return ErrInvalidTagSet
}
tagset := make(tag.Set, 0, length/2)
for i := 1; i < length; i += 2 {
tagset = append(tagset, tag.Tag{Name: tags[i-1], Value: tags[i]})
}
return WithTagSets(tagset)(rp)
}
}
// WithTagSets sets the tag sets used to match
// a server. The last call to WithTags or WithTagSets
// overrides all previous calls to either method.
func WithTagSets(tagSets ...tag.Set) Option {
return func(rp *ReadPref) error {
rp.tagSets = tagSets
return nil
}
}

View File

@@ -0,0 +1,99 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package readpref // import "go.mongodb.org/mongo-driver/mongo/readpref"
import (
"errors"
"time"
"go.mongodb.org/mongo-driver/tag"
)
var (
errInvalidReadPreference = errors.New("can not specify tags or max staleness on primary")
)
var primary = ReadPref{mode: PrimaryMode}
// Primary constructs a read preference with a PrimaryMode.
func Primary() *ReadPref {
return &primary
}
// PrimaryPreferred constructs a read preference with a PrimaryPreferredMode.
func PrimaryPreferred(opts ...Option) *ReadPref {
// New only returns an error with a mode of Primary
rp, _ := New(PrimaryPreferredMode, opts...)
return rp
}
// SecondaryPreferred constructs a read preference with a SecondaryPreferredMode.
func SecondaryPreferred(opts ...Option) *ReadPref {
// New only returns an error with a mode of Primary
rp, _ := New(SecondaryPreferredMode, opts...)
return rp
}
// Secondary constructs a read preference with a SecondaryMode.
func Secondary(opts ...Option) *ReadPref {
// New only returns an error with a mode of Primary
rp, _ := New(SecondaryMode, opts...)
return rp
}
// Nearest constructs a read preference with a NearestMode.
func Nearest(opts ...Option) *ReadPref {
// New only returns an error with a mode of Primary
rp, _ := New(NearestMode, opts...)
return rp
}
// New creates a new ReadPref.
func New(mode Mode, opts ...Option) (*ReadPref, error) {
rp := &ReadPref{
mode: mode,
}
if mode == PrimaryMode && len(opts) != 0 {
return nil, errInvalidReadPreference
}
for _, opt := range opts {
err := opt(rp)
if err != nil {
return nil, err
}
}
return rp, nil
}
// ReadPref determines which servers are considered suitable for read operations.
type ReadPref struct {
maxStaleness time.Duration
maxStalenessSet bool
mode Mode
tagSets []tag.Set
}
// MaxStaleness is the maximum amount of time to allow
// a server to be considered eligible for selection. The
// second return value indicates if this value has been set.
func (r *ReadPref) MaxStaleness() (time.Duration, bool) {
return r.maxStaleness, r.maxStalenessSet
}
// Mode indicates the mode of the read preference.
func (r *ReadPref) Mode() Mode {
return r.mode
}
// TagSets are multiple tag sets indicating
// which servers should be considered.
func (r *ReadPref) TagSets() []tag.Set {
return r.tagSets
}

140
vendor/go.mongodb.org/mongo-driver/mongo/results.go generated vendored Executable file
View File

@@ -0,0 +1,140 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
)
// BulkWriteResult holds the result of a bulk write operation.
type BulkWriteResult struct {
InsertedCount int64
MatchedCount int64
ModifiedCount int64
DeletedCount int64
UpsertedCount int64
UpsertedIDs map[int64]interface{}
}
// InsertOneResult is a result of an InsertOne operation.
//
// InsertedID will be a Go type that corresponds to a BSON type.
type InsertOneResult struct {
// The identifier that was inserted.
InsertedID interface{}
}
// InsertManyResult is a result of an InsertMany operation.
type InsertManyResult struct {
// Maps the indexes of inserted documents to their _id fields.
InsertedIDs []interface{}
}
// DeleteResult is a result of an DeleteOne operation.
type DeleteResult struct {
// The number of documents that were deleted.
DeletedCount int64 `bson:"n"`
}
// ListDatabasesResult is a result of a ListDatabases operation. Each specification
// is a description of the datbases on the server.
type ListDatabasesResult struct {
Databases []DatabaseSpecification
TotalSize int64
}
func newListDatabasesResultFromOperation(res operation.ListDatabasesResult) ListDatabasesResult {
var ldr ListDatabasesResult
ldr.Databases = make([]DatabaseSpecification, 0, len(res.Databases))
for _, spec := range res.Databases {
ldr.Databases = append(
ldr.Databases,
DatabaseSpecification{Name: spec.Name, SizeOnDisk: spec.SizeOnDisk, Empty: spec.Empty},
)
}
ldr.TotalSize = res.TotalSize
return ldr
}
// DatabaseSpecification is the information for a single database returned
// from a ListDatabases operation.
type DatabaseSpecification struct {
Name string
SizeOnDisk int64
Empty bool
}
// UpdateResult is a result of an update operation.
//
// UpsertedID will be a Go type that corresponds to a BSON type.
type UpdateResult struct {
// The number of documents that matched the filter.
MatchedCount int64
// The number of documents that were modified.
ModifiedCount int64
// The number of documents that were upserted.
UpsertedCount int64
// The identifier of the inserted document if an upsert took place.
UpsertedID interface{}
}
// UnmarshalBSON implements the bson.Unmarshaler interface.
func (result *UpdateResult) UnmarshalBSON(b []byte) error {
elems, err := bson.Raw(b).Elements()
if err != nil {
return err
}
for _, elem := range elems {
switch elem.Key() {
case "n":
switch elem.Value().Type {
case bson.TypeInt32:
result.MatchedCount = int64(elem.Value().Int32())
case bson.TypeInt64:
result.MatchedCount = elem.Value().Int64()
default:
return fmt.Errorf("Received invalid type for n, should be Int32 or Int64, received %s", elem.Value().Type)
}
case "nModified":
switch elem.Value().Type {
case bson.TypeInt32:
result.ModifiedCount = int64(elem.Value().Int32())
case bson.TypeInt64:
result.ModifiedCount = elem.Value().Int64()
default:
return fmt.Errorf("Received invalid type for nModified, should be Int32 or Int64, received %s", elem.Value().Type)
}
case "upserted":
switch elem.Value().Type {
case bson.TypeArray:
e, err := elem.Value().Array().IndexErr(0)
if err != nil {
break
}
if e.Value().Type != bson.TypeEmbeddedDocument {
break
}
var d struct {
ID interface{} `bson:"_id"`
}
err = bson.Unmarshal(e.Value().Document(), &d)
if err != nil {
return err
}
result.UpsertedID = d.ID
default:
return fmt.Errorf("Received invalid type for upserted, should be Array, received %s", elem.Value().Type)
}
}
}
return nil
}

277
vendor/go.mongodb.org/mongo-driver/mongo/session.go generated vendored Executable file
View File

@@ -0,0 +1,277 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"context"
"errors"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
"go.mongodb.org/mongo-driver/x/mongo/driver"
"go.mongodb.org/mongo-driver/x/mongo/driver/description"
"go.mongodb.org/mongo-driver/x/mongo/driver/operation"
"go.mongodb.org/mongo-driver/x/mongo/driver/session"
"go.mongodb.org/mongo-driver/x/mongo/driver/topology"
"go.mongodb.org/mongo-driver/x/mongo/driverlegacy"
"go.mongodb.org/mongo-driver/x/network/command"
)
// ErrWrongClient is returned when a user attempts to pass in a session created by a different client than
// the method call is using.
var ErrWrongClient = errors.New("session was not created by this client")
var withTransactionTimeout = 120 * time.Second
// SessionContext is a hybrid interface. It combines a context.Context with
// a mongo.Session. This type can be used as a regular context.Context or
// Session type. It is not goroutine safe and should not be used in multiple goroutines concurrently.
type SessionContext interface {
context.Context
Session
}
type sessionContext struct {
context.Context
Session
}
type sessionKey struct {
}
// Session is the interface that represents a sequential set of operations executed.
// Instances of this interface can be used to use transactions against the server
// and to enable causally consistent behavior for applications.
type Session interface {
EndSession(context.Context)
WithTransaction(ctx context.Context, fn func(sessCtx SessionContext) (interface{}, error), opts ...*options.TransactionOptions) (interface{}, error)
StartTransaction(...*options.TransactionOptions) error
AbortTransaction(context.Context) error
CommitTransaction(context.Context) error
ClusterTime() bson.Raw
AdvanceClusterTime(bson.Raw) error
OperationTime() *primitive.Timestamp
AdvanceOperationTime(*primitive.Timestamp) error
Client() *Client
session()
}
// sessionImpl represents a set of sequential operations executed by an application that are related in some way.
type sessionImpl struct {
clientSession *session.Client
client *Client
topo *topology.Topology
didCommitAfterStart bool // true if commit was called after start with no other operations
}
// EndSession ends the session.
func (s *sessionImpl) EndSession(ctx context.Context) {
if s.clientSession.TransactionInProgress() {
// ignore all errors aborting during an end session
_ = s.AbortTransaction(ctx)
}
s.clientSession.EndSession()
}
// WithTransaction creates a transaction on this session and runs the given callback, retrying for
// TransientTransactionError and UnknownTransactionCommitResult errors. The only way to provide a
// session to a CRUD method is to invoke that CRUD method with the mongo.SessionContext within the
// callback. The mongo.SessionContext can be used as a regular context, so methods like
// context.WithDeadline and context.WithTimeout are supported.
//
// If the context.Context already has a mongo.Session attached, that mongo.Session will be replaced
// with the one provided.
//
// The callback may be run multiple times due to retry attempts. Non-retryable and timed out errors
// are returned from this function.
func (s *sessionImpl) WithTransaction(ctx context.Context, fn func(sessCtx SessionContext) (interface{}, error), opts ...*options.TransactionOptions) (interface{}, error) {
timeout := time.NewTimer(withTransactionTimeout)
defer timeout.Stop()
var err error
for {
err = s.StartTransaction(opts...)
if err != nil {
return nil, err
}
res, err := fn(contextWithSession(ctx, s))
if err != nil {
if s.clientSession.TransactionRunning() {
_ = s.AbortTransaction(ctx)
}
select {
case <-timeout.C:
return nil, err
default:
}
if cerr, ok := err.(CommandError); ok {
if cerr.HasErrorLabel(command.TransientTransactionError) {
continue
}
}
return res, err
}
err = s.clientSession.CheckAbortTransaction()
if err != nil {
return res, nil
}
CommitLoop:
for {
err = s.CommitTransaction(ctx)
if err == nil {
return res, nil
}
select {
case <-timeout.C:
return res, err
default:
}
if cerr, ok := err.(CommandError); ok {
if cerr.HasErrorLabel(command.UnknownTransactionCommitResult) {
continue
}
if cerr.HasErrorLabel(command.TransientTransactionError) {
break CommitLoop
}
}
return res, err
}
}
}
// StartTransaction starts a transaction for this session.
func (s *sessionImpl) StartTransaction(opts ...*options.TransactionOptions) error {
err := s.clientSession.CheckStartTransaction()
if err != nil {
return err
}
s.didCommitAfterStart = false
topts := options.MergeTransactionOptions(opts...)
coreOpts := &session.TransactionOptions{
ReadConcern: topts.ReadConcern,
ReadPreference: topts.ReadPreference,
WriteConcern: topts.WriteConcern,
}
return s.clientSession.StartTransaction(coreOpts)
}
// AbortTransaction aborts the session's transaction, returning any errors and error codes
func (s *sessionImpl) AbortTransaction(ctx context.Context) error {
err := s.clientSession.CheckAbortTransaction()
if err != nil {
return err
}
// Do not run the abort command if the transaction is in starting state
if s.clientSession.TransactionStarting() || s.didCommitAfterStart {
return s.clientSession.AbortTransaction()
}
cmd := command.AbortTransaction{
Session: s.clientSession,
}
s.clientSession.Aborting = true
_, err = driverlegacy.AbortTransaction(ctx, cmd, s.topo, description.WriteSelector())
_ = s.clientSession.AbortTransaction()
return replaceErrors(err)
}
// CommitTransaction commits the sesson's transaction.
func (s *sessionImpl) CommitTransaction(ctx context.Context) error {
err := s.clientSession.CheckCommitTransaction()
if err != nil {
return err
}
// Do not run the commit command if the transaction is in started state
if s.clientSession.TransactionStarting() || s.didCommitAfterStart {
s.didCommitAfterStart = true
return s.clientSession.CommitTransaction()
}
if s.clientSession.TransactionCommitted() {
s.clientSession.RetryingCommit = true
}
var selector description.ServerSelectorFunc = func(t description.Topology, svrs []description.Server) ([]description.Server, error) {
if s.clientSession.PinnedServer != nil {
return s.clientSession.PinnedServer.SelectServer(t, svrs)
}
return description.WriteSelector().SelectServer(t, svrs)
}
s.clientSession.Committing = true
err = operation.NewCommitTransaction().
Session(s.clientSession).ClusterClock(s.client.clock).Database("admin").Deployment(s.topo).
WriteConcern(s.clientSession.CurrentWc).ServerSelector(selector).Retry(driver.RetryOncePerCommand).
CommandMonitor(s.client.monitor).RecoveryToken(bsoncore.Document(s.clientSession.RecoveryToken)).Execute(ctx)
s.clientSession.Committing = false
commitErr := s.clientSession.CommitTransaction()
// We set the write concern to majority for subsequent calls to CommitTransaction.
s.clientSession.UpdateCommitTransactionWriteConcern()
if err != nil {
return replaceErrors(err)
}
return commitErr
}
func (s *sessionImpl) ClusterTime() bson.Raw {
return s.clientSession.ClusterTime
}
func (s *sessionImpl) AdvanceClusterTime(d bson.Raw) error {
return s.clientSession.AdvanceClusterTime(d)
}
func (s *sessionImpl) OperationTime() *primitive.Timestamp {
return s.clientSession.OperationTime
}
func (s *sessionImpl) AdvanceOperationTime(ts *primitive.Timestamp) error {
return s.clientSession.AdvanceOperationTime(ts)
}
func (s *sessionImpl) Client() *Client {
return s.client
}
func (*sessionImpl) session() {
}
// sessionFromContext checks for a sessionImpl in the argued context and returns the session if it
// exists
func sessionFromContext(ctx context.Context) *session.Client {
s := ctx.Value(sessionKey{})
if ses, ok := s.(*sessionImpl); ses != nil && ok {
return ses.clientSession
}
return nil
}
func contextWithSession(ctx context.Context, sess Session) SessionContext {
return &sessionContext{
Context: context.WithValue(ctx, sessionKey{}, sess),
Session: sess,
}
}

93
vendor/go.mongodb.org/mongo-driver/mongo/single_result.go generated vendored Executable file
View File

@@ -0,0 +1,93 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo
import (
"context"
"errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
)
// ErrNoDocuments is returned by Decode when an operation that returns a
// SingleResult doesn't return any documents.
var ErrNoDocuments = errors.New("mongo: no documents in result")
// SingleResult represents a single document returned from an operation. If
// the operation returned an error, the Err method of SingleResult will
// return that error.
type SingleResult struct {
err error
cur *Cursor
rdr bson.Raw
reg *bsoncodec.Registry
}
// Decode will attempt to decode the first document into v. If there was an
// error from the operation that created this SingleResult then the error
// will be returned. If there were no returned documents, ErrNoDocuments is
// returned. If v is nil or is a typed nil, an error will be returned.
func (sr *SingleResult) Decode(v interface{}) error {
if sr.err != nil {
return sr.err
}
if sr.reg == nil {
return bson.ErrNilRegistry
}
if sr.err = sr.setRdrContents(); sr.err != nil {
return sr.err
}
return bson.UnmarshalWithRegistry(sr.reg, sr.rdr, v)
}
// DecodeBytes will return a copy of the document as a bson.Raw. If there was an
// error from the operation that created this SingleResult then the error
// will be returned. If there were no returned documents, ErrNoDocuments is
// returned.
func (sr *SingleResult) DecodeBytes() (bson.Raw, error) {
if sr.err != nil {
return nil, sr.err
}
if sr.err = sr.setRdrContents(); sr.err != nil {
return nil, sr.err
}
return sr.rdr, nil
}
// setRdrContents will set the contents of rdr by iterating the underlying cursor if necessary.
func (sr *SingleResult) setRdrContents() error {
switch {
case sr.err != nil:
return sr.err
case sr.rdr != nil:
return nil
case sr.cur != nil:
defer sr.cur.Close(context.TODO())
if !sr.cur.Next(context.TODO()) {
if err := sr.cur.Err(); err != nil {
return err
}
return ErrNoDocuments
}
sr.rdr = sr.cur.Current
return nil
}
return ErrNoDocuments
}
// Err will return the error from the operation that created this SingleResult.
// If there was no error, nil is returned.
func (sr *SingleResult) Err() error {
sr.err = sr.setRdrContents()
return sr.err
}

7
vendor/go.mongodb.org/mongo-driver/mongo/util.go generated vendored Executable file
View File

@@ -0,0 +1,7 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package mongo

View File

@@ -0,0 +1,216 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
package writeconcern // import "go.mongodb.org/mongo-driver/mongo/writeconcern"
import (
"errors"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)
// ErrInconsistent indicates that an inconsistent write concern was specified.
var ErrInconsistent = errors.New("a write concern cannot have both w=0 and j=true")
// ErrEmptyWriteConcern indicates that a write concern has no fields set.
var ErrEmptyWriteConcern = errors.New("a write concern must have at least one field set")
// ErrNegativeW indicates that a negative integer `w` field was specified.
var ErrNegativeW = errors.New("write concern `w` field cannot be a negative number")
// ErrNegativeWTimeout indicates that a negative WTimeout was specified.
var ErrNegativeWTimeout = errors.New("write concern `wtimeout` field cannot be negative")
// WriteConcern describes the level of acknowledgement requested from MongoDB for write operations
// to a standalone mongod or to replica sets or to sharded clusters.
type WriteConcern struct {
w interface{}
j bool
wTimeout time.Duration
}
// Option is an option to provide when creating a WriteConcern.
type Option func(concern *WriteConcern)
// New constructs a new WriteConcern.
func New(options ...Option) *WriteConcern {
concern := &WriteConcern{}
for _, option := range options {
option(concern)
}
return concern
}
// W requests acknowledgement that write operations propagate to the specified number of mongod
// instances.
func W(w int) Option {
return func(concern *WriteConcern) {
concern.w = w
}
}
// WMajority requests acknowledgement that write operations propagate to the majority of mongod
// instances.
func WMajority() Option {
return func(concern *WriteConcern) {
concern.w = "majority"
}
}
// WTagSet requests acknowledgement that write operations propagate to the specified mongod
// instance.
func WTagSet(tag string) Option {
return func(concern *WriteConcern) {
concern.w = tag
}
}
// J requests acknowledgement from MongoDB that write operations are written to
// the journal.
func J(j bool) Option {
return func(concern *WriteConcern) {
concern.j = j
}
}
// WTimeout specifies specifies a time limit for the write concern.
func WTimeout(d time.Duration) Option {
return func(concern *WriteConcern) {
concern.wTimeout = d
}
}
// MarshalBSONValue implements the bson.ValueMarshaler interface.
func (wc *WriteConcern) MarshalBSONValue() (bsontype.Type, []byte, error) {
if !wc.IsValid() {
return bsontype.Type(0), nil, ErrInconsistent
}
var elems []byte
if wc.w != nil {
switch t := wc.w.(type) {
case int:
if t < 0 {
return bsontype.Type(0), nil, ErrNegativeW
}
elems = bsoncore.AppendInt32Element(elems, "w", int32(t))
case string:
elems = bsoncore.AppendStringElement(elems, "w", string(t))
}
}
if wc.j {
elems = bsoncore.AppendBooleanElement(elems, "j", wc.j)
}
if wc.wTimeout < 0 {
return bsontype.Type(0), nil, ErrNegativeWTimeout
}
if wc.wTimeout != 0 {
elems = bsoncore.AppendInt64Element(elems, "wtimeout", int64(wc.wTimeout/time.Millisecond))
}
if len(elems) == 0 {
return bsontype.Type(0), nil, ErrEmptyWriteConcern
}
return bsontype.EmbeddedDocument, bsoncore.BuildDocument(nil, elems), nil
}
// AcknowledgedValue returns true if a BSON RawValue for a write concern represents an acknowledged write concern.
// The element's value must be a document representing a write concern.
func AcknowledgedValue(rawv bson.RawValue) bool {
doc, ok := bsoncore.Value{Type: rawv.Type, Data: rawv.Value}.DocumentOK()
if !ok {
return false
}
val, err := doc.LookupErr("w")
if err != nil {
// key w not found --> acknowledged
return true
}
i32, ok := val.Int32OK()
if !ok {
return false
}
return i32 != 0
}
// Acknowledged indicates whether or not a write with the given write concern will be acknowledged.
func (wc *WriteConcern) Acknowledged() bool {
if wc == nil || wc.j {
return true
}
switch v := wc.w.(type) {
case int:
if v == 0 {
return false
}
}
return true
}
// IsValid checks whether the write concern is invalid.
func (wc *WriteConcern) IsValid() bool {
if !wc.j {
return true
}
switch v := wc.w.(type) {
case int:
if v == 0 {
return false
}
}
return true
}
// GetW returns the write concern w level.
func (wc *WriteConcern) GetW() interface{} {
return wc.w
}
// GetJ returns the write concern journaling level.
func (wc *WriteConcern) GetJ() bool {
return wc.j
}
// GetWTimeout returns the write concern timeout.
func (wc *WriteConcern) GetWTimeout() time.Duration {
return wc.wTimeout
}
// WithOptions returns a copy of this WriteConcern with the options set.
func (wc *WriteConcern) WithOptions(options ...Option) *WriteConcern {
if wc == nil {
return New(options...)
}
newWC := &WriteConcern{}
*newWC = *wc
for _, option := range options {
option(newWC)
}
return newWC
}
// AckWrite returns true if a write concern represents an acknowledged write
func AckWrite(wc *WriteConcern) bool {
return wc == nil || wc.Acknowledged()
}