17

After using InsertOne to create a new document, when I return the result I'm getting an array of numbers rather than an ObjectID. In the DB, the id is generating fine.

type User struct {
  ID       string
  Email    string
  Username string
  Password string
}

var db = ...

// UserStore creates user
func UserStore(c echo.Context) (err error) {

  coll := db.Collection("users")

  u := new(User)
  if err = c.Bind(u); err != nil {
    return c.JSON(http.StatusInternalServerError, err)
  }

  result, err := coll.InsertOne(
    context.Background(),
    bson.NewDocument(
        bson.EC.String("email", u.Email),
        bson.EC.String("username", u.Username),
        bson.EC.String("password", u.Password),
    ),
  )
  if err != nil {
    return c.JSON(http.StatusInternalServerError, err)
  }

  return c.JSON(http.StatusCreated, result)
}

This returns something like InsertedID: [90, 217, 85, 109, 184, 249, 162, 204, 249, 103, 214, 121] instead of a normal ObjectID. How can I return the actual ObjectID from the newly inserted document?

icza
  • 389,944
  • 63
  • 907
  • 827
kue
  • 341
  • 1
  • 4
  • 11

1 Answers1

48

A successful Collection.InsertOne() will return a result of type mongo.InsertOneResult, which is a struct wrapping the ID of the newly inserted document:

type InsertOneResult struct {
    // The identifier that was inserted.
    InsertedID interface{}
}

The official MongoDB Go driver uses the primitive.ObjectID type to represent MongoDB ObjectIds. This type is a simple byte array:

type ObjectID [12]byte

To access this type you need to import the primitive package.

import "go.mongodb.org/mongo-driver/bson/primitive"

InsertOneResult.InsertedID will hold a dynamic type of primitive.ObjectID. The primitive.ObjectID type does not define a custom JSON marshaling method (does not implement json.Marshaler), which means when the result is converted to JSON, the default marshaling rules will be used, which for a byte array (not slice) is what you see: the decimal representation of the bytes of the ObjectID.

You should not convert a value of type InsertOneResult to JSON, as it (or rather primitive.ObjectID itself) is not "JSON-friendly" (at least not in the current version).

Instead create / wrap your own type where you define how you want the result to look like in JSON, for example:

if oid, ok := result.InsertedID.(primitive.ObjectID); ok {
    return c.JSON(http.StatusCreated, map[string]interface{}{
        "id": oid.String(),
    })
} else {
    // Not objectid.ObjectID, do what you want
}

The above code would result in a JSON response like this:

{"id":"ObjectID(\"5ad9a913478c26d220afb681\")"}

Or if you just want the hex representation:

if oid, ok := result.InsertedID.(primitive.ObjectID); ok {
    return c.JSON(http.StatusCreated, map[string]interface{}{
        "id": oid.Hex(),
    })
}

Which would be:

{"id":"5ad9a913478c26d220afb681"}
Adam D
  • 1,962
  • 2
  • 21
  • 37
icza
  • 389,944
  • 63
  • 907
  • 827
  • 1
    Great answer! But now things have changed and instead of using `objectid.ObjectID` you need to use [primitive.ObjectID](https://godoc.org/go.mongodb.org/mongo-driver/bson/primitive#ObjectID) – Adam D Jul 16 '19 at 07:44
  • 1
    @AdamD Yes, you're right. Answer was created prior to `primitive.ObjectID`. – icza Jul 16 '19 at 07:55
  • 2
    the answer is correct and for getting `objectid` you can user primitive.`primitive.ObjectIDFromHex("your generated hex")` which will return `ibjectid` instead of hex-decimal string presentation. – Manouchehr Rasouli Apr 21 '20 at 06:51
  • new to golang. I don't exactly understand when you said `result.InsertedID.(primitive.ObjectID)` what are you doing here. `InsertedID` is empty interface, so... – Tannu Dec 11 '21 at 01:26
  • 1
    @Tannu It's a type assertion, see [Is this casting in golang?](https://stackoverflow.com/questions/31379404/is-this-casting-in-golang/31379484#31379484) – icza Dec 11 '21 at 08:06