blob: 22f383b3f6faf86266d0585bfd0c4357730731e8 [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001// Copyright (C) MongoDB, Inc. 2017-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package options
8
9import (
10 "fmt"
11 "reflect"
12
13 "github.com/mongodb/mongo-go-driver/bson"
14 "github.com/mongodb/mongo-go-driver/bson/bsoncodec"
15 "github.com/mongodb/mongo-go-driver/x/bsonx"
16)
17
18// Collation allows users to specify language-specific rules for string comparison, such as
19// rules for lettercase and accent marks.
20type Collation struct {
21 Locale string `bson:",omitempty"` // The locale
22 CaseLevel bool `bson:",omitempty"` // The case level
23 CaseFirst string `bson:",omitempty"` // The case ordering
24 Strength int `bson:",omitempty"` // The number of comparision levels to use
25 NumericOrdering bool `bson:",omitempty"` // Whether to order numbers based on numerical order and not collation order
26 Alternate string `bson:",omitempty"` // Whether spaces and punctuation are considered base characters
27 MaxVariable string `bson:",omitempty"` // Which characters are affected by alternate: "shifted"
28 Normalization bool `bson:",omitempty"` // Causes text to be normalized into Unicode NFD
29 Backwards bool `bson:",omitempty"` // Causes secondary differences to be considered in reverse order, as it is done in the French language
30}
31
32// ToDocument converts the Collation to a *bsonx.Document
33func (co *Collation) ToDocument() bsonx.Doc {
34 doc := bsonx.Doc{}
35 if co.Locale != "" {
36 doc = append(doc, bsonx.Elem{"locale", bsonx.String(co.Locale)})
37 }
38 if co.CaseLevel {
39 doc = append(doc, bsonx.Elem{"caseLevel", bsonx.Boolean(true)})
40 }
41 if co.CaseFirst != "" {
42 doc = append(doc, bsonx.Elem{"caseFirst", bsonx.String(co.CaseFirst)})
43 }
44 if co.Strength != 0 {
45 doc = append(doc, bsonx.Elem{"strength", bsonx.Int32(int32(co.Strength))})
46 }
47 if co.NumericOrdering {
48 doc = append(doc, bsonx.Elem{"numericOrdering", bsonx.Boolean(true)})
49 }
50 if co.Alternate != "" {
51 doc = append(doc, bsonx.Elem{"alternate", bsonx.String(co.Alternate)})
52 }
53 if co.MaxVariable != "" {
54 doc = append(doc, bsonx.Elem{"maxVariable", bsonx.String(co.MaxVariable)})
55 }
56 if co.Normalization {
57 doc = append(doc, bsonx.Elem{"normalization", bsonx.Boolean(co.Normalization)})
58 }
59 if co.Backwards {
60 doc = append(doc, bsonx.Elem{"backwards", bsonx.Boolean(true)})
61 }
62 return doc
63}
64
65// CursorType specifies whether a cursor should close when the last data is retrieved. See
66// NonTailable, Tailable, and TailableAwait.
67type CursorType int8
68
69const (
70 // NonTailable specifies that a cursor should close after retrieving the last data.
71 NonTailable CursorType = iota
72 // Tailable specifies that a cursor should not close when the last data is retrieved and can be resumed later.
73 Tailable
74 // TailableAwait specifies that a cursor should not close when the last data is retrieved and
75 // that it should block for a certain amount of time for new data before returning no data.
76 TailableAwait
77)
78
79// ReturnDocument specifies whether a findAndUpdate operation should return the document as it was
80// before the update or as it is after the update.
81type ReturnDocument int8
82
83const (
84 // Before specifies that findAndUpdate should return the document as it was before the update.
85 Before ReturnDocument = iota
86 // After specifies that findAndUpdate should return the document as it is after the update.
87 After
88)
89
90// FullDocument specifies whether a change stream should include a copy of the entire document that was changed from
91// some time after the change occurred.
92type FullDocument string
93
94const (
95 // Default does not include a document copy
96 Default FullDocument = "default"
97 // UpdateLookup includes a delta describing the changes to the document and a copy of the entire document that
98 // was changed
99 UpdateLookup FullDocument = "updateLookup"
100)
101
102// ArrayFilters is used to hold filters for the array filters CRUD option. If a registry is nil, bson.DefaultRegistry
103// will be used when converting the filter interfaces to BSON.
104type ArrayFilters struct {
105 Registry *bsoncodec.Registry // The registry to use for converting filters. Defaults to bson.DefaultRegistry.
106 Filters []interface{} // The filters to apply
107}
108
109// ToArray builds a bsonx.Arr from the provided ArrayFilters.
110func (af *ArrayFilters) ToArray() (bsonx.Arr, error) {
111 docs := make([]bsonx.Doc, 0, len(af.Filters))
112 for _, f := range af.Filters {
113 d, err := transformDocument(af.Registry, f)
114 if err != nil {
115 return nil, err
116 }
117 docs = append(docs, d)
118 }
119
120 arr := bsonx.Arr{}
121 for _, doc := range docs {
122 arr = append(arr, bsonx.Document(doc))
123 }
124
125 return arr, nil
126}
127
128// MarshalError is returned when attempting to transform a value into a document
129// results in an error.
130type MarshalError struct {
131 Value interface{}
132 Err error
133}
134
135// Error implements the error interface.
136func (me MarshalError) Error() string {
137 return fmt.Sprintf("cannot transform type %s to a *bsonx.Document", reflect.TypeOf(me.Value))
138}
139
140var defaultRegistry = bson.DefaultRegistry
141
142func transformDocument(registry *bsoncodec.Registry, val interface{}) (bsonx.Doc, error) {
143 if val == nil {
144 return bsonx.Doc{}, nil
145 }
146 reg := defaultRegistry
147 if registry != nil {
148 reg = registry
149 }
150
151 if bs, ok := val.([]byte); ok {
152 // Slight optimization so we'll just use MarshalBSON and not go through the codec machinery.
153 val = bson.Raw(bs)
154 }
155
156 // TODO(skriptble): Use a pool of these instead.
157 buf := make([]byte, 0, 256)
158 b, err := bson.MarshalAppendWithRegistry(reg, buf, val)
159 if err != nil {
160 return nil, MarshalError{Value: val, Err: err}
161 }
162 return bsonx.ReadDoc(b)
163}