This code is working fine in go mgo library
result interface{}
err = getCollection.Find(bson.M{}).Select(bson.M{"_id": 1}).One(&result)
but I want to perform this using go mongo-driver library
I have tried below code but it is not working as the above one
err = getCollection.FindOne(ctx, bson.M{}, options.FindOne().SetProjection(bson.M{"_id": 1})).Decode(&result)
My test collection data is
example{
"_id":ObjectId(),
"Name":"qwert"
}
Anyone suggest me how can we achieve this in mongo-driver?
i can't comment on your question because i am new contributor here, i am using mongo-driver in my project now, i have tried to fetch only projection only some fields to be show up,
can you specific the argument on second for filtering ?
var (
opt options.FindOneOptions
modelStruct model.Person
)
filter := bson.M{"email": "hello#test.com"}
opt.SetProjection(bson.M{"name": 1})
err := collection.findOne(context.Background(), filter, opt).Decode(&modelStruct)
if that doesn't work, then you should limit the struct , make sure in your model.Person has data like this
type Person struct {
Name string `json:"name" bson:"name"`
Gender string `json:"gender" bson:"gender"`
}
or you can just make own model for limit the fields:
var personLimitOnlyGetName struct {
Name string `json:"name" bson:"name"`
}
// please look carefully in your collection field for camelCase
opt.SetProjection(bson.M{"name": 1})
I'm trying to save user entries in a MongoDB database with Go. Users should get an ID automatically. I'm using the offical MongoDB Go driver.
My sources were especially https://vkt.sh/go-mongodb-driver-cookbook/ and https://www.mongodb.com/blog/post/mongodb-go-driver-tutorial.
Struct looks like this:
type User struct {
ID primitive.ObjectID `json:"_id" bson:"_id"`
Fname string `json:"fname" bson:"fname"`
Lname string `json:"lname" bson:"lname"`
Mail string `json:"mail" bson:"mail"`
Password string `json:"password" bson:"password"`
Street string `json:"street" bson:"street"`
Zip string `json:"zip" bson:"zip"`
City string `json:"city" bson:"city"`
Country string `json:"country" bson:"country"`
}
Setting up the database (connection works) and signing up users (based on an HTTP-Request r with a user in it's body):
ctx := context.Background()
uriDB := "someURI"
clientOptions := options.Client().ApplyURI(uriDB)
client, err := mongo.Connect(ctx, clientOptions)
collection := client.Database("guDB").Collection("users")
...
var user User
err := json.NewDecoder(r.Body).Decode(&user)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result, err := collection.InsertOne(ctx, user)
...
When I enter the first user, it is added to the collection, but the ID looks like this:
_id:ObjectID(000000000000000000000000)
If I now want to enter another user, I get the following error:
multiple write errors: [{write errors: [{E11000 duplicate key error collection: guDB.users index: _id_ dup key: { : ObjectId('000000000000000000000000') }}]}, {<nil>}]
So it seems like again ObjectID 000000000000000000000000 is assigned.
I expected the ID to be automatically set to a unique value for each entry.
Do I have to manually set the ID or how can I assign unique IDs to users?
Per the documentation you linked, you must set the object ID yourself when using structs:
_, err := col.InsertOne(ctx, &Post{
ID: primitive.NewObjectID(), // <-- this line right here
Title: "post",
Tags: []string{"mongodb"},
Body: `blog post`,
CreatedAt: time.Now(),
})
The examples before that using bson.M don't need to specify an ID because they don't send the _id field at all; with a struct, the field is being sent with its zero value (as you've seen).
If you set a document _id, mongodb will use that _id for the document during insertion and will not generate. You have to either omit it, or set it manually with primitive.NewObjectID().
For the record, I'm learning Go. I'm trying to use and the mgo package and I'd like to insert a new document and return this newly created document to user (I'm trying to write a basic API). I've wrote the following code:
EDIT: Here's the struct for the model:
type Book struct {
ISBN string `json:"isbn"`
Title string `json:"title"`
Authors []string `json:"authors"`
Price string `json:"price"`
}
session := s.Copy()
defer session.Close()
var book Book
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&book)
if err != nil {
ErrorWithJSON(w, "Incorrect body", http.StatusBadRequest)
return
}
c := session.DB("store").C("books")
info, err := c.Upsert(nil, book)
if err != nil {
ErrorWithJSON(w, "Database error", http.StatusInternalServerError)
log.Println("Failed insert book: ", err)
return
}
respBody, err := json.MarshalIndent(info, "", " ")
if err != nil {
log.Fatal(err)
}
ResponseWithJSON(w, respBody, http.StatusOK)
Please note that Book is a struct I have created earlier. The above code does work but what it returns is the upsert result like so:
{
"Updated": 1,
"Removed": 0,
"Matched": 1,
"UpsertedId": null
}
Which is not the recently created object. How can I get the the recently created object to return as a response (please note that ideally I'd like the confirmation that the document was successfully inserted. I have seen other questions where the ID is generated beforehand but for what I've seen it doesn't confirm that the document was created was it?)
Let's clear the concepts first. In MongoDB, each document must have an _id property which acts as its unique document identifier inside a collection. Either you provide the value of this _id or it is assigned automatically by MongoDB.
So it would be ideal (or it's strongly recommended) for your model types to include a field for the _id document identifier. Since we're talking about books here, books already have a unique identifier called ISBN, which you may opt to use as the value of the _id field.
The mapping between MongoDB fields and Go struct fields must be specified using the bson tag (not json). So you should provide bson tag values along with json tags.
So change your model to:
type Book struct {
ISBN string `json:"isbn" bson:"_id"`
Title string `json:"title" bson:"title"`
Authors []string `json:"authors" bson:"authors"`
Price string `json:"price" bson:"price"`
}
If you want to insert a new document (a new book), you should always use Collection.Insert().
And what will be the ID of the newly inserted document? The field you set to the Book.ISBN field as we declared it to be the document ID with the bson:"_id" tag.
You should only use Collection.Upsert() if you are not sure whether the document already exists, but either way you want it to be the document you have at hand. Collection.Upsert() will try to find a document to update, and if one is found, that will be updated. If no document is found, then an insert operation will be performed. The first parameter is the selector to find the document to be updated. Since you passed nil, that means any document may qualify, so one will be selected "randomly". So if you already have books saved, any may get selected and get overwritten. This is certainly not want you want.
Since now the ISBN is the ID, you should specify a selector that filters by ISBN, like this:
info, err := c.Upsert(bson.M{"_id": book.ISBN}, book)
Or since we're filtering by ID, use the more convenient Collection.UpsertId():
info, err := c.UpsertId(book.ISBN, book)
If you want to update an existing document, for that you may use Collection.Update(). This is similar to Collection.Upsert(), but the difference is that if no documents match the selector, an insert will not be performed. Updating a document matched by ID can also be done with the more convenient Collection.UpdateId() (which is analogue to Collection.UpsertId()).
For other documents which do not have a unique identifier naturally (like books having ISBN), you may use generated IDs. The mgo library provides the bson.NewObjectId() function for such purpose, which returns you a value of type bson.ObjectId. When saving new documents with Collection.Insert(), you can acquire a new unique ID with bson.NewObjectId() and assign it to the struct field that is mapped to the MongoDB _id property. If the insert succeeds, you can be sure the document's ID is what you just set before calling Collection.Insert(). This ID generation is designed to work even in a distributed environment, so it will generate unique IDs even if 2 of your nodes attempt to generate an ID at the same time.
So for example if you don't have the ISBN for a book when saving it, then you must have a separate, designated ID field in your Book type, for example:
type Book struct {
ID bson.ObjectId `bson:"_id"`
ISBN string `json:"isbn" bson:"isbn"`
Title string `json:"title" bson:"title"`
Authors []string `json:"authors" bson:"authors"`
Price string `json:"price" bson:"price"`
}
And when saving a new book:
var book Book
// Fill what you have about the book
book.ID = bson.NewObjectId()
c := session.DB("store").C("books")
err = c.Insert(book)
// check error
// If no error, you can refer to this document via book.ID
Banged my head for a while on this.
info, err := c.Upsert(nil, book)
The nil selector for the upsert will match everything so if your collection is empty the selector won't match and all will be fine, the info object will contain the ObjectId in the UpsertedId field, but on every following upsert with nil selector the collection will have records and the nil selector for the upsert will match, therefore it won't return you the UpsertedId and you will be updating the first matched record.
You could use never matching selector for the upsert (which was my solution), but note that this way you will only insert and get the inserted ObjectId and never update a record.
You can checkout the following test:
func TestFoo(t *testing.T) {
foo := struct {
Bar string `bson:"bar"`
}{
Bar: "barrr",
}
session, _ := mgo.DialWithInfo(&mgo.DialInfo{
Addrs: []string{"127.0.0.1:27017"},
})
coll := session.DB("foo").C("bar")
coll.DropCollection()
info, _ := coll.Upsert(nil, foo) // will insert
count, _ := coll.Count()
fmt.Printf("%+v, collecton records:%v\n", info, count) // &{Updated:0 Removed:0 Matched:0 UpsertedId:ObjectIdHex("5c35e8ee1f5b80c932b44afb")}, collection records:1
info, _ = coll.Upsert(bson.M{}, foo) // will update
count, _ = coll.Count()
fmt.Printf("%+v, collecton records:%v\n", info, count) // &{Updated:1 Removed:0 Matched:1 UpsertedId:<nil>}, collection records:1
info, _ = coll.Upsert(bson.M{"nonsense": -1}, foo) // will insert duplicate record (due to the selector that will never match anything)
count, _ = coll.Count()
fmt.Printf("%+v, collecton records:%v\n", info, count) // &{Updated:0 Removed:0 Matched:0 UpsertedId:ObjectIdHex("5c35ea2a1f5b80c932b44b1d")}, collection records:2
foo.Bar = "baz"
info, _ = coll.Upsert(nil, foo) // will update the first matched (since the selector matches everything)
count, _ = coll.Count()
fmt.Printf("%+v, collecton records:%v\n", info, count)
// after the test the collection will have the following records
//> use foo
//> db.bar.find()
//{ "_id" : ObjectId("5c35ebf41f5b80c932b44b81"), "bar" : "baz" } // the first matched on the last update with nil selector
//{ "_id" : ObjectId("5c35ebf41f5b80c932b44b86"), "bar" : "barrr" } // the duplicated record with the selector that never matches anything
}
EDIT: Note that you should have an index on the never matching field, or your insert query will take too long if you have many records, because the upsert on not indexed filed will scan all the documents in the collection.
Can we update sub-document array fields along with other document fields in Mgo?
If so, please help me with my query.
c := db.C("user")
colQuerier := bson.M{"email": *olduname}
change := bson.M{"$set":bson.M{"password":*pwd, "place":*place, "emails.$.received":*received,"emails.$.sent":*sent}}
err := c.Update(colQuerier, change)
My Database Structs are as follows:
type Emails struct{
Id bson.ObjectId `bson:"_id,omitempty"`
Received string
Sent string
}
type User struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Email string
Password string
Place string
Emails
}
I am getting a run time error saying: The positional operator did not find the match needed from the query. Unexpanded update: emails.$.received
It should be emails.received as received is not an array, so you don't need the positional operator $:
c := db.C("user")
colQuerier := bson.M{"email": *olduname}
change := bson.M{"$set":bson.M{"password":*pwd, "place":*place, "emails.received":*received}}
err := c.Update(colQuerier, change)
I'm still new to go and while I see multiple questions on SO similar to this, I'm unable to reproduce the output some OP's had requested (this answer looking the closest).
I'm doing something fairly simple, I'm hitting a users collection in mongo and all I want to do is get the _id value back as a string. I'm going to eventually push these _id's up to NSQ but that's the brunt of my task.
var users []bson.M
err = sess.DB("db_name").C("users").Find(bson.M{}).All(&users)
if err != nil {
os.Exit(1)
}
for _, user := range users {
fmt.Printf("%+v \n", user["_id"])
}
Today this outputs:
ObjectIdHex("537f700b537461b70c5f0000")
ObjectIdHex("537f700b537461b70c600000")
ObjectIdHex("537f700b537461b70c610000")
ObjectIdHex("537f700b537461b70c620000")
I went through the bson#m docs and thought I was correctly using the map in order to extra the value. So I think, my query results in:
{"_id" : ObjectIdHex("Some_ID") }
but if ObjectIdHex("ID") is the value, how do I simply get the string within there.
So ideal output:
"537f700b537461b70c5f0000"
"537f700b537461b70c600000"
"537f700b537461b70c610000"
"537f700b537461b70c620000"
The value associated with key "_id" is of type bson.ObjectId which is simply a string.
bson.M is a type map[string]interface{}, so you need Type assertion to get the id as an ObjectId:
objid, ok := m["_id"].(ObjectId)
if !ok {
panic("Not ObjectId")
}
And the ObjectId has a ObjectId.Hex() method which returns exactly what you want: the object id as a "pure" hex string:
fmt.Println(objid.Hex())
Alternatives
objid can simply be converted to string because its underlying type is string. So you can use a number of further options to convert it to a hex string:
hexid := fmt.Sprintf("%x", string(objid))
If you just want to print it, you can do directly:
fmt.Printf("%x", string(objid))
Note: Converting it to string is important else the fmt package would call its String() method which results in a string like ObjectIdHex("537f700b537461b70c5f0000") and this is what would be converted to hex which is clearly not what you want.
Alternatively you can use the encoding/hex package and the hex.EncodeToString() function:
hexid := hex.EncodeToString([]byte(objid))