This question already has answers here:
Accessing Nested Map of Type map[string]interface{} in Golang
(2 answers)
Is there any convenient way to get JSON element without type assertion?
(2 answers)
invalid operation: type interface {} does not support indexing
(3 answers)
Closed last month.
I recently started studying Golang and I have a problem now, I can't find any information or documentetion about it :( Please, I need your help! I have some structure in mongodb:
"id": 123,
"attributes.store.name": TestStore
I need to get these values (id, name), I use this code:
...
opt := options.Find().SetProjection(bson.M{
"_id": 0,
"id": 1,
"attributes.store.name": 1})
opt.SetLimit(1) //1 just for test, I need to get 100000
var curr *mongo.Cursor
curr, err = visits.Find(context.Background(), filter, opt)
if err != nil {
log.Fatal(err)
}
var results []bson.M
if err = curr.All(context.Background(), &results); err != nil {
log.Fatal(err)
}
for _, result := range results {
fmt.Println(result)
}
But in this way I get:
[map[attributes:map[store:map[name:TestStore]] id:123]]
So I can't understand how I can get exactly "name", because I can use result["attributes"], but I can't get result["attributes"]["store"]["name"] because the result["attributes"] has a "primitive.M" type.
Is there maybe a way to "avoid" keys "attributes.store" in "attributes.store.name": 1?
Or how it possible to get the "name"?
And please give me a documentatial link about it, beacuse unfortunately I couldn't find anytithg about this case.
///////////////////////////////////////////////////////
I have tried this:
opt := options.Find().SetProjection(bson.M{
"_id": 0,
"id": 1,
"attributes.store.name": 1})
I expected to see:
[map[name:TestStore] id:123]]
I've got:
[map[attributes:map[store:map[name:TestStore]] id:123]]
This way I can't get the value under the key "name"
Related
I'm stuck at a probably simple problem: If I filter this in mongodb compass (filter {dateTime:{$gt: new Date("2020-11-23T12:31:38")}}):
It returns 556 documents.
Trying to create a cursor in Go that have those documents is proving to be quite hard!
I've this right now:
cursor, err := coll.Find(context.Background(), bson.M{"dateTime": bson.M{"$gt": "new Date("+ date + ")"}}, opt)
if err != nil {
fmt.Println("Err creting database: ", err)
return nil, err
}
if cursor.Next(context.Background()) {
fmt.Println("Cursor0!")
cursor.Next(context.Background())
}
cursor1, err := coll.Find(context.Background(), bson.M{}, opt)
if err != nil {
fmt.Println("Err creting database: ", err)
return nil, err
}
if cursor1.Next(context.Background()) {
fmt.Println("Cursor1!")
cursor.Next(context.Background())
}.
I've tried, along other different tries, to put the filter just as bson.M{"dateTime": bson.M{"$gt": date}}, along other similar tryes, but they also returned 0 documents. The date variable have exacly the date used in the mongodb compass filter.
I created another cursor, with no filter, just to control if the connection with mongo is ok, and to see if it returns any documents when it has no filter, and it does return documents. Does anyone knows the answer to this one?
Thanks!!
new Date("2020-11-23T12:31:38") is JavaScript syntax. You need to use the proper Go syntax for creating timestamps.
The problem was that I was dealling with more than 1 Collection, and in one the date was saved as string, and in the other, as date. In the one that the date s saved as string, no surprise, we have to send the date as string too, some logic to when date is in mongo as Date
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 am getting the following error while deleting from key from array of JSON objects using Go.
Error:
repository/orderRepository.go:394:11: first argument to delete must be map; have interface {}
repository/orderRepository.go:395:11: first argument to delete must be map; have interface {}
repository/orderRepository.go:396:11: first argument to delete must be map; have interface {}
repository/orderRepository.go:397:11: first argument to delete must be map; have interface {}
repository/orderRepository.go:398:11: first argument to delete must be map; have interface {}
I am explaining my code below.
func SyncOrders() map[string]interface{} {
logger.Log.Println("OrderRepository SyncOrders Begin")
resourceManager := resources.ResourceManager{}
session, error := driver.Connect()
db := session.DB(config.Configuration.Database)
var resp map[string]interface{}
if error != nil {
resp := utils.Message(resourceManager.GetProperty(constants.ERROR), resourceManager.GetProperty(constants.DB_SERVER_NOT_REACHABLE_CODE), resourceManager.GetProperty(constants.DB_SERVER_NOT_REACHABLE_DESC))
return resp
} else {
var result []interface{}
//filter := bson.M{"Customer.CustomerID": id, "PaymentDetails.PaymentStatus": "Payment Received"}
//fmt.Println(filter)
err := db.C(ORDERCOLLECTION).Find(nil).All(&result)
if err == nil {
resp = utils.Message(resourceManager.GetProperty(constants.SUCCESS), resourceManager.GetProperty(constants.PRODUCT_GETBYID_CODE), resourceManager.GetProperty(constants.PRODUCT_GETBYID_DESC))
for i := 1; i < len(result); i++ {
delete(result[i],"_id");
delete(result[i],"CreatedAt");
delete(result[i],"CreatedBy");
delete(result[i],"UpdatedAt");
delete(result[i],"UpdatedBy");
}
resp["data"] = result
} else {
//fmt.Println(err)
resp = utils.Message(resourceManager.GetProperty(constants.ERROR), resourceManager.GetProperty(constants.PRODUCT_GETBYID_NOTFOUND_CODE), resourceManager.GetProperty(constants.PRODUCT_GETBYID_NOTFOUND_DESC))
}
defer session.Close()
return resp
}
}
Here I am fetching some record from MongoDB and delete some key value from each record but when I am running the server I am getting these errors. As I am beginner to Go. Can anybody help me to resolve these errors?
The error message says it all: the first argument to the builtin delete() must be a value of static type map.
Your result variable is of type []interface{}, so indexing it like result[i] will result in a value of type interface{}.
If it holds a map, you may use type assertion to obtain the map value from it. Since you use the mgo driver, it is of type bson.M (which is a map[string]interface{}), so you may do it like this:
delete(result[i].(bson.M), "_id")
But it would be better if you would declare result to be a slice of maps in the first place:
var result []bson.M
So then no type assertion will be needed, and the following will be valid code:
delete(result[i], "_id")
Also note that if you want to remove these properties from the results, it would be best if you would tell MongoDB you don't need these fields and so the server wouldn't even send these (saving network traffic) and then you wouldn't have to remove them (saving time and memory).
Use projection to tell you don't need these fields. In mgo you can set a projection using the Query.Select() method.
For example:
err := db.C(ORDERCOLLECTION).Find(nil).Select(bson.M{
"_id": 0,
"CreatedAt": 0,
"CreatedBy": 0,
"UpdatedAt": 0,
"UpdatedBy": 0,
}).All(&result)
The above query will result in documents where the listed fields will not be present, so you don't have to manually remove them using delete().
You are trying to delete key from variable of type interface{}.
#icza gives a good solution.
You can use .Select() to select which fields should be retrieved for the results.
Then you don't need to delete those fields from every object.
For example, the following query would only retrieve the name and age field:
err := db.C(ORDERCOLLECTION).Find(nil).Select(bson.M{"name": 1, "age": 1}).All(&result)
I'm having a bit of trouble creating a query using the bson functionality of mgo. I'm simply trying to do {'search_id': {'$in': [1,2,4,7,9]}}, but I can't work out how to do it in mgo.
I have a slice of ints, and tried passing that directly:
toRemove := []int{1,2,4,7,9}
err = coll.Remove(bson.M{"search_id": bson.M{"$in": toRemove}})
I saw another post which suggested I needed to use []interface{}, but that doesn't work either:
toRemoveI := make([]interface{}, len(toRemove))
for idx, val := range toRemove {
toRemoveI[idx] = val
}
err = coll.Remove(bson.M{"search_id": bson.M{"$in": toRemoveI}})
I've looked through he docs and other questions here and on gh, but most questions involving slices seem to be about getting data into a slice as opposed to what I'm trying to achieve.
Any help would be most appreciated.
Your original proposal (passing an []int value) has no flaws, it's valid to do that.
What the problem is is that you use Collection.Remove() which finds and removes a single document matching the provided selector document. So your proposed solution will remove exactly 1 document, one whose search_id is contained in the slice you passed. If no such document is found (and the session is in safe mode, see Session.SetSafe()), mgo.ErrNotFound is returned.
Instead use Collection.RemoveAll() which finds and removes all documents matching the selector:
toRemove := []int{1,2,4,7,9}
info, err := c.RemoveAll(bson.M{"search_id": bson.M{"$in": toRemove}})
if err != nil {
log.Printf("Failed to remove: %v", err)
} else {
log.Printf("Removed %d documents.", info.Removed)
}
Mgo and golang question.
I run into problem again. I try to update record in the database, but running simple command visitors.UpdateId(v.Id, bson.M{"$set": zscore}); where zscore is a variable of type Zscore, does not work. However if I manually convert zscore to bson.M structure, everything works fine.
Does anybody know how to update the record in mongodb using mgo, without manually dumping structure values into bson.M?
Example:
type Zscore struct {
a float64 `bson:"a,omitempty" json:"a"`
b float64 `bson:"b,omitempty" json:"b"`
c float64 `bson:"c,omitempty" json:"c"`
}
v := Visitor{}
zscore := Zscore{}
visitors := updater.C("visitors")
for result.Next(&v) {
zscore.a = 1
zscore.b = 2
zscore.c = 0
//does not work
if err := visitors.UpdateId(v.Id, bson.M{"$set": zscore}); err != nil {
log.Printf("Got error while updating visitor: %v\n", err)
}
//works
set := bson.M{
"zscore.a": zscore.a,
"zscore.b": zscore.b,
"zscore.c": zscore.c,
}
if err := visitors.UpdateId(v.Id, bson.M{"$set": set}); err != nil {
log.Printf("Got error while updating visitor: %v\n", err)
}
}
All Go marshaling packages I'm aware of, including the bson package, will not marshal fields that are private (start with a lowercase letter). To fix the issue, just export the relevant fields by uppercasing the first letter of their name.
Also note that, besides the issue mentioned above, the first part of your example will not marshal in an equivalent way to the second part. bson.M{"$set": zscore} is equivalent to bson.M{"$set": bson.M{"a": ... etc ...}}.