You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

document.go 8.5 kB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. // 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. package bsonx
  7. import (
  8. "bytes"
  9. "errors"
  10. "fmt"
  11. "go.mongodb.org/mongo-driver/bson/bsontype"
  12. "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
  13. )
  14. // ErrNilDocument indicates that an operation was attempted on a nil *bson.Document.
  15. var ErrNilDocument = errors.New("document is nil")
  16. // KeyNotFound is an error type returned from the Lookup methods on Document. This type contains
  17. // information about which key was not found and if it was actually not found or if a component of
  18. // the key except the last was not a document nor array.
  19. type KeyNotFound struct {
  20. Key []string // The keys that were searched for.
  21. Depth uint // Which key either was not found or was an incorrect type.
  22. Type bsontype.Type // The type of the key that was found but was an incorrect type.
  23. }
  24. func (knf KeyNotFound) Error() string {
  25. depth := knf.Depth
  26. if depth >= uint(len(knf.Key)) {
  27. depth = uint(len(knf.Key)) - 1
  28. }
  29. if len(knf.Key) == 0 {
  30. return "no keys were provided for lookup"
  31. }
  32. if knf.Type != bsontype.Type(0) {
  33. return fmt.Sprintf(`key "%s" was found but was not valid to traverse BSON type %s`, knf.Key[depth], knf.Type)
  34. }
  35. return fmt.Sprintf(`key "%s" was not found`, knf.Key[depth])
  36. }
  37. // Doc is a type safe, concise BSON document representation.
  38. type Doc []Elem
  39. // ReadDoc will create a Document using the provided slice of bytes. If the
  40. // slice of bytes is not a valid BSON document, this method will return an error.
  41. func ReadDoc(b []byte) (Doc, error) {
  42. doc := make(Doc, 0)
  43. err := doc.UnmarshalBSON(b)
  44. if err != nil {
  45. return nil, err
  46. }
  47. return doc, nil
  48. }
  49. // Copy makes a shallow copy of this document.
  50. func (d Doc) Copy() Doc {
  51. d2 := make(Doc, len(d))
  52. copy(d2, d)
  53. return d2
  54. }
  55. // Append adds an element to the end of the document, creating it from the key and value provided.
  56. func (d Doc) Append(key string, val Val) Doc {
  57. return append(d, Elem{Key: key, Value: val})
  58. }
  59. // Prepend adds an element to the beginning of the document, creating it from the key and value provided.
  60. func (d Doc) Prepend(key string, val Val) Doc {
  61. // TODO: should we just modify d itself instead of doing an alloc here?
  62. return append(Doc{{Key: key, Value: val}}, d...)
  63. }
  64. // Set replaces an element of a document. If an element with a matching key is
  65. // found, the element will be replaced with the one provided. If the document
  66. // does not have an element with that key, the element is appended to the
  67. // document instead.
  68. func (d Doc) Set(key string, val Val) Doc {
  69. idx := d.IndexOf(key)
  70. if idx == -1 {
  71. return append(d, Elem{Key: key, Value: val})
  72. }
  73. d[idx] = Elem{Key: key, Value: val}
  74. return d
  75. }
  76. // IndexOf returns the index of the first element with a key of key, or -1 if no element with a key
  77. // was found.
  78. func (d Doc) IndexOf(key string) int {
  79. for i, e := range d {
  80. if e.Key == key {
  81. return i
  82. }
  83. }
  84. return -1
  85. }
  86. // Delete removes the element with key if it exists and returns the updated Doc.
  87. func (d Doc) Delete(key string) Doc {
  88. idx := d.IndexOf(key)
  89. if idx == -1 {
  90. return d
  91. }
  92. return append(d[:idx], d[idx+1:]...)
  93. }
  94. // Lookup searches the document and potentially subdocuments or arrays for the
  95. // provided key. Each key provided to this method represents a layer of depth.
  96. //
  97. // This method will return an empty Value if they key does not exist. To know if they key actually
  98. // exists, use LookupErr.
  99. func (d Doc) Lookup(key ...string) Val {
  100. val, _ := d.LookupErr(key...)
  101. return val
  102. }
  103. // LookupErr searches the document and potentially subdocuments or arrays for the
  104. // provided key. Each key provided to this method represents a layer of depth.
  105. func (d Doc) LookupErr(key ...string) (Val, error) {
  106. elem, err := d.LookupElementErr(key...)
  107. return elem.Value, err
  108. }
  109. // LookupElement searches the document and potentially subdocuments or arrays for the
  110. // provided key. Each key provided to this method represents a layer of depth.
  111. //
  112. // This method will return an empty Element if they key does not exist. To know if they key actually
  113. // exists, use LookupElementErr.
  114. func (d Doc) LookupElement(key ...string) Elem {
  115. elem, _ := d.LookupElementErr(key...)
  116. return elem
  117. }
  118. // LookupElementErr searches the document and potentially subdocuments for the
  119. // provided key. Each key provided to this method represents a layer of depth.
  120. func (d Doc) LookupElementErr(key ...string) (Elem, error) {
  121. // KeyNotFound operates by being created where the error happens and then the depth is
  122. // incremented by 1 as each function unwinds. Whenever this function returns, it also assigns
  123. // the Key slice to the key slice it has. This ensures that the proper depth is identified and
  124. // the proper keys.
  125. if len(key) == 0 {
  126. return Elem{}, KeyNotFound{Key: key}
  127. }
  128. var elem Elem
  129. var err error
  130. idx := d.IndexOf(key[0])
  131. if idx == -1 {
  132. return Elem{}, KeyNotFound{Key: key}
  133. }
  134. elem = d[idx]
  135. if len(key) == 1 {
  136. return elem, nil
  137. }
  138. switch elem.Value.Type() {
  139. case bsontype.EmbeddedDocument:
  140. switch tt := elem.Value.primitive.(type) {
  141. case Doc:
  142. elem, err = tt.LookupElementErr(key[1:]...)
  143. case MDoc:
  144. elem, err = tt.LookupElementErr(key[1:]...)
  145. }
  146. default:
  147. return Elem{}, KeyNotFound{Type: elem.Value.Type()}
  148. }
  149. switch tt := err.(type) {
  150. case KeyNotFound:
  151. tt.Depth++
  152. tt.Key = key
  153. return Elem{}, tt
  154. case nil:
  155. return elem, nil
  156. default:
  157. return Elem{}, err // We can't actually hit this.
  158. }
  159. }
  160. // MarshalBSONValue implements the bsoncodec.ValueMarshaler interface.
  161. //
  162. // This method will never return an error.
  163. func (d Doc) MarshalBSONValue() (bsontype.Type, []byte, error) {
  164. if d == nil {
  165. // TODO: Should we do this?
  166. return bsontype.Null, nil, nil
  167. }
  168. data, _ := d.MarshalBSON()
  169. return bsontype.EmbeddedDocument, data, nil
  170. }
  171. // MarshalBSON implements the Marshaler interface.
  172. //
  173. // This method will never return an error.
  174. func (d Doc) MarshalBSON() ([]byte, error) { return d.AppendMarshalBSON(nil) }
  175. // AppendMarshalBSON marshals Doc to BSON bytes, appending to dst.
  176. //
  177. // This method will never return an error.
  178. func (d Doc) AppendMarshalBSON(dst []byte) ([]byte, error) {
  179. idx, dst := bsoncore.ReserveLength(dst)
  180. for _, elem := range d {
  181. t, data, _ := elem.Value.MarshalBSONValue() // Value.MarshalBSONValue never returns an error.
  182. dst = append(dst, byte(t))
  183. dst = append(dst, elem.Key...)
  184. dst = append(dst, 0x00)
  185. dst = append(dst, data...)
  186. }
  187. dst = append(dst, 0x00)
  188. dst = bsoncore.UpdateLength(dst, idx, int32(len(dst[idx:])))
  189. return dst, nil
  190. }
  191. // UnmarshalBSON implements the Unmarshaler interface.
  192. func (d *Doc) UnmarshalBSON(b []byte) error {
  193. if d == nil {
  194. return ErrNilDocument
  195. }
  196. if err := bsoncore.Document(b).Validate(); err != nil {
  197. return err
  198. }
  199. elems, err := bsoncore.Document(b).Elements()
  200. if err != nil {
  201. return err
  202. }
  203. var val Val
  204. for _, elem := range elems {
  205. rawv := elem.Value()
  206. err = val.UnmarshalBSONValue(rawv.Type, rawv.Data)
  207. if err != nil {
  208. return err
  209. }
  210. *d = d.Append(elem.Key(), val)
  211. }
  212. return nil
  213. }
  214. // UnmarshalBSONValue implements the bson.ValueUnmarshaler interface.
  215. func (d *Doc) UnmarshalBSONValue(t bsontype.Type, data []byte) error {
  216. if t != bsontype.EmbeddedDocument {
  217. return fmt.Errorf("cannot unmarshal %s into a bsonx.Doc", t)
  218. }
  219. return d.UnmarshalBSON(data)
  220. }
  221. // Equal compares this document to another, returning true if they are equal.
  222. func (d Doc) Equal(id IDoc) bool {
  223. switch tt := id.(type) {
  224. case Doc:
  225. d2 := tt
  226. if len(d) != len(d2) {
  227. return false
  228. }
  229. for idx := range d {
  230. if !d[idx].Equal(d2[idx]) {
  231. return false
  232. }
  233. }
  234. case MDoc:
  235. unique := make(map[string]struct{}, 0)
  236. for _, elem := range d {
  237. unique[elem.Key] = struct{}{}
  238. val, ok := tt[elem.Key]
  239. if !ok {
  240. return false
  241. }
  242. if !val.Equal(elem.Value) {
  243. return false
  244. }
  245. }
  246. if len(unique) != len(tt) {
  247. return false
  248. }
  249. case nil:
  250. return d == nil
  251. default:
  252. return false
  253. }
  254. return true
  255. }
  256. // String implements the fmt.Stringer interface.
  257. func (d Doc) String() string {
  258. var buf bytes.Buffer
  259. buf.Write([]byte("bson.Document{"))
  260. for idx, elem := range d {
  261. if idx > 0 {
  262. buf.Write([]byte(", "))
  263. }
  264. fmt.Fprintf(&buf, "%v", elem)
  265. }
  266. buf.WriteByte('}')
  267. return buf.String()
  268. }
  269. func (Doc) idoc() {}