I have a struct like this:
type SavedData struct {
ID bson.ObjectId `bson:"_id"`
Data string
Date time.Time
}
I also have my
collection := database.C("coll_name")
How do I retrieve the last inserted entry in this collection ?
Thank you
The accepted answer is 5 years old. This should work Today with mongodb driver
collection.FindOne(ctx, bson.M{"$natural": -1})
Apparently mongoDB is by default sorted by insertion time according to
this question so you can just skip the first N elements of the collection like so.
var myData SavedData
dbSize, err := collection.Count()
if err != nil {
return err
}
err = c.Find(nil).skip(dbSize-1).One(&myData)
if err != nil {
return err
}
or you can search in the reverse order
c.Find(bson.M{ "$natural": -1 }).One(&myData)
You have to use options with mongo-driver if you want to get the latest document in the collection
import(
...
"go.mongodb.org/mongo-driver/mongo/options"
)
myOptions := options.FindOne()
myOptions.SetSort(bson.M{"$natural":-1})
collection.FindOne(ctx, bson.M{}, myOptions)
Related
I want to bulk update collection by slice of objectid
Here is the function for updatemany
func BulkDeleteEmailContentFieldByIds(ids []primitive.ObjectID) error {
filter := bson.M{"_id": bson.M{"$in": ids}}
update := bson.D{{"$unset", bson.D{{"email_content", 1}}}}
result, err := collection.UpdateMany(ctx, filter, update)
if err != nil {
return err
}
pp.Printf("Bulk delete removed %v document(s)\n", result.ModifiedCount)
return nil
}
var trackingLogIds []primitive.ObjectID
for _, v := range userDocs {
wg.Add(1)
go func(v bson.M) {
defer wg.Done()
// do something to return objectID
trackingLogIds = append(trackingLogIds, emaDoc["_id"].(primitive.ObjectID))
}(v)
}
emadb.BulkDeleteEmailContentFieldByIds(trackingLogIds)
Is the slice trackingLogIds is the correct way to search for documents with objectId
And is the query for updatemany i doing is right?
If not then how can i improve it.
Thanks
I had test the function i provided and its working as expected.
Find all document in slice of objectid and unset the field email_content
I have written following query which returns me records where updated_at date is greater than synced_at date from all records using mongodb library in Golang.
pipeline := []bson.M{}
filter := []string{"$updated_at", "$synced_at"}
pipeline = append(pipeline, bson.M{"$match": bson.M{"$expr": bson.M{"$gte": filter}}})
opts := options.Aggregate().SetMaxTime(2 * time.Second)
cursor, err := collection.Aggregate(ctx, pipeline, opts)
for cursor.Next(context.Background()) {
records := model.DData{}
err = cursor.Decode(&records)
}
The structure of Data is:
type DData struct {
Name string `json:"name" bson:"name"`
Data interface{} `json:"data" bson:"data"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
SyncedAt time.Time `json:"synced_at" bson:"synced_at"`
}
Data in collection is of the form:
{
"name":"Vallabh",
"data":{
"field1":"value1",
"field2":"value2",
"field3":"value3",
},
"updated_at":2021-08-17T09:43:27Z,
"synced_at":2021-08-07T09:43:27Z
}
But with above query I am getting data in the form:
{
"name":"Vallabh",
"data":[
{
"key":"field1",
"value":"value1"
},
{
"key":"field2",
"value":"value2"
},
{
"key":"field3",
"value":"value3"
}
}],
"updated_at":"2021-08-17T09:43:27Z",
"synced_at":"2021-08-07T09:43:27Z"
}
What am I doing wrong? Its happening only when field type is an interface in struct.
Try with this
type DData struct {
Name string `json:"name" bson:"name"`
Data map[string]string `json:"data" bson:"data"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
SyncedAt time.Time `json:"synced_at" bson:"synced_at"`
}
Always use the type if you know it beforehand, with interface{} the burden is on the library to find out what the type is. If you expect the varied values in the map you can use map[string]interface{}
Found a very weird solution to this problem where I decoded it to bson.M{}, then marshalled it and unmarshalled it to my struct. I am sure there are better ways but this one worked for me.
for cursor.Next(context.Background()) {
tempResult := bson.M{}
err = decode(cursor, &tempResult)
if err != nil {
logger.LogErrorf(err, "error while decoding")
continue
}
obj, err := marshal(tempResult)
if err != nil {
logger.LogErrorf(err, "error while marshalling")
continue
}
var data model.DData
err = json.Unmarshal(obj, &data)
if err != nil {
tenant.LogError(err, "error while marshalling")
continue
}
}
#VallabhLakade I had similar concern like this and tried the below way that helped .
So basically what's the problem is that the mongo-driver defaults to unmarshalling as bson.D for structs of type interface{} where as mgo mgo-driver defaults to bson.M .
So we will have to add the below code while trying to establish connection with mongo-db , SetRegistry() options as clientOpts To map the old mgo behavior, so that mongo-driver defaults to bson.M while unmarshalling structs of type interface{} , and this should not display the values back as key-value pair
tM := reflect.TypeOf(bson.M{})
reg := bson.NewRegistryBuilder().RegisterTypeMapEntry(bsontype.EmbeddedDocument, tM).Build()
clientOpts := options.Client().ApplyURI(SOMEURI).SetAuth(authVal).SetRegistry(reg)
client, err := mongo.Connect(ctx, clientOpts)
Reference -> MongoDB document returns array of key value pair in go mongo-driver
#Vallabh you shall use mgocompat too something like below this should mimic the old mgo behavior
registry := mgocompat.NewRegistryBuilder().Build()
connectOpts := options.Client().ApplyURI(SOMEURI).SetAuth(info).SetRegistry(registry)
I'm using the official mongo driver in golang, and am trying to determine if a record exists. Unfortunately the documentation doesn't explain how to do this. I'm attempting to do it with FindOne, but it returns and error when no results are found, and I don't know how to distinguish that error from any other error (short of comparing strings which feels wrong. What's the right way to check if a document exists in mongo with the official golang driver?
Here's my code.
ctx := context.Background()
var result Page
err := c.FindOne(ctx, bson.D{{"url", url}}).Decode(&result)
fmt.Println("err: ", err)
// how do I distinguish which error type here?
if err != nil {
log.Fatal(err)
}
Here's the answer.
var coll *mongo.Collection
var id primitive.ObjectID
// find the document for which the _id field matches id
// specify the Sort option to sort the documents by age
// the first document in the sorted order will be returned
opts := options.FindOne().SetSort(bson.D{{"age", 1}})
var result bson.M
err := coll.FindOne(context.TODO(), bson.D{{"_id", id}}, opts).Decode(&result)
if err != nil {
// ErrNoDocuments means that the filter did not match any documents in the collection
if err == mongo.ErrNoDocuments {
return
}
log.Fatal(err)
}
fmt.Printf("found document %v", result)
I'm trying to find a document by its auto generated _id field. Code below returns nothing:
var documentID bson.RawValue
documentID.Type = 7
documentID.Value = []byte("5c7452c7aeb4c97e0cdb75bf")
objID := documentID.ObjectID()
value := collection.FindOne(ctx, bson.M{"_id": objID})
The value I provided is a real document id I got from Mongo Express
"_id": ObjectID("5c7452c7aeb4c97e0cdb75bf")
In case you're wondering why I bother with RawValue, I found examples using bson.EC.ObjectID but bson package doesn't seem to have EC type, also I found some examples mentioning github.com/mongodb/mongo-go-driver/bson/objectid package, but I could not find that package either. I previously developed with mgo but I'm new to mongo-go-driver, so if you can point an easy way to declare an ObjectID.
As #Carlos mentioned, I changed my code as this and everything works well.
objID, _ := primitive.ObjectIDFromHex("5c7452c7aeb4c97e0cdb75bf")
value := collection.FindOne(ctx, bson.M{"_id": objID})
You can use some thing like this:
var userDB user
objectIDS, _ := primitive.ObjectIDFromHex(userID)
collectionUser := dBClient.Database("MyDatabase").Collection("Users")
filter := bson.M{"_id": objectIDS}
err := collectionUser.FindOne(ctx, filter).Decode(&userDB)
if err != nil {
fmt.Println("errror retrieving user userid : " + userID)
}
package main
import (
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"context"
)
// get collection "users" from db() which returns *mongo.Client
var userCollection = db().Database("goTest").Collection("users")
func mongodriver_find_by_id() {
objectId, err1 := primitive.ObjectIDFromHex("6041c3a6cfcba2fb9c4a4fd2")
if err1 != nil {fmt.Println(err1)}
findone_result := userCollection.FindOne(context.TODO(), bson.M{"_id":objectId})
var bson_obj bson.M
if err2 := findone_result.Decode(&bson_obj); err2 != nil {fmt.Println(err2)}
fmt.Println("bson_obj:", bson_obj)
}
So here is my question. I have an array which are stored the _ids of mongodbs objects. Whats the right way to retrieve them all in one query using the mgo and bson package?
So if the array is like that: ids:=["543d171c5b2c12420dd016","543d171c5b2dd016"]
How we make the query ? I tried that but I know its wrong.
query := bson.M{"_id": bson.M{"$in": ids}}
c.Find(query).All()
Thanks in advance
If the documents are stored with string ids, then the code looks correct.
The ids look like hex encoded object ids. If the object identifiers are object ids, then you need to the convert the hex strings to object ids:
oids := make([]bson.ObjectId, len(ids))
for i := range ids {
oids[i] = bson.ObjectIdHex(ids[i])
}
query := bson.M{"_id": bson.M{"$in": oids}}
MongoDB syntax for go.mongodb.org/mongo-driver has been updated, this should work using the official driver.
oids := make([]primitive.ObjectID, len(ids))
for i := range ids {
objID, err := primitive.ObjectIDFromHex(ids[i])
if err == nil {
oids = append(oids, objID)
}
}
This is to convert back to a struct that can be used through out the app
type MongoUser struct {
ID *primitive.ObjectID `json:"id" bson:"_id"`
FirstName string `json:"first_name" bson:"firstName"`
LastName string `json:"last_name" bson:"lastName"`
Email string `json:"email" bson:"email"`
}
This is a helper method that takes your slice of ids and turns it into the object id type.
func formatObjectIdMultiple(hex []string) ([]primitive.ObjectID, error) {
var list []primitive.ObjectID
oids := make([]primitive.ObjectID, len(hex))
for _, i := range hex {
objectId, err := primitive.ObjectIDFromHex(i)
if err != nil {
return nil, err
}
oids = append(oids, objectId)
}
return list, nil
}
Here is my method for the db. Its important you use bson.M for some reason bson.D does not work with this. Also dont forget to close your cursor the defer function will close it at the end of your GetMultipleUser function.
func (mongo *Mongo) GetMultipleUser(ids []string) ([]*MongoUser, error) {
objectIDs, err := formatObjectIdMultiple(ids)
if err != nil {
return nil, err
}
query := bson.M{"_id": bson.M{"$in": objectIDs}}
coll := mongo.Con.Database("dbName").Collection("users")
cursor, err := coll.Find(context.Background(), query)
if err != nil {
return nil, err
}
defer func() {
cursor.Close(context.Background())
}()
var output []*MongoUser
for cursor.Next(context.Background()) {
var temp *MongoUser
cursor.Decode(&temp)
output = append(output, temp)
}
return output, nil
}