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.

cursor.go 6.3 kB

4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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 mongo
  7. import (
  8. "context"
  9. "errors"
  10. "io"
  11. "reflect"
  12. "go.mongodb.org/mongo-driver/bson"
  13. "go.mongodb.org/mongo-driver/bson/bsoncodec"
  14. "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
  15. "go.mongodb.org/mongo-driver/x/mongo/driver"
  16. "go.mongodb.org/mongo-driver/x/mongo/driver/session"
  17. )
  18. // Cursor is used to iterate a stream of documents. Each document is decoded into the result
  19. // according to the rules of the bson package.
  20. //
  21. // A typical usage of the Cursor type would be:
  22. //
  23. // var cur *Cursor
  24. // ctx := context.Background()
  25. // defer cur.Close(ctx)
  26. //
  27. // for cur.Next(ctx) {
  28. // elem := &bson.D{}
  29. // if err := cur.Decode(elem); err != nil {
  30. // log.Fatal(err)
  31. // }
  32. //
  33. // // do something with elem....
  34. // }
  35. //
  36. // if err := cur.Err(); err != nil {
  37. // log.Fatal(err)
  38. // }
  39. //
  40. type Cursor struct {
  41. // Current is the BSON bytes of the current document. This property is only valid until the next
  42. // call to Next or Close. If continued access is required to the bson.Raw, you must make a copy
  43. // of it.
  44. Current bson.Raw
  45. bc batchCursor
  46. batch *bsoncore.DocumentSequence
  47. registry *bsoncodec.Registry
  48. clientSession *session.Client
  49. err error
  50. }
  51. func newCursor(bc batchCursor, registry *bsoncodec.Registry) (*Cursor, error) {
  52. return newCursorWithSession(bc, registry, nil)
  53. }
  54. func newCursorWithSession(bc batchCursor, registry *bsoncodec.Registry, clientSession *session.Client) (*Cursor, error) {
  55. if registry == nil {
  56. registry = bson.DefaultRegistry
  57. }
  58. if bc == nil {
  59. return nil, errors.New("batch cursor must not be nil")
  60. }
  61. c := &Cursor{
  62. bc: bc,
  63. registry: registry,
  64. clientSession: clientSession,
  65. }
  66. if bc.ID() == 0 {
  67. c.closeImplicitSession()
  68. }
  69. return c, nil
  70. }
  71. func newEmptyCursor() *Cursor {
  72. return &Cursor{bc: driver.NewEmptyBatchCursor()}
  73. }
  74. // ID returns the ID of this cursor.
  75. func (c *Cursor) ID() int64 { return c.bc.ID() }
  76. // Next gets the next result from this cursor. Returns true if there were no errors and the next
  77. // result is available for decoding.
  78. func (c *Cursor) Next(ctx context.Context) bool {
  79. if ctx == nil {
  80. ctx = context.Background()
  81. }
  82. doc, err := c.batch.Next()
  83. switch err {
  84. case nil:
  85. c.Current = bson.Raw(doc)
  86. return true
  87. case io.EOF: // Need to do a getMore
  88. default:
  89. c.err = err
  90. return false
  91. }
  92. // call the Next method in a loop until at least one document is returned in the next batch or
  93. // the context times out.
  94. for {
  95. // If we don't have a next batch
  96. if !c.bc.Next(ctx) {
  97. // Do we have an error? If so we return false.
  98. c.err = c.bc.Err()
  99. if c.err != nil {
  100. return false
  101. }
  102. // Is the cursor ID zero?
  103. if c.bc.ID() == 0 {
  104. c.closeImplicitSession()
  105. return false
  106. }
  107. // empty batch, but cursor is still valid, so continue.
  108. continue
  109. }
  110. // close the implicit session if this was the last getMore
  111. if c.bc.ID() == 0 {
  112. c.closeImplicitSession()
  113. }
  114. c.batch = c.bc.Batch()
  115. doc, err = c.batch.Next()
  116. switch err {
  117. case nil:
  118. c.Current = bson.Raw(doc)
  119. return true
  120. case io.EOF: // Empty batch so we continue
  121. default:
  122. c.err = err
  123. return false
  124. }
  125. }
  126. }
  127. // Decode will decode the current document into val. If val is nil or is a typed nil, an error will be returned.
  128. func (c *Cursor) Decode(val interface{}) error {
  129. return bson.UnmarshalWithRegistry(c.registry, c.Current, val)
  130. }
  131. // Err returns the current error.
  132. func (c *Cursor) Err() error { return c.err }
  133. // Close closes this cursor.
  134. func (c *Cursor) Close(ctx context.Context) error {
  135. defer c.closeImplicitSession()
  136. return c.bc.Close(ctx)
  137. }
  138. // All iterates the cursor and decodes each document into results.
  139. // The results parameter must be a pointer to a slice. The slice pointed to by results will be completely overwritten.
  140. // If the cursor has been iterated, any previously iterated documents will not be included in results.
  141. // The cursor will be closed after the method has returned.
  142. func (c *Cursor) All(ctx context.Context, results interface{}) error {
  143. resultsVal := reflect.ValueOf(results)
  144. if resultsVal.Kind() != reflect.Ptr {
  145. return errors.New("results argument must be a pointer to a slice")
  146. }
  147. sliceVal := resultsVal.Elem()
  148. elementType := sliceVal.Type().Elem()
  149. var index int
  150. var err error
  151. defer c.Close(ctx)
  152. batch := c.batch // exhaust the current batch before iterating the batch cursor
  153. for {
  154. sliceVal, index, err = c.addFromBatch(sliceVal, elementType, batch, index)
  155. if err != nil {
  156. return err
  157. }
  158. if !c.bc.Next(ctx) {
  159. break
  160. }
  161. batch = c.bc.Batch()
  162. }
  163. if err = c.bc.Err(); err != nil {
  164. return err
  165. }
  166. resultsVal.Elem().Set(sliceVal.Slice(0, index))
  167. return nil
  168. }
  169. // addFromBatch adds all documents from batch to sliceVal starting at the given index. It returns the new slice value,
  170. // the next empty index in the slice, and an error if one occurs.
  171. func (c *Cursor) addFromBatch(sliceVal reflect.Value, elemType reflect.Type, batch *bsoncore.DocumentSequence,
  172. index int) (reflect.Value, int, error) {
  173. docs, err := batch.Documents()
  174. if err != nil {
  175. return sliceVal, index, err
  176. }
  177. for _, doc := range docs {
  178. if sliceVal.Len() == index {
  179. // slice is full
  180. newElem := reflect.New(elemType)
  181. sliceVal = reflect.Append(sliceVal, newElem.Elem())
  182. sliceVal = sliceVal.Slice(0, sliceVal.Cap())
  183. }
  184. currElem := sliceVal.Index(index).Addr().Interface()
  185. if err = bson.UnmarshalWithRegistry(c.registry, doc, currElem); err != nil {
  186. return sliceVal, index, err
  187. }
  188. index++
  189. }
  190. return sliceVal, index, nil
  191. }
  192. func (c *Cursor) closeImplicitSession() {
  193. if c.clientSession != nil && c.clientSession.SessionType == session.Implicit {
  194. c.clientSession.EndSession()
  195. }
  196. }
  197. // BatchCursorFromCursor returns a driver.BatchCursor for the given Cursor. If there is no underlying driver.BatchCursor,
  198. // nil is returned. This method is deprecated and does not have any stability guarantees. It may be removed in the future.
  199. func BatchCursorFromCursor(c *Cursor) *driver.BatchCursor {
  200. bc, _ := c.bc.(*driver.BatchCursor)
  201. return bc
  202. }