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.

mdocument.go 6.0 kB

4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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. "fmt"
  10. "go.mongodb.org/mongo-driver/bson/bsontype"
  11. "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
  12. )
  13. // MDoc is an unordered, type safe, concise BSON document representation. This type should not be
  14. // used if you require ordering of values or duplicate keys.
  15. type MDoc map[string]Val
  16. // ReadMDoc will create a Doc using the provided slice of bytes. If the
  17. // slice of bytes is not a valid BSON document, this method will return an error.
  18. func ReadMDoc(b []byte) (MDoc, error) {
  19. doc := make(MDoc, 0)
  20. err := doc.UnmarshalBSON(b)
  21. if err != nil {
  22. return nil, err
  23. }
  24. return doc, nil
  25. }
  26. // Copy makes a shallow copy of this document.
  27. func (d MDoc) Copy() MDoc {
  28. d2 := make(MDoc, len(d))
  29. for k, v := range d {
  30. d2[k] = v
  31. }
  32. return d2
  33. }
  34. // Lookup searches the document and potentially subdocuments or arrays for the
  35. // provided key. Each key provided to this method represents a layer of depth.
  36. //
  37. // This method will return an empty Value if they key does not exist. To know if they key actually
  38. // exists, use LookupErr.
  39. func (d MDoc) Lookup(key ...string) Val {
  40. val, _ := d.LookupErr(key...)
  41. return val
  42. }
  43. // LookupErr searches the document and potentially subdocuments or arrays for the
  44. // provided key. Each key provided to this method represents a layer of depth.
  45. func (d MDoc) LookupErr(key ...string) (Val, error) {
  46. elem, err := d.LookupElementErr(key...)
  47. return elem.Value, err
  48. }
  49. // LookupElement searches the document and potentially subdocuments or arrays for the
  50. // provided key. Each key provided to this method represents a layer of depth.
  51. //
  52. // This method will return an empty Element if they key does not exist. To know if they key actually
  53. // exists, use LookupElementErr.
  54. func (d MDoc) LookupElement(key ...string) Elem {
  55. elem, _ := d.LookupElementErr(key...)
  56. return elem
  57. }
  58. // LookupElementErr searches the document and potentially subdocuments for the
  59. // provided key. Each key provided to this method represents a layer of depth.
  60. func (d MDoc) LookupElementErr(key ...string) (Elem, error) {
  61. // KeyNotFound operates by being created where the error happens and then the depth is
  62. // incremented by 1 as each function unwinds. Whenever this function returns, it also assigns
  63. // the Key slice to the key slice it has. This ensures that the proper depth is identified and
  64. // the proper keys.
  65. if len(key) == 0 {
  66. return Elem{}, KeyNotFound{Key: key}
  67. }
  68. var elem Elem
  69. var err error
  70. val, ok := d[key[0]]
  71. if !ok {
  72. return Elem{}, KeyNotFound{Key: key}
  73. }
  74. if len(key) == 1 {
  75. return Elem{Key: key[0], Value: val}, nil
  76. }
  77. switch val.Type() {
  78. case bsontype.EmbeddedDocument:
  79. switch tt := val.primitive.(type) {
  80. case Doc:
  81. elem, err = tt.LookupElementErr(key[1:]...)
  82. case MDoc:
  83. elem, err = tt.LookupElementErr(key[1:]...)
  84. }
  85. default:
  86. return Elem{}, KeyNotFound{Type: val.Type()}
  87. }
  88. switch tt := err.(type) {
  89. case KeyNotFound:
  90. tt.Depth++
  91. tt.Key = key
  92. return Elem{}, tt
  93. case nil:
  94. return elem, nil
  95. default:
  96. return Elem{}, err // We can't actually hit this.
  97. }
  98. }
  99. // MarshalBSONValue implements the bsoncodec.ValueMarshaler interface.
  100. //
  101. // This method will never return an error.
  102. func (d MDoc) MarshalBSONValue() (bsontype.Type, []byte, error) {
  103. if d == nil {
  104. // TODO: Should we do this?
  105. return bsontype.Null, nil, nil
  106. }
  107. data, _ := d.MarshalBSON()
  108. return bsontype.EmbeddedDocument, data, nil
  109. }
  110. // MarshalBSON implements the Marshaler interface.
  111. //
  112. // This method will never return an error.
  113. func (d MDoc) MarshalBSON() ([]byte, error) { return d.AppendMarshalBSON(nil) }
  114. // AppendMarshalBSON marshals Doc to BSON bytes, appending to dst.
  115. //
  116. // This method will never return an error.
  117. func (d MDoc) AppendMarshalBSON(dst []byte) ([]byte, error) {
  118. idx, dst := bsoncore.ReserveLength(dst)
  119. for k, v := range d {
  120. t, data, _ := v.MarshalBSONValue() // Value.MarshalBSONValue never returns an error.
  121. dst = append(dst, byte(t))
  122. dst = append(dst, k...)
  123. dst = append(dst, 0x00)
  124. dst = append(dst, data...)
  125. }
  126. dst = append(dst, 0x00)
  127. dst = bsoncore.UpdateLength(dst, idx, int32(len(dst[idx:])))
  128. return dst, nil
  129. }
  130. // UnmarshalBSON implements the Unmarshaler interface.
  131. func (d *MDoc) UnmarshalBSON(b []byte) error {
  132. if d == nil {
  133. return ErrNilDocument
  134. }
  135. if err := bsoncore.Document(b).Validate(); err != nil {
  136. return err
  137. }
  138. elems, err := bsoncore.Document(b).Elements()
  139. if err != nil {
  140. return err
  141. }
  142. var val Val
  143. for _, elem := range elems {
  144. rawv := elem.Value()
  145. err = val.UnmarshalBSONValue(rawv.Type, rawv.Data)
  146. if err != nil {
  147. return err
  148. }
  149. (*d)[elem.Key()] = val
  150. }
  151. return nil
  152. }
  153. // Equal compares this document to another, returning true if they are equal.
  154. func (d MDoc) Equal(id IDoc) bool {
  155. switch tt := id.(type) {
  156. case MDoc:
  157. d2 := tt
  158. if len(d) != len(d2) {
  159. return false
  160. }
  161. for key, value := range d {
  162. value2, ok := d2[key]
  163. if !ok {
  164. return false
  165. }
  166. if !value.Equal(value2) {
  167. return false
  168. }
  169. }
  170. case Doc:
  171. unique := make(map[string]struct{}, 0)
  172. for _, elem := range tt {
  173. unique[elem.Key] = struct{}{}
  174. val, ok := d[elem.Key]
  175. if !ok {
  176. return false
  177. }
  178. if !val.Equal(elem.Value) {
  179. return false
  180. }
  181. }
  182. if len(unique) != len(d) {
  183. return false
  184. }
  185. case nil:
  186. return d == nil
  187. default:
  188. return false
  189. }
  190. return true
  191. }
  192. // String implements the fmt.Stringer interface.
  193. func (d MDoc) String() string {
  194. var buf bytes.Buffer
  195. buf.Write([]byte("bson.Document{"))
  196. first := true
  197. for key, value := range d {
  198. if !first {
  199. buf.Write([]byte(", "))
  200. }
  201. fmt.Fprintf(&buf, "%v", Elem{Key: key, Value: value})
  202. first = false
  203. }
  204. buf.WriteByte('}')
  205. return buf.String()
  206. }
  207. func (MDoc) idoc() {}