go-bongo / bongo

Go ODM for MongoDB
MIT License
487 stars 40 forks source link

Indexes? #7

Closed secretfader closed 9 years ago

secretfader commented 9 years ago

How would I go about creating indexes with Bongo? I have several collections that will require unique indexes, and would love to use Bongo for modeling. (I assume once I have the constraints set up, I'll be able to parse any validation errors, including unique constraint errors, during the middleware hooks – correct?)

jraede commented 9 years ago

You can create indexes using the raw mgo collection (http://godoc.org/gopkg.in/mgo.v2#Collection.EnsureIndex)

For example,

err := myBongoCollection.Collection().Ensureindex(...)

RE: validation, the Collection.Save function will return a ValidationError if it failed because of validation (you can define the validation logic in the validation hook in your model. So you can do something like this:

err := myCollection.Save(myModel)
if vErr, ok := err.(*bongo.ValidationError); ok {
    fmt.Println("Validation errors are:", vErr.Errors)
} else {
    fmt.Println("Got a real error:", err.Error())
}
secretfader commented 9 years ago

Great, thanks @jraede. One more question. This code, based loosely on the README, seems to error. Granted, I'm new to Go, but this seems a bit perplexing.

package main

import (
  "github.com/maxwellhealth/bongo"
  "log"
  "fmt"
)

type Album struct {
  bongo.DocumentBase `bson:",inline"`
  Title string
}

func main () {
  dbConfig := &bongo.Config {
    ConnectionString: "localhost",
    Database: "godev",
  }

  connection, err := bongo.Connect(dbConfig)

  if (err != nil) {
    log.Fatal(err)
  }

  newAlbum := &Album {
    Title: "Signs",
  }

  errX := connection.Collection("albums").Save(newAlbum)

  if vErr, ok := errX.(*bongo.ValidationError); ok {
    fmt.Println("Validation errors are:", vErr.Errors)
  } else {
    fmt.Println("Got a real error:", errX.Error())
  }

  fmt.Printf("All good: %v", newAlbum)
}
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x20 pc=0x2533]

goroutine 1 [running]:
main.main()
    /Users/nicholaswyoung/go/src/github.com/nicholaswyoung/mixdown/db.go:36 +0x533

goroutine 5 [sleep]:
gopkg.in/mgo%2ev2.(*mongoCluster).syncServersLoop(0xc208042000)
    /Users/nicholaswyoung/go/src/gopkg.in/mgo.v2/cluster.go:366 +0x291
created by gopkg.in/mgo%2ev2.newCluster
    /Users/nicholaswyoung/go/src/gopkg.in/mgo.v2/cluster.go:76 +0x20b

goroutine 9 [IO wait]:
net.(*pollDesc).Wait(0xc208010220, 0x72, 0x0, 0x0)
    /usr/local/Cellar/go/1.4.2/libexec/src/net/fd_poll_runtime.go:84 +0x47
net.(*pollDesc).WaitRead(0xc208010220, 0x0, 0x0)
    /usr/local/Cellar/go/1.4.2/libexec/src/net/fd_poll_runtime.go:89 +0x43
net.(*netFD).Read(0xc2080101c0, 0xc20803a810, 0x24, 0x24, 0x0, 0x511a50, 0xc20800b4b0)
    /usr/local/Cellar/go/1.4.2/libexec/src/net/fd_unix.go:242 +0x40f
net.(*conn).Read(0xc208038058, 0xc20803a810, 0x24, 0x24, 0x0, 0x0, 0x0)
    /usr/local/Cellar/go/1.4.2/libexec/src/net/net.go:121 +0xdc
gopkg.in/mgo%2ev2.fill(0x512c70, 0xc208038058, 0xc20803a810, 0x24, 0x24, 0x0, 0x0)
    /Users/nicholaswyoung/go/src/gopkg.in/mgo.v2/socket.go:504 +0x75
gopkg.in/mgo%2ev2.(*mongoSocket).readLoop(0xc2080442a0)
    /Users/nicholaswyoung/go/src/gopkg.in/mgo.v2/socket.go:521 +0x126
created by gopkg.in/mgo%2ev2.newSocket
    /Users/nicholaswyoung/go/src/gopkg.in/mgo.v2/socket.go:168 +0x3d3

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
    /usr/local/Cellar/go/1.4.2/libexec/src/runtime/asm_amd64.s:2232 +0x1

goroutine 10 [sleep]:
gopkg.in/mgo%2ev2.(*mongoServer).pinger(0xc2080440e0, 0x1)
    /Users/nicholaswyoung/go/src/gopkg.in/mgo.v2/server.go:297 +0x169
created by gopkg.in/mgo%2ev2.newServer
    /Users/nicholaswyoung/go/src/gopkg.in/mgo.v2/server.go:90 +0x196
exit status 2
jraede commented 9 years ago

What's on mixdown/db.go on line 36? Can you provide that whole file? Line 36 of the one you pasted is }, which doesn't make much sense. Do you maybe have comments at the top?

jraede commented 9 years ago

Oh, you aren't checking if the returned errX is nil, so when you try to run errX.Error() you get the nil pointer dereference. Code should be:

package main

import (
  "github.com/maxwellhealth/bongo"
  "log"
  "fmt"
)

type Album struct {
  bongo.DocumentBase `bson:",inline"`
  Title string
}

func main () {
  dbConfig := &bongo.Config {
    ConnectionString: "localhost",
    Database: "godev",
  }

  connection, err := bongo.Connect(dbConfig)

  if (err != nil) {
    log.Fatal(err)
  }

  newAlbum := &Album {
    Title: "Signs",
  }

  errX := connection.Collection("albums").Save(newAlbum)
  if errX != nil {
    if vErr, ok := errX.(*bongo.ValidationError); ok {
      fmt.Println("Validation errors are:", vErr.Errors)
    } else {
      fmt.Println("Got a real error:", errX.Error())
    }
  }

  fmt.Printf("All good: %v", newAlbum)
}

Or you could also do:

  errX := connection.Collection("albums").Save(newAlbum)
  if vErr, ok := errX.(*bongo.ValidationError); ok {
    fmt.Println("Validation errors are:", vErr.Errors)
  } else if errX != nil {
    fmt.Println("Got a real error:", errX.Error())
  } else {
    fmt.Printf("All good: %v", newAlbum)
  }
secretfader commented 9 years ago

Wow. That was a rookie mistake. Thanks for patiently pointing it out.

jraede commented 9 years ago

No problem

secretfader commented 9 years ago

@jraede Where would you recommend putting the EnsureIndex call, given that each Model will probably be defined in it's own file. It seems preferable to do all initialization of that model in the same file.

jraede commented 9 years ago

I usually just create a separate executable file for ensuring indexes and run that manually. It's generally not a good idea to run EnsureIndex every time your application boots if you don't need to.