blob: 22f383b3f6faf86266d0585bfd0c4357730731e8 [file] [log] [blame]
// 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"
"github.com/mongodb/mongo-go-driver/bson"
"github.com/mongodb/mongo-go-driver/bson/bsoncodec"
"github.com/mongodb/mongo-go-driver/x/bsonx"
)
// 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 *bsonx.Document
func (co *Collation) ToDocument() bsonx.Doc {
doc := bsonx.Doc{}
if co.Locale != "" {
doc = append(doc, bsonx.Elem{"locale", bsonx.String(co.Locale)})
}
if co.CaseLevel {
doc = append(doc, bsonx.Elem{"caseLevel", bsonx.Boolean(true)})
}
if co.CaseFirst != "" {
doc = append(doc, bsonx.Elem{"caseFirst", bsonx.String(co.CaseFirst)})
}
if co.Strength != 0 {
doc = append(doc, bsonx.Elem{"strength", bsonx.Int32(int32(co.Strength))})
}
if co.NumericOrdering {
doc = append(doc, bsonx.Elem{"numericOrdering", bsonx.Boolean(true)})
}
if co.Alternate != "" {
doc = append(doc, bsonx.Elem{"alternate", bsonx.String(co.Alternate)})
}
if co.MaxVariable != "" {
doc = append(doc, bsonx.Elem{"maxVariable", bsonx.String(co.MaxVariable)})
}
if co.Normalization {
doc = append(doc, bsonx.Elem{"normalization", bsonx.Boolean(co.Normalization)})
}
if co.Backwards {
doc = append(doc, bsonx.Elem{"backwards", bsonx.Boolean(true)})
}
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 bsonx.Arr from the provided ArrayFilters.
func (af *ArrayFilters) ToArray() (bsonx.Arr, error) {
docs := make([]bsonx.Doc, 0, len(af.Filters))
for _, f := range af.Filters {
d, err := transformDocument(af.Registry, f)
if err != nil {
return nil, err
}
docs = append(docs, d)
}
arr := bsonx.Arr{}
for _, doc := range docs {
arr = append(arr, bsonx.Document(doc))
}
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 *bsonx.Document", reflect.TypeOf(me.Value))
}
var defaultRegistry = bson.DefaultRegistry
func transformDocument(registry *bsoncodec.Registry, val interface{}) (bsonx.Doc, error) {
if val == nil {
return bsonx.Doc{}, nil
}
reg := defaultRegistry
if registry != nil {
reg = registry
}
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(reg, buf, val)
if err != nil {
return nil, MarshalError{Value: val, Err: err}
}
return bsonx.ReadDoc(b)
}