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
Related
I have this method:
func GetPostBySlug(slug string) (PostWithCategory, error) {
post := PostWithCategory{}
filter := bson.M{
"slug": slug,
}
database, error := Database.GetMongoDatabase()
if error != nil {
println(error)
}
match := bson.D{{"$match", bson.D{{"slug", slug}}}}
lookup := bson.D{{"$lookup", bson.D{{"from", categories.Model_name}, {"localField", "category"}, {"foreignField", "_id"}, {"as", "category"}}}}
unwind := bson.D{{"$unwind", bson.D{{"path", "$category"}, {"preserveNullAndEmptyArrays", false}}}}
collection := database.Collection(model_name)
cursor, err := collection.Aggregate(context.TODO(), mongo.Pipeline{match, lookup, unwind})
if err != nil {
return post, err
}
var results []PostWithCategory
if err = cursor.All(context.TODO(), &results); err != nil {
panic(err)
}
fmt.Println(results)
if len(results) > 0 {
post = results[0]
}
return post, err
}
If I remove "unwind" stage from Aggregate(), I will get my document but with empty array instead of category. If I keep it, all the fields become "null" in the document.
Why is this so hard 😠Maybe there is some other way around? The goal is simple. I have an ID of the category in the post and in above method I want to return the post with category object (which is stored in other collection). In node.js with mongoose there is a very simple method called "populate", so I need to perform similar operation here :(
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 not able to frame my whole question in title so here it is:
I have a slice of strings var temp = []string{"abc","efg","xyz"}
Now I want to search documents in collection for every element in the above slice.
I know I can do something like this:
for _, str:=range temp{
collection.Find(context.background(), bson.M{"key":str})
}
but as you can see I will have to fire many queries.
So is there a solution where I can fire a single query to find all those documents
e.g:
err = collection.Find(context.Background(), bson.M{"key": MY_SLICE_OF_STRING})
You can use:
// I'm not sure what is your struct, so I use bson.Raw for this example
// but you can parse into your struct in the loop.
resultQuery := make([]bson.Raw, 0)
// you can use bson.M if you like,
// filter := bson.M{"key": bson.M{"$in": MY_SLICE_OF_STRING}}
filter := bson.D{
{
Key: "key",
Value: bson.E{
Key: "$in",
Value: MY_SLICE_OF_STRING,
},
},
}
ctx := context.background()
cursor, err := collection.Find(ctx, filter)
if err != nil {
//Handle your error.
}
if err == nil {
// you should put defer function to close your cursor,
defer func() {
cursor.Close(ctx)
}()
for cursor.Next(ctx) {
resultQuery = append(resultQuery, cursor.Current)
}
}
This is my database collection:
With this go code, I try to get all the users who are either involved in the story or created the story with the given id.
func main() {
for stf.DB == nil {
}
collection := stf.DB.Collection("user")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
id, _ := primitive.ObjectIDFromHex("5cb4dd7e29d3dca573a73d4c")
fter := bson.M{"_id": id}
involvedFilter := bson.M{"stories_involved": fter}
createdFilter := bson.M{"stories_created": fter}
filter := bson.M{"$or": []bson.M{involvedFilter, createdFilter}}
cur, err := collection.Find(ctx, filter)
if err != nil {
log.Fatal(err.Error())
}
defer cur.Close(ctx)
for cur.Next(ctx) {
var result bson.M
err := cur.Decode(&result)
if err != nil {
log.Fatal(err.Error())
}
fmt.Println(result)
}
if err := cur.Err(); err != nil {
log.Fatal(err.Error())
}
}
The code doesn't output any errors, but it also doesn't output any objects...
Thanks for your help in advance!
Your query translates into:
{"$or":[
{"stories_involved":{
"_id": ObjectId("5cb4dd7e29d3dca573a73d4c")}},
{"stories_created":{
"_id":ObjectId("5cb4dd7e29d3dca573a73d4c")}}
]}
Which means that it's searching for either a document with a nested document
i.e:
{stories_involved: {_id: <value>}} OR {stories_created: {_id: <value>}}.
However, the documents in the collection contains nested document array i.e:
{stories_involved: [{_id:<value>}]} OR {stories_created: [{_id:<value>}]}
This is the reason your query is not returning any value (and no error because the query syntax is correct).
There are two ways of Querying a document nested in an array using dot notation. If you know the index of the array for the document, you can just specify the position:
id, _ := primitive.ObjectIDFromHex("5cb4dd7e29d3dca573a73d4c")
involvedFilter := bson.M{"stories_involved.0._id": id}
createdFilter := bson.M{"stories_created.0._id": id}
filter := bson.M{"$or": []bson.M{involvedFilter, createdFilter}}
cur, err := collection.Find(ctx, filter)
If you do not know the index position of the document nested in the array, concatenate the name of the array field, with a dot (.) and the name of the field in the nested document:
id, _ := primitive.ObjectIDFromHex("5cb4dd7e29d3dca573a73d4c")
involvedFilter := bson.M{"stories_involved._id": id}
createdFilter := bson.M{"stories_created._id": id}
filter := bson.M{"$or": []bson.M{involvedFilter, createdFilter}}
cur, err := collection.Find(ctx, filter)
See also MongoDB: Query Documents
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
}