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.

index_view.go 12 kB

4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  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. "bytes"
  9. "context"
  10. "errors"
  11. "fmt"
  12. "strconv"
  13. "time"
  14. "go.mongodb.org/mongo-driver/bson"
  15. "go.mongodb.org/mongo-driver/bson/bsoncodec"
  16. "go.mongodb.org/mongo-driver/bson/bsontype"
  17. "go.mongodb.org/mongo-driver/mongo/options"
  18. "go.mongodb.org/mongo-driver/mongo/readpref"
  19. "go.mongodb.org/mongo-driver/mongo/writeconcern"
  20. "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
  21. "go.mongodb.org/mongo-driver/x/mongo/driver"
  22. "go.mongodb.org/mongo-driver/x/mongo/driver/description"
  23. "go.mongodb.org/mongo-driver/x/mongo/driver/operation"
  24. "go.mongodb.org/mongo-driver/x/mongo/driver/session"
  25. )
  26. // ErrInvalidIndexValue indicates that the index Keys document has a value that isn't either a number or a string.
  27. var ErrInvalidIndexValue = errors.New("invalid index value")
  28. // ErrNonStringIndexName indicates that the index name specified in the options is not a string.
  29. var ErrNonStringIndexName = errors.New("index name must be a string")
  30. // ErrMultipleIndexDrop indicates that multiple indexes would be dropped from a call to IndexView.DropOne.
  31. var ErrMultipleIndexDrop = errors.New("multiple indexes would be dropped")
  32. // IndexView is used to create, drop, and list indexes on a given collection.
  33. type IndexView struct {
  34. coll *Collection
  35. }
  36. // IndexModel contains information about an index.
  37. type IndexModel struct {
  38. Keys interface{}
  39. Options *options.IndexOptions
  40. }
  41. func isNamespaceNotFoundError(err error) bool {
  42. if de, ok := err.(driver.Error); ok {
  43. return de.Code == 26
  44. }
  45. return false
  46. }
  47. // List returns a cursor iterating over all the indexes in the collection.
  48. func (iv IndexView) List(ctx context.Context, opts ...*options.ListIndexesOptions) (*Cursor, error) {
  49. if ctx == nil {
  50. ctx = context.Background()
  51. }
  52. sess := sessionFromContext(ctx)
  53. if sess == nil && iv.coll.client.topology.SessionPool != nil {
  54. var err error
  55. sess, err = session.NewClientSession(iv.coll.client.topology.SessionPool, iv.coll.client.id, session.Implicit)
  56. if err != nil {
  57. return nil, err
  58. }
  59. }
  60. err := iv.coll.client.validSession(sess)
  61. if err != nil {
  62. closeImplicitSession(sess)
  63. return nil, err
  64. }
  65. readSelector := description.CompositeSelector([]description.ServerSelector{
  66. description.ReadPrefSelector(readpref.Primary()),
  67. description.LatencySelector(iv.coll.client.localThreshold),
  68. })
  69. op := operation.NewListIndexes().
  70. Session(sess).CommandMonitor(iv.coll.client.monitor).
  71. ServerSelector(readSelector).ClusterClock(iv.coll.client.clock).
  72. Database(iv.coll.db.name).Collection(iv.coll.name).
  73. Deployment(iv.coll.client.topology)
  74. var cursorOpts driver.CursorOptions
  75. lio := options.MergeListIndexesOptions(opts...)
  76. if lio.BatchSize != nil {
  77. op = op.BatchSize(*lio.BatchSize)
  78. cursorOpts.BatchSize = *lio.BatchSize
  79. }
  80. if lio.MaxTime != nil {
  81. op = op.MaxTimeMS(int64(*lio.MaxTime / time.Millisecond))
  82. }
  83. retry := driver.RetryNone
  84. if iv.coll.client.retryReads {
  85. retry = driver.RetryOncePerCommand
  86. }
  87. op.Retry(retry)
  88. err = op.Execute(ctx)
  89. if err != nil {
  90. // for namespaceNotFound errors, return an empty cursor and do not throw an error
  91. closeImplicitSession(sess)
  92. if isNamespaceNotFoundError(err) {
  93. return newEmptyCursor(), nil
  94. }
  95. return nil, replaceErrors(err)
  96. }
  97. bc, err := op.Result(cursorOpts)
  98. if err != nil {
  99. closeImplicitSession(sess)
  100. return nil, replaceErrors(err)
  101. }
  102. cursor, err := newCursorWithSession(bc, iv.coll.registry, sess)
  103. return cursor, replaceErrors(err)
  104. }
  105. // CreateOne creates a single index in the collection specified by the model.
  106. func (iv IndexView) CreateOne(ctx context.Context, model IndexModel, opts ...*options.CreateIndexesOptions) (string, error) {
  107. names, err := iv.CreateMany(ctx, []IndexModel{model}, opts...)
  108. if err != nil {
  109. return "", err
  110. }
  111. return names[0], nil
  112. }
  113. // CreateMany creates multiple indexes in the collection specified by the models. The names of the
  114. // created indexes are returned.
  115. func (iv IndexView) CreateMany(ctx context.Context, models []IndexModel, opts ...*options.CreateIndexesOptions) ([]string, error) {
  116. names := make([]string, 0, len(models))
  117. var indexes bsoncore.Document
  118. aidx, indexes := bsoncore.AppendArrayStart(indexes)
  119. for i, model := range models {
  120. if model.Keys == nil {
  121. return nil, fmt.Errorf("index model keys cannot be nil")
  122. }
  123. name, err := getOrGenerateIndexName(iv.coll.registry, model)
  124. if err != nil {
  125. return nil, err
  126. }
  127. names = append(names, name)
  128. keys, err := transformBsoncoreDocument(iv.coll.registry, model.Keys)
  129. if err != nil {
  130. return nil, err
  131. }
  132. var iidx int32
  133. iidx, indexes = bsoncore.AppendDocumentElementStart(indexes, strconv.Itoa(i))
  134. indexes = bsoncore.AppendDocumentElement(indexes, "key", keys)
  135. if model.Options == nil {
  136. model.Options = options.Index()
  137. }
  138. model.Options.SetName(name)
  139. optsDoc, err := iv.createOptionsDoc(model.Options)
  140. if err != nil {
  141. return nil, err
  142. }
  143. indexes = bsoncore.AppendDocument(indexes, optsDoc)
  144. indexes, err = bsoncore.AppendDocumentEnd(indexes, iidx)
  145. if err != nil {
  146. return nil, err
  147. }
  148. }
  149. indexes, err := bsoncore.AppendArrayEnd(indexes, aidx)
  150. if err != nil {
  151. return nil, err
  152. }
  153. sess := sessionFromContext(ctx)
  154. if sess == nil && iv.coll.client.topology.SessionPool != nil {
  155. sess, err = session.NewClientSession(iv.coll.client.topology.SessionPool, iv.coll.client.id, session.Implicit)
  156. if err != nil {
  157. return nil, err
  158. }
  159. defer sess.EndSession()
  160. }
  161. err = iv.coll.client.validSession(sess)
  162. if err != nil {
  163. return nil, err
  164. }
  165. selector := iv.coll.writeSelector
  166. if sess != nil && sess.PinnedServer != nil {
  167. selector = sess.PinnedServer
  168. }
  169. option := options.MergeCreateIndexesOptions(opts...)
  170. op := operation.NewCreateIndexes(indexes).
  171. Session(sess).ClusterClock(iv.coll.client.clock).
  172. Database(iv.coll.db.name).Collection(iv.coll.name).CommandMonitor(iv.coll.client.monitor).
  173. Deployment(iv.coll.client.topology).ServerSelector(selector)
  174. if option.MaxTime != nil {
  175. op.MaxTimeMS(int64(*option.MaxTime / time.Millisecond))
  176. }
  177. err = op.Execute(ctx)
  178. if err != nil {
  179. return nil, err
  180. }
  181. return names, nil
  182. }
  183. func (iv IndexView) createOptionsDoc(opts *options.IndexOptions) (bsoncore.Document, error) {
  184. optsDoc := bsoncore.Document{}
  185. if opts.Background != nil {
  186. optsDoc = bsoncore.AppendBooleanElement(optsDoc, "background", *opts.Background)
  187. }
  188. if opts.ExpireAfterSeconds != nil {
  189. optsDoc = bsoncore.AppendInt32Element(optsDoc, "expireAfterSeconds", *opts.ExpireAfterSeconds)
  190. }
  191. if opts.Name != nil {
  192. optsDoc = bsoncore.AppendStringElement(optsDoc, "name", *opts.Name)
  193. }
  194. if opts.Sparse != nil {
  195. optsDoc = bsoncore.AppendBooleanElement(optsDoc, "sparse", *opts.Sparse)
  196. }
  197. if opts.StorageEngine != nil {
  198. doc, err := transformBsoncoreDocument(iv.coll.registry, opts.StorageEngine)
  199. if err != nil {
  200. return nil, err
  201. }
  202. optsDoc = bsoncore.AppendDocumentElement(optsDoc, "storageEngine", doc)
  203. }
  204. if opts.Unique != nil {
  205. optsDoc = bsoncore.AppendBooleanElement(optsDoc, "unique", *opts.Unique)
  206. }
  207. if opts.Version != nil {
  208. optsDoc = bsoncore.AppendInt32Element(optsDoc, "v", *opts.Version)
  209. }
  210. if opts.DefaultLanguage != nil {
  211. optsDoc = bsoncore.AppendStringElement(optsDoc, "default_language", *opts.DefaultLanguage)
  212. }
  213. if opts.LanguageOverride != nil {
  214. optsDoc = bsoncore.AppendStringElement(optsDoc, "language_override", *opts.LanguageOverride)
  215. }
  216. if opts.TextVersion != nil {
  217. optsDoc = bsoncore.AppendInt32Element(optsDoc, "textIndexVersion", *opts.TextVersion)
  218. }
  219. if opts.Weights != nil {
  220. doc, err := transformBsoncoreDocument(iv.coll.registry, opts.Weights)
  221. if err != nil {
  222. return nil, err
  223. }
  224. optsDoc = bsoncore.AppendDocumentElement(optsDoc, "weights", doc)
  225. }
  226. if opts.SphereVersion != nil {
  227. optsDoc = bsoncore.AppendInt32Element(optsDoc, "2dsphereIndexVersion", *opts.SphereVersion)
  228. }
  229. if opts.Bits != nil {
  230. optsDoc = bsoncore.AppendInt32Element(optsDoc, "bits", *opts.Bits)
  231. }
  232. if opts.Max != nil {
  233. optsDoc = bsoncore.AppendDoubleElement(optsDoc, "max", *opts.Max)
  234. }
  235. if opts.Min != nil {
  236. optsDoc = bsoncore.AppendDoubleElement(optsDoc, "min", *opts.Min)
  237. }
  238. if opts.BucketSize != nil {
  239. optsDoc = bsoncore.AppendInt32Element(optsDoc, "bucketSize", *opts.BucketSize)
  240. }
  241. if opts.PartialFilterExpression != nil {
  242. doc, err := transformBsoncoreDocument(iv.coll.registry, opts.PartialFilterExpression)
  243. if err != nil {
  244. return nil, err
  245. }
  246. optsDoc = bsoncore.AppendDocumentElement(optsDoc, "partialFilterExpression", doc)
  247. }
  248. if opts.Collation != nil {
  249. optsDoc = bsoncore.AppendDocumentElement(optsDoc, "collation", bsoncore.Document(opts.Collation.ToDocument()))
  250. }
  251. if opts.WildcardProjection != nil {
  252. doc, err := transformBsoncoreDocument(iv.coll.registry, opts.WildcardProjection)
  253. if err != nil {
  254. return nil, err
  255. }
  256. optsDoc = bsoncore.AppendDocumentElement(optsDoc, "wildcardProjection", doc)
  257. }
  258. return optsDoc, nil
  259. }
  260. func (iv IndexView) drop(ctx context.Context, name string, opts ...*options.DropIndexesOptions) (bson.Raw, error) {
  261. if ctx == nil {
  262. ctx = context.Background()
  263. }
  264. sess := sessionFromContext(ctx)
  265. if sess == nil && iv.coll.client.topology.SessionPool != nil {
  266. var err error
  267. sess, err = session.NewClientSession(iv.coll.client.topology.SessionPool, iv.coll.client.id, session.Implicit)
  268. if err != nil {
  269. return nil, err
  270. }
  271. defer sess.EndSession()
  272. }
  273. err := iv.coll.client.validSession(sess)
  274. if err != nil {
  275. return nil, err
  276. }
  277. wc := iv.coll.writeConcern
  278. if sess.TransactionRunning() {
  279. wc = nil
  280. }
  281. if !writeconcern.AckWrite(wc) {
  282. sess = nil
  283. }
  284. selector := iv.coll.writeSelector
  285. if sess != nil && sess.PinnedServer != nil {
  286. selector = sess.PinnedServer
  287. }
  288. dio := options.MergeDropIndexesOptions(opts...)
  289. op := operation.NewDropIndexes(name).
  290. Session(sess).WriteConcern(wc).CommandMonitor(iv.coll.client.monitor).
  291. ServerSelector(selector).ClusterClock(iv.coll.client.clock).
  292. Database(iv.coll.db.name).Collection(iv.coll.name).
  293. Deployment(iv.coll.client.topology)
  294. if dio.MaxTime != nil {
  295. op.MaxTimeMS(int64(*dio.MaxTime / time.Millisecond))
  296. }
  297. err = op.Execute(ctx)
  298. if err != nil {
  299. return nil, replaceErrors(err)
  300. }
  301. // TODO: it's weird to return a bson.Raw here because we have to convert the result back to BSON
  302. ridx, res := bsoncore.AppendDocumentStart(nil)
  303. res = bsoncore.AppendInt32Element(res, "nIndexesWas", op.Result().NIndexesWas)
  304. res, _ = bsoncore.AppendDocumentEnd(res, ridx)
  305. return res, nil
  306. }
  307. // DropOne drops the index with the given name from the collection.
  308. func (iv IndexView) DropOne(ctx context.Context, name string, opts ...*options.DropIndexesOptions) (bson.Raw, error) {
  309. if name == "*" {
  310. return nil, ErrMultipleIndexDrop
  311. }
  312. return iv.drop(ctx, name, opts...)
  313. }
  314. // DropAll drops all indexes in the collection.
  315. func (iv IndexView) DropAll(ctx context.Context, opts ...*options.DropIndexesOptions) (bson.Raw, error) {
  316. return iv.drop(ctx, "*", opts...)
  317. }
  318. func getOrGenerateIndexName(registry *bsoncodec.Registry, model IndexModel) (string, error) {
  319. if model.Options != nil && model.Options.Name != nil {
  320. return *model.Options.Name, nil
  321. }
  322. name := bytes.NewBufferString("")
  323. first := true
  324. keys, err := transformDocument(registry, model.Keys)
  325. if err != nil {
  326. return "", err
  327. }
  328. for _, elem := range keys {
  329. if !first {
  330. _, err := name.WriteRune('_')
  331. if err != nil {
  332. return "", err
  333. }
  334. }
  335. _, err := name.WriteString(elem.Key)
  336. if err != nil {
  337. return "", err
  338. }
  339. _, err = name.WriteRune('_')
  340. if err != nil {
  341. return "", err
  342. }
  343. var value string
  344. switch elem.Value.Type() {
  345. case bsontype.Int32:
  346. value = fmt.Sprintf("%d", elem.Value.Int32())
  347. case bsontype.Int64:
  348. value = fmt.Sprintf("%d", elem.Value.Int64())
  349. case bsontype.String:
  350. value = elem.Value.StringValue()
  351. default:
  352. return "", ErrInvalidIndexValue
  353. }
  354. _, err = name.WriteString(value)
  355. if err != nil {
  356. return "", err
  357. }
  358. first = false
  359. }
  360. return name.String(), nil
  361. }