How to use Find().Select().One() in go mongo-driver library - mongodb

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})

Related

Get embedded object values in an ordered map from MongoDb Go driver

GO version: 1.18.3
Mongodb version: 4.4
Mongodb driver used: go.mongodb.org/mongo-driver/bson
I want to receive an embedded object saved in the database in the form of a map with preserved order (order matters for me). For this I have used following package:
https://github.com/elliotchance/orderedmap
from the following reference:
Preserve map order while unmarshalling bson data to a Golang map
Now I have a struct below:
type PageResp struct {
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
Status int `json:"status" bson:"status"`
AddedSections []string `json:"added_sections" bson:"added_sections"`
Sections *orderedmap.OrderedMap `json:"sections,omitempty" bson:"sections,omitempty"`
}
But Sections are empty by using type like this. I have tried it by defining type alias as well. Nothing is working.
After that I receive the data from mongodb in a temp variable with type bson.D then explicitly loop over this to build an ordered map. See the code below:
type PageResp struct {
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
Status int `json:"status" bson:"status"`
AddedSections []string `json:"added_sections" bson:"added_sections"`
SectionsTemp2 *orderedmap.OrderedMap `json:"sections_temp2,omitempty" bson:"sections_temp2,omitempty"`
SectionsTemp bson.D `json:"sections_temp,omitempty" bson:"sections_temp,omitempty"`
}
// function to convert bson.D to ordered map
func test(sections bson.D) *orderedmap.OrderedMap {
newSections := orderedmap.NewOrderedMap()
for _, section := range sections {
childMap := orderedmap.NewOrderedMap()
for _, v := range section.Value.(bson.D) {
childMap.Set(v.Key, v.Value)
}
// fmt.Println("childMap", childMap)
newSections.Set(section.Key, *childMap)
}
// fmt.Println("newSections", newSections)
return newSections
}
But this method is returning values like:
result: &{map[123:0xc0000125d0 foo:0xc000012570 qux:0xc0000125a0] 0xc000012540}
But it should return values like:
result: [["foo","bar"],["qux",1.23],[123,true]]
It shows that this package is also not maintaining the order. Also if some other method for ordered map exists, please write to me.
Thanks!

Querying GORM database by its pq.StringArray attribute

I have the following gorm.Model and I want to Query my Postgres database to return Confessions that have a specific category in their .Categories attribute, but I have no idea how to Query inside a pq.StringArray. Is there a work-around?
type Confession struct {
gorm.Model
User string `json:"User"`
Title string `json:"Title"`
Body string `json:"Body"`
Mood string `json:"Mood"`
Categories pq.StringArray `gorm:"type:varchar(64)[]" json:"Categories"`
}
And here is how I tried to query, but using the LIKE operator throws an error.
if categories != nil {
for _, cat := range categories {
tx = tx.Where("Categories LIKE ?", "%"+cat+"%")
}
}
Use <# for array contains. You can query using Query function of *sql.DB and get *sql.DB using tx.DB() in gorm
sel := "SELECT * FROM confessions WHERE $1 <# categories"
categories := []string{"cat1", "cat2"}
rows, err := tx.DB().Query(sel, pq.Array(categories))
Or try Gorm Raw SQL , but I won't sure it will work properly or not for array functions.
References:
PostgreSQL Array function here
ProtgreSQL Array use in golang here
The easiest solution to my problem was to use the .Where command as such
tx = tx.Where("categories && ?", pq.Array(categories))
This will return a gorm.DB so I can continue to chain actions. The && operator is to check for OVERLAPPING elements.

to find the last index of the array in mongodb

In the back end i m using go lang and for database i use mongoDB. I m trying to find the last document inserted in the embedded array so i can retrieve the document in the last array index without knowing its index.Right now i m getting all the documents of the employee and then find the last index.It is like overloading my RAM as i need to retrieve 1000 of record of employee and store it in ram before finding the last index of the array
My struct is as follows
type (
Employee struct {
Name string
Password string
EmpId string
EmailAddress string
Position string
Gender string
Nationality string
Department string
MaritalStatus string
Approvedby string
JoinDate time.Time
ConfirmationDate time.Time
EndDate time.Time
Leave []*LeaveInfo
}
LeaveInfo struct {
Total float64
Id int
Days float64
From time.Time
To time.Time
Status string
Certificate []*CertificateInfo
}
CertificateInfo struct {
FileName string
FileType string
FileSize int
}
Here is how i do in my application
My code is follows
employee := Employee{}
err = c.Find(bson.M{
"empid": "1234"
}).One(&result)
if err != nil {
log.Fatal(err)
}
name:=result.Name
lastindex:= result.LeaveInfo[len(result.LeaveInfo)-1].Id
As you can see i retrive the whole employee data and then find the Id of the last document.Is there any better way to do this.Appreciate any help.Please ...Thanks..
Newly Added Codes
This code is based on your example
var result bson.M
query := bson.M{"empid": "1234"} // gets the employee you are interested in
match := bson.M{"$match": query} // Set up the match part of the pipeline
unwind := bson.M{"$unwind": "$leave"} // sets up the leave field to be unwound
pipeline := []bson.M{match, unwind,{
"$project":bson.M{
"ID":bson.M{
"$slice": []interface{}{"$leave.id", -1},
}
}
iter := postCollection.Pipe(pipeline).Iter()
for iter.Next(&result) {
fmt.Printf("%+v\n", result)
}
iter.Close()
This code gives me lot of same document like 542 documents .But all these document are same...
If you are running a version on Mongo which has $slice in it, It was introduced in 2.4, you can use it in a Find with the addition of a Select Function, which works like project.
err = c.Find(bson.M{"empid": "1234"}).Select(bson.M{"name": 1, "leave": bson.M{"$slice": -1}}).One(&result) // result is an Employee struct
Otherwise, aggregation is your friend. You need to use a pipeline and unwind the Employee Leave field.
There are a few simple steps:
1) Define a result record based on your Employee record where the Leave field is defined as a single LeaveInfo rather than a slice of LeaveInfos, eg
EmployeeResult struct {
Name string `bson:"name"`
Leave LeaveInfo `bson:"leave"`
}
Do not forget to make the bson tag the same name as the LeaveInfo tag in the Employee struct.
2) Then create a pipeline with a couple of stages:
query := bson.M{"empid": "1234"} // gets the employee you are interested in
match := bson.M{"$match": query} // Set up the match part of the pipeline
unwind := bson.M{"$unwind": "$leave"} // sets up the leave field to be unwound
pipeline := []bson.M{match, unwind} // the pipeline you are passing to pipe. I like to split the parts of the pipe call up for clarity and ease of later modification
3) Call Pipe with the pipeline as a parameter then Iter over the results, this should give you one LeaveInfo at a time
var (
result EmployeeResult
)
iter := postCollection.Pipe(pipeline).Iter()
for iter.Next(&result) {
fmt.Printf("%+v\n", result)
}
iter.Close()
4) At the end of the loop, result will have the last item in the list, or be blank if nothing was read.

How to update a sub-document array fields along with some other fields in Mongodb, Mgo?

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)

Obtain ObjectIdHex value from mgo query

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))