MongoDB BSON - Search on specific fields [duplicate] - mongodb

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)

Related

MongoDB Golang: FindOne() with nested Bson.M failed for 10% attempts, Bson.M+Bson.D or a single Bson.M always work, why?

Need some help to figure out why nested bson.M doesn’t work occasionally in FindOne().
For the following Golang structs stored in a MongoDb collection for type A:
type A struct {
Id primitive.ObjectID
Random1 string
Parents []B
Random2 int
}
type B struct {
Id primitive.ObjectID
Random3 string
Children []C
Random4 int
}
type C struct {
Random5 string
Name Name
Random6 int
}
type Name struct {
FirstName string
LastName string
}
The following filter for FindOne(), which uses two bson.M, worked in most situations but failed to find a match in about 10% runs
filter1 := bson.M{
"parents.0.chilren.0.name": bson.M{
"first_name": "Mike",
"last_name": "Anderson",
},
}
The following two filters alway work, where filter 2 uses bson.D inside bson.M, and filter 3 just uses one bson.M
filter2 := bson.M{
"parents.0.chilren.0.name": bson.D{
{Key: "first_name", Value: "Mike"},
{Key: "last_name", Value: "Anderson"},
},
}
filter3 := bson.M{
"parents.0.chilren.0.name.first_name": "Mike",
"parents.0.chilren.0.name.last_name": "Anderson",
}
I found a similar question in https://jira.mongodb.org/browse/GODRIVER-877 but still don’t understand the differences or root cause. Thanks for the help!
bson.M is a map, thus, the order of its elements are not guaranteed. Since you are comparing parents.0.chilren.0.name, which is a structure, the search criteria has to match exactly, with the order of fields in the criteria matching the order fields in the database. That's why when you use bson.M, it sometimes matches and sometimes not. When the search criteria matches the database field order, you get results, otherwise you don't.
That's the reason why the criteria with bson.D always matches, because it is based on a slice, so the order of fields are preserved.
The last search criteria compares individual fields, not a structure, so the order of fields are immeterial.

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

Making a unique field in Mongo-go-driver

I am very new to both Go and Mongodb and was writing my first rest-api with Go and Mongo. I am using mongo-go-driver and have the following Modal struct in Go
type Modal struct {
Group []string `bson:"group" json:"group"`
Hostname string `bson:"hostname" json:"hostname"`
Overrides map[string]string `bson:"overrides" json:"overrides"`
Excludes []string `bson:"excludes" json:"excludes"`
}
I do not want to use the default ObjectId field provided by mongo-db as my primary key and instead would like to make the Hostname field as the primary key.
If I make the type of Hostname field as primitive.ObjectID, then the hostname would be unique but its value will be randomly generated string by mongodb and not the actual hostname string value.
So is there a way I can do this.
You may use a unique index to enforce / allow only distinct values of a given field, e.g.:
db.collectionname.createIndex( { "hostname": 1 }, { unique: true } )
If you want to create such index using the official MongoDB driver, this is how you can do that:
indexName, err := coll.Indexes().CreateOne(
context.Background(),
mongo.IndexModel{
Keys: bson.D{{Key: "hostname", Value: 1}},
Options: options.Index().SetUnique(true),
},
)
But know that in MongoDB each document must have an _id property, so doing the above, documents will have an auto-generated _id field (of ObjectId type). If this doesn't bother you, you're done.
Also note that you may map Modal.Hostname to the _id field with struct tags:
type Modal struct {
Group []string `bson:"group" json:"group"`
Hostname string `bson:"_id" json:"hostname"`
Overrides map[string]string `bson:"overrides" json:"overrides"`
Excludes []string `bson:"excludes" json:"excludes"`
}
And again, you're done. The downside of this solution is that the documents in MongoDB will not have a property named hostname, as it will be stored in _id.

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)

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

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.