/
mongooptions.go
158 lines (137 loc) · 5.35 KB
/
mongooptions.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// 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"
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.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
}
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)
}