mongo-go-driver find a document by _id - mongodb

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

Related

How to replace a document in MongoDB with Go driver?

I'm trying to update a document in MongoDB with mongodb/mongo-go-driver. From its doc, one document can be replaced with:
var coll *mongo.Collection
var id primitive.ObjectID
// find the document for which the _id field matches id and add a field called "location"
// specify the Upsert option to insert a new document if a document matching the filter isn't found
opts := options.Replace().SetUpsert(true)
filter := bson.D{{"_id", id}}
replacement := bson.D{{"location", "NYC"}}
result, err := coll.ReplaceOne(context.TODO(), filter, replacement, opts)
if err != nil {
log.Fatal(err)
}
if result.MatchedCount != 0 {
fmt.Println("matched and replaced an existing document")
return
}
if result.UpsertedCount != 0 {
fmt.Printf("inserted a new document with ID %v\n", result.UpsertedID)
}
But what if I have fields more than like 20. I am wondering if I can update without re-writing all fields again, I tried something like this:
// Replacement struct
type Input struct {
Name *string `json:"name,omitempty" bson:"name,omitempty" validate:"required"`
Description *string `json:"description,omitempty" bson:"description,omitempty"`
Location *string `json:"location,omitempty" bson:"location,omitempty"`
}
...
oid, _ := primitive.ObjectIDFromHex(id) // because 'id' from request is string
filter := bson.M{"_id", oid} // throws `[compiler] [E] missing key in map literal`
// ^^^
replacement := bson.D{{"$set", input}} // throws `composite literal uses unkeyed fields`
// ^^^^^
result, err := coll.ReplaceOne(context.TODO(), filter, replacement, opts)
...
But it throws errors at filter and replacement. How can I replace a whole document properly?
bson.M is a map, so if you use it, you have to write: bson.M{"_id": oid}. See missing type in composite literal go AND missing key in map literal go.
And bson.D is a slice of structs, so if you use it, you should write bson.D{{Key:"$set", Value: input}}. Omitting the field names is not a compiler error, it's just a go vet warning.
Now on to replacing. The replacement must be a document itself, without using $set (this is not updating but replacing). For reference see MongoDB's collection.replaceOne() and the driver's doc: Collection.ReplaceOne():
The replacement parameter must be a document that will be used to replace the selected document. It cannot be nil and cannot contain any update operators (https://docs.mongodb.com/manual/reference/operator/update/).
So do it like this:
filter := bson.M{"_id": oid}
result, err := coll.ReplaceOne(context.TODO(), filter, input, opts)
Here is the complete code. This will help readers to understand the code flow.
func UpdateFunction(echoCtx echo.Context) (error) {
//Web Section
documentID := echoCtx.Param("id") //Provided in URL
var newObject myStruct
err := echoCtx.Bind(&newObject) // Json with updated values sent by web client mapped to struct
ctx := echoCtx.Request().Context()
//Database Section
database := db.Conn.Database("myDatabase")
collection := database.Collection("myCollection")
existingHexID, err := primitive.ObjectIDFromHex(documentID)
if err != nil {
fmt.Println("ObjectIDFromHex ERROR", err)
} else {
fmt.Println("ObjectIDFromHex:", existingHexID)
}
// Replacing OLD document with new using _id
filter := bson.M{"_id": newDocumentID}
result, err := collection.ReplaceOne(ctx, filter, newObject)
if err != nil {
log.Fatal(err)
}
fmt.Printf(
"insert: %d, updated: %d, deleted: %d /n",
result.MatchedCount,
result.ModifiedCount,
result.UpsertedCount,
)
return echoCtx.JSON(http.StatusOK, result.ModifiedCount)
}

List MongoDB databases names using regex [duplicate]

Question
How can I list databases only with the given prefix (prefix_)?
Example:
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)
type foo struct {
Value string
}
func main() {
clientOptions := options.Client().ApplyURI("mongodb://10.0.12.76:27018")
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}
db := [3]string{"prefix_foo", "prefix_bar", "bar"}
for _, element := range db {
_, err := client.Database(element).Collection("placeholder").InsertOne(context.TODO(), foo{"sth"})
if err != nil {
log.Fatal(err)
}
}
filter := bson.D{{}}
dbs, err := client.ListDatabaseNames(context.TODO(), filter)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", dbs)
}
Output:
[admin bar config local prefix_bar prefix_foo]
Expected output:
[prefix_bar prefix_foo]
Bonus:
It is possible to create a database without defining new struct in my case foo?
My goal is to run a query on databases only with a prefix, so maybe better solution exists than listing dbs and then run a query on each database?
Simply filter by the name property, which denotes the database name. And to list databases starting with a given prefix, you may use a regexp being ^prefix_:
filter := bson.M{"name": primitive.Regex{Pattern: "^prefix_"}}
Other filter options are listed on the listDatabases command page:
You can specify a condition on any of the fields in the output of listDatabases:
name
sizeOnDisk
empty
shards
And you may use an empty bson.M{} to insert an empty document (_id will be added of course).

MongoDB list databases with given prefix in Go

Question
How can I list databases only with the given prefix (prefix_)?
Example:
package main
import (
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)
type foo struct {
Value string
}
func main() {
clientOptions := options.Client().ApplyURI("mongodb://10.0.12.76:27018")
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}
db := [3]string{"prefix_foo", "prefix_bar", "bar"}
for _, element := range db {
_, err := client.Database(element).Collection("placeholder").InsertOne(context.TODO(), foo{"sth"})
if err != nil {
log.Fatal(err)
}
}
filter := bson.D{{}}
dbs, err := client.ListDatabaseNames(context.TODO(), filter)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", dbs)
}
Output:
[admin bar config local prefix_bar prefix_foo]
Expected output:
[prefix_bar prefix_foo]
Bonus:
It is possible to create a database without defining new struct in my case foo?
My goal is to run a query on databases only with a prefix, so maybe better solution exists than listing dbs and then run a query on each database?
Simply filter by the name property, which denotes the database name. And to list databases starting with a given prefix, you may use a regexp being ^prefix_:
filter := bson.M{"name": primitive.Regex{Pattern: "^prefix_"}}
Other filter options are listed on the listDatabases command page:
You can specify a condition on any of the fields in the output of listDatabases:
name
sizeOnDisk
empty
shards
And you may use an empty bson.M{} to insert an empty document (_id will be added of course).

Struggling to convert a MongoDB singleResult object into Go struct

I tried to follow documentation here and here but had no luck.
I want to get a singleResult from FindOne on the Collection named moviesCollection and then use Decode or Unmarshal to put those values into a struct. The values in the struct JSONData are exactly the same as in each Document
I am using the official mongodb driver github.com/mongodb/mongo-go-driver
Here is an example of what I have tried:
mongoContext, cancelContext := context.WithTimeout(context.Background(), 10*time.Second)
defer cancelContext()
mongoClient, _ := mongo.Connect(mongoContext, options.Client().ApplyURI("mongodb://localhost:27017"))
moviesCollection := mongoClient.Database("Entertainment").Collection("Movies")
moviesCollection.InsertOne(mongoContext, bson.M{"_id": "Deadpool", "Path": "path/to/file"})
singleResult := moviesCollection.FindOne(mongoContext, bson.M{"_id": "Deadpool"})
if singleResult.Err() != nil {
log.Println("Find error: ", singleResult.Err())
}
JSONData := struct {
Path string `json:"Path"`
}{}
decodeError := singleResult.Decode(&JSONData)
if decodeError != nil {
log.Println("Decode error: ", decodeError)
}
fmt.Println("Path: ", JSONData.Path)
However no errors are produced and JSON.Path produces and empty string.
I have also tried using bson.D{{"_id", "Deadpool"}} instead of bson.M{"_id": "Deadpool"}
I can confirm that JSON.Path is not empty string as I have checked the database natively using MongoDB Compass. The entry contains the following:
{"_id":"Deadpool","Path":"path/to/file"}
Internally, MongoDB uses bson. Change your struct as below should work.
From
JSONData := struct {
Path string `json:"Path"`
}{}
to
JSONData := struct {
Path string `bson:"Path"`
}{}
Hey so as simagix mentioned you should be able to change your tag from JSON to bson:
`bson:"Path"`
Another option, incase you need to obtain a more generic result is to pass it a D object like so:
JSONData := &bson.D{}
decodeError := singleResult.Decode(JSONData)
You can then obtain all the information through a map using the JSON.Data.Map function.
If you are using mongo-go-driver >= v.0.1.0 then, taking a look to the go-doc it looks pretty straightforward:
filter := bson.D{{"hello", "world"}}
err := collection.FindOne(context.Background(), filter).Decode(&result)
if err != nil { return err }
// do something with result...
So, what you need is:
package main
import (
"context"
"github.com/mongodb/mongo-go-driver/bson"
"github.com/mongodb/mongo-go-driver/mongo
)
func main() {
ctx := context.Background()
client, err := mongo.NewClient("mongodb://localhost:27017")
if err != nil {
...
}
if err := client.Connect(ctx); err != nil {
...
}
defer client.Disconnect(ctx)
collection := client.Database("myDb").Collection("movies")
filter := bson.D{{"_id", "sometitle"}}
var result JSONData
err := collection.FindOne(ctx, filter).Decode(&result)
if err != nil {
...
}
// Do Something cool here
}
I see you are using Title to filter the doc. Pay attention.

Querying mongodb from golang using the _id stored in an array

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
}