Uploaded image for project: 'Go Driver'
  1. Go Driver
  2. GODRIVER-1565

Consolidate IndexModel spec generation helpers and add docs to require order-preserving types



    • Type: Improvement
    • Status: Closed
    • Priority: Major - P3
    • Resolution: Fixed
    • Affects Version/s: 1.3.2
    • Fix Version/s: 1.4.0
    • Component/s: Core API
    • Security Level: Public
    • Labels:


      EDIT: We've had a number of users try to use maps for the IndexModel.Keys value. This is incorrect because index key specs are ordered, but Go maps do not guarantee that items will be returned in insertion order when reading. We should improve the docs for the Keys field to mention that an order-preserving type is required, similar to the docs we have for Database.RunCommand.

      While we're here, we can also consolidate some of the logic in the index name generation code by first calling transformBsoncoreDocument to create the keys spec and passing that to getOrGenerateIndexName to loop over it. This will likely be a small perf improvement, but it'll still be incorrect to use an unordered type for Keys.



      If you want to create a compound index you should fill the following struct:


      // IndexModel represents a new index to be created.
      type IndexModel struct {
      	// A document describing which keys should be used for the index. It cannot be nil. See
      	// https://docs.mongodb.com/manual/indexes/#indexes for examples of valid documents.
      	Keys interface{}
      	// The options to use to create the index.
      	Options *options.IndexOptions

      Here the "keys" should be of type map. Let's assume we want to create the following compound index (without any options):

      { "item": 1, "stock": -1 }


      The code could create all of the following indexes:

      1. index { "item": 1, "stock": -1 } with name item_1_stock_-1

      2. index { "item": 1, "stock": -1 } with name stock_-1_item_1

      3. index { "stock": -1, "item": 1 } with name item_1_stock_-1

      4. index { "stock": -1, "item": 1 } with name stock_-1_item_1



      First of all, it's important to understand why do we have this bug. in Go when you want to go over all the keys in map you use 'range' clause in a for statement. The problem is that according to the spec: "The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next."

      So if we insert a map to this IndexModel struct the order is not guaranteed ! and as we know it's important in the case of compound index.


      Ok, but why do we have 4 options in the example above and not two?


      The reason is that if you don't specify a name in the 'Index Option' it will generate one for you. The function that generates the name and the function that generates the keys are two different functions! they both go over the same map but do different things.

      And this is the two functions that I'm referring to:


      func getOrGenerateIndexName(registry *bsoncodec.Registry, model IndexModel) (string, error)

      func transformBsoncoreDocument(registry *bsoncodec.Registry, val interface{}) (bsoncore.Document, error)







            divjot.arora Divjot Arora
            tamirav@checkpoint.com Tamir Aviv
            0 Vote for this issue
            2 Start watching this issue