how to use $push and $each with the go mgo driver? - mongodb

I have a created a basic nested structure:
type Komplex struct {
count int `bson:"count"`
text string `bson:"text"`
}
type Parent struct {
Count int `bson:"count"`
SubCount []Komplex `bson:"subcount"`
}
And i would like to use mongo's safe update feature to extend a collection:
session.DB("test").C("ints").Upsert(bson.M{"count": toWrite.Count},
bson.M{"$addToSet": bson.M{"subcount": bson.M{"$each": toWrite.SubCount}}})
This works when i replace the Komplex struct with a slice of just int. However when i try to follow a complex set addition with my Komplex struct like described at https://docs.mongodb.org/manual/reference/operator/update/push/#up._S_push nothing gets inserted.
How do i properly marshal my slice with structs to bson.M in this situation?

Maybe work only for this code :
session.DB("test").C("ints").Update(bson.M{"count": toWrite.Count},
bson.M{"$addToSet": bson.M{"subcount": bson.M{"$each": toWrite.SubCount}}}
)

Your Golang code should be like:
data := model.Komplex {
count: 12345,
text: "yourText",
}
selector := bson.M{"count": toWrite.Count}
changes := bson.M{"$addToSet": bson.M{"subcount": bson.M{"$each": []model.Komplex{data}}}}
err = c.Update(selector, changes)
You have shared the reference link which deals with a $push but you are using $addToSet in your query.
$push - appends the array even if the data is duplicate
$addToSet- keeps only distinct values in the array
Also, I'd like to suggest you to include an _id field in the Parent struct.

Related

MongoDB BSON - Search on specific fields [duplicate]

How can I filter fields with the mongo-go-driver.
Tried it with findopt.Projection but no success.
type fields struct {
_id int16
}
s := bson.NewDocument()
filter := bson.NewDocument(bson.EC.ObjectID("_id", starterId))
var opts []findopt.One
opts = append(opts, findopt.Projection(fields{
_id: 0,
}))
staCon.collection.FindOne(nil, filter, opts...).Decode(s)
In the end, I want to suppress the field "_id". But the documents didn't change.
Edit: As the mongo-go driver evolved, it is possible to specify a projection using a simple bson.M like this:
options.FindOne().SetProjection(bson.M{"_id": 0})
Original (old) answer follows.
The reason why it doesn't work for you is because the field fields._id is unexported, and as such, no other package can access it (only the declaring package).
You must use a field name that is exported (starts with an uppercase latter), e.g. ID, and use struct tags to map it to the MongoDB _id field like this:
type fields struct {
ID int `bson:"_id"`
}
And now to perform a query using a projection:
projection := fields{
ID: 0,
}
result := staCon.collection.FindOne(
nil, filter, options.FindOne().SetProjection(projection)).Decode(s)
Note that you may also use a bson.Document as the projection, you don't need your own struct type. E.g. the following does the same:
projection := bson.NewDocument(
bson.EC.Int32("_id", 0),
)
result := staCon.collection.FindOne(
nil, filter, options.FindOne().SetProjection(projection)).Decode(s)

Go Mongo Driver Retrieve Schemaless Documents

While working with Mongo Go Driver I want to retrieve Schemaless Documents.
I am able to retrieve documents using bson.M json:",inline" bson:",inline"
But this adds extra "M" key in json when i try to Decode to a struct
type Product struct {
ID primitive.ObjectID `bson:"_id"`
ProductId string `bson:"product_id" json:"product_id"`
bson.M `json:",inline" bson:",inline"`
}
Output:-
{
"id":"<ObjectId>",
"M":{
"some":""
}
}
But instead what i want how it is stored in Mongo.
{
"id":"<ObjectId>",
"some":""
}
I cant use directly something like this as I want to cast it to struct to work with some properties
var pr bson.M
err := p.FindOne(ctx, &p.options,query, &pr)
How can I remove that extra key which is getting added while converting schemaless Documents from Mongo?
Do I need to explicitly overwrite MarshalJSON() or is there something provided using Tags?
How can I remove that extra key which is getting added while converting schemaless Documents from Mongo?
You can just define a field mapping name, which will be flattened when marshalled. For example:
type Product struct {
ID primitive.ObjectID `bson:"_id"`
ProductId string `bson:"product_id"`
Others bson.M `bson:",inline"`
}
When you decode a document, you'll see that it will include other fields without the Others name. For example if you have a document:
{
"_id": ObjectId("5e8d330de85566f5a0557ea4"),
"product_id": "foo",
"some": "x",
"more": "y"
}
doc := Product{}
err = cur.Decode(&doc)
fmt.Println(doc)
// Outputs
// {ObjectID("5e8d330de85566f5a0557ea4") foo map[more:y some:x]}
I cant use directly something like this as I want to cast it to struct to work with some properties
You can use this directly for a query predicate. For example:
// After decoding 'doc' to product
var result bson.M
err := collection.FindOne(context.TODO(), doc).Decode(&result)
Tested using MongoDB Go driver v1.3.2
UPDATED:
If you would like to return JSON, you could use bson.MarshalExtJSON(). This should be easier as well in terms of dealing with objects that don't exist in JSON. i.e. ObjectId. For example:
// After decoding 'doc' to product
ejson, err := bson.MarshalExtJSON(doc, true, false)
fmt.Println(string(ejson))

How to filter fields from a mongo document with the official mongo-go-driver

How can I filter fields with the mongo-go-driver.
Tried it with findopt.Projection but no success.
type fields struct {
_id int16
}
s := bson.NewDocument()
filter := bson.NewDocument(bson.EC.ObjectID("_id", starterId))
var opts []findopt.One
opts = append(opts, findopt.Projection(fields{
_id: 0,
}))
staCon.collection.FindOne(nil, filter, opts...).Decode(s)
In the end, I want to suppress the field "_id". But the documents didn't change.
Edit: As the mongo-go driver evolved, it is possible to specify a projection using a simple bson.M like this:
options.FindOne().SetProjection(bson.M{"_id": 0})
Original (old) answer follows.
The reason why it doesn't work for you is because the field fields._id is unexported, and as such, no other package can access it (only the declaring package).
You must use a field name that is exported (starts with an uppercase latter), e.g. ID, and use struct tags to map it to the MongoDB _id field like this:
type fields struct {
ID int `bson:"_id"`
}
And now to perform a query using a projection:
projection := fields{
ID: 0,
}
result := staCon.collection.FindOne(
nil, filter, options.FindOne().SetProjection(projection)).Decode(s)
Note that you may also use a bson.Document as the projection, you don't need your own struct type. E.g. the following does the same:
projection := bson.NewDocument(
bson.EC.Int32("_id", 0),
)
result := staCon.collection.FindOne(
nil, filter, options.FindOne().SetProjection(projection)).Decode(s)

Need to include $each and $position with $push in MongoDB and golang(mgo)

1.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.Is is possible??
After researching on this i came to know that its not possible.So i m thinking of using $push,$each and $position.Here i can set the position to 0 so the newly added document will be in 0 so i can retreive it using the index 0.
Here is bson format
{
empid:"L12"
AnnualLeave:[
{
"atotal" : 20,
}
]
}
Here is my schema
type (
Employee struct {
EmpId string
AnnualLeave []*AnnualLeaveInfo
}
AnnualLeaveInfo struct {
ATotal int64
}
I use the mgo statement as follows`enter code here`
c.Update(bson.M{"empid": "string"}, bson.M{"$push": bson.M{"annualleave":bson.M{"$each":
bson.M{"atotal": 4},"$position":0}}
2.Please advice me as well how to decrement the ATotal of the previous document attached and keep it as the value of the atotal of the new document.
Please help me.Thanks
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.Is is possible?
You can find the last array index by derriving from the array length. Using your example:
type Employee struct {
EmpId string
AnnualLeave []AnnualLeaveInfo
}
type AnnualLeaveInfo struct {
ATotal int64
}
result := Employee{}
err = c.Find(bson.M{"empid": "example employee ID"}).One(&result)
if err != nil {
log.Fatal(err)
}
lastAnnualTotal:= result.AnnualLeave[len(result.AnnualLeave)-1].ATotal
Please advice me as well how to decrement the ATotal of the previous document attached and keep it as the value of the atotal of the new document
Depending on your use case, you could try performing two database operations:
Fetch the last ATotal value from the collection.
Push a new AnnualLeaveInfo document with the new ATotal value.
// Assuming that EmpId is unique
err = c.Update(bson.M{"empid": result.EmpId},
bson.M{"$push": bson.M{"annualleave": bson.M{"atotal": int(latestAnnualTotal-1)}}})
If you require atomic updates, see MongoDB Atomicity and Transactions and Model Data for Atomic Operations.
On another note, it seems that you're trying to do something related to CQRS design patterns. This design pattern may help with calculating your annual leave use case. See also Even Sourcing with MongoDB

Storing nested structs with mgo

I'm trying to build a mongo document from a go struct that is heavily nested, and I'm running into a problem with the transition from go struct to a mongo object. I've built a very simplified version of what I'm trying to work with here: http://play.golang.org/p/yPZW88deOa
package main
import (
"os"
"fmt"
"encoding/json"
)
type Square struct {
Length int
Width int
}
type Cube struct {
Square
Depth int
}
func main() {
c := new(Cube)
c.Length = 2
c.Width = 3
c.Depth = 4
b, err := json.Marshal(c)
if err != nil {
panic(err)
}
fmt.Println(c)
os.Stdout.Write(b)
}
Running this produces the following output:
&{{2 3} 4}
{"Length":2,"Width":3,"Depth":4}
Which makes complete sense. It seems either the Write function or the json.Marshal function has some functionality that collapses the nested struct, but my problem comes when I try to insert this data into a mongo database using the mgo function func (*Collection) Upsert (http://godoc.org/labix.org/v2/mgo#Collection.Upsert). If I use the json.Marshal() function first and pass the bytes to collection.Upsert(), it is stored as binary, which I don't want, but if I use collection.Upsert(bson.M("_id": id, &c) it appears as a nested struct with the form:
{
"Square": {
"Length": 2
"Width": 3
}
"Depth": 4
}
But what I want to do is upsert to mongo with the same structure as I get when I use the os.Stdout.Write() function:
{
"Length":2,
"Width":3,
"Depth":4
}
Is there some flag I'm missing that would easily handle this? The only alternative I can see at this point is severely cutting down on the readability of the code by removing the nesting of the structs, which I really hate to do. Again, my actual code is way more complex than this example, so if I can avoid complicating it even more by keeping things nested, that would definitely be preferable.
I think using the inline field tag is the best option for you. The mgo/v2/bson documentation states:
inline Inline the field, which must be a struct or a map,
causing all of its fields or keys to be processed as if
they were part of the outer struct. For maps, keys must
not conflict with the bson keys of other struct fields.
Your struct should then be defined as follows:
type Cube struct {
Square `bson:",inline"`
Depth int
}
Edit
inline also exists in mgo/v1/bson incase you are using that one.