I have this function which uses the official mongo-go-driver (https://github.com/mongodb/mongo-go-driver)
func FindItemMongo(dataStruct interface{}, subItemKey string, collectionName string)(){
fmt.Println("inside FindDataMongo in Controller")
localDB := DB
coll := localDB.Collection(collectionName)
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
var searchData []uint8
if subItemKey==""{
fmt.Println("inside no key searchData")
searchData, _ = bson.Marshal(bson.M{"data": dataStruct})
fmt.Println("value of searchData")
fmt.Println(searchData)
fmt.Println("value of reflect type of searchData")
fmt.Println(reflect.TypeOf(searchData))
}else{
reflectItem := reflect.ValueOf(dataStruct)
subItem := reflectItem.FieldByName(subItemKey)
subItemString := subItem.Interface().(string)
searchData, _ = bson.Marshal(bson.M{"data": bson.M{strings.ToLower(subItemKey): bson.M{"$eq": subItemString}}})
fmt.Println("value of searchData")
fmt.Println(searchData)
fmt.Println("value of reflect type of searchData")
fmt.Println(reflect.TypeOf(searchData))
}
cursor, err := coll.Find(ctx, searchData)
if err != nil {
log.Fatal(err)
}
defer cursor.Close(ctx)
var returnBson []map[string]interface{}
var result bson.M
for cursor.Next(ctx) {
err := cursor.Decode(&result)
if err != nil { log.Fatal(err) }
fmt.Println("value of found parsed")
fmt.Println(result)
returnBson = append(returnBson, result)
}
}
Which is attempting to find data that is in the database for two conditions, related to the if statement. If the subItemKey is not defined, then it searches for an exact match to the passed in struct, if the key is defined, then it searches for a match in the data using the $eq
operator for only a specific field.
This works for the condition where the subItemKey is empty. Like this:
database.FindItemMongo(dataStruct, "", "users")
Gives
api | 1:36:55 app | value of found parsed
api | map[_id:ObjectID("8494398fsfd") data:map[email:qwer password:wer token:...]]
However, if I use the command:
database.FindItemMongo(dataStruct, "Email", "users")
I get nothing.
Something is not correct with how I define searchData in the else statement and I'm not sure what. Does anyone see what is going wrong?
EDIT
I should mention that the result of
unmarshalledSearch := bson.M{"data": bson.M{strings.ToLower(subItemKey): bson.M{"$eq": subItemString}}}
fmt.Println("value of unmarshalledSearch")
fmt.Println(unmarshalledSearch)
is
api | value of unmarshalledSearch
api | map[data:map[email:map[$eq:qwer]]]
which is the same format as the data that is coming back from the empty field query. This is the crux of the problem - if the searchData appears to be correctly formatted, why is the cursor not returning with any results?
I'm new to Golang and do not have an environment to test this, but I think you should start by following the examples in the MongoDB Documentation and change
var searchData []uint8
searchData, _ = bson.Marshal(bson.M{"data": bson.M{strings.ToLower(subItemKey): bson.M{"$eq": subItemString}}})
to
var searchData bson.D
searchData = bson.D{{"data." + strings.ToLower(subItemKey), bson.D{{"$eq", subItemString}}}}
You definitely should use bison.D
and not bison.M
for the case where subItemKey
is empty because the whole sub-document match you are doing will fail if the fields are in the wrong order.
For the special case of searching for exact match, you can use the simplified format
searchData = bson.D{{"data." + strings.ToLower(subItemKey), subItemString}}
It might not solve your problem but it does at least follow the recommended pattern.
If that does not fix your search, please add to your post the versions of the Mongo driver and database you are using, the full document you expect to retrieve, the declaration of the dataStruct
type, and the initializer for the dataStruct
that you are passing in.
Given the struct Login
type Login struct {
Email string `json:"email"`
Password string `json:"password"`
Token string `json:"token"`
}
Assuming you have data as
{data: {email: "name@example.com", password: "password", token: "token"}}
When subItemKey
is blank, you're searching exactly like above. But when you search using only Email, the syntax should be
{"data.email": "name@example.com"}
But your code generates the search criteria as
{"data":{"email":{"$eq":"name@example.com"}}}
To make it work, change the line from
searchData, _ = bson.Marshal(bson.M{"data": bson.M{strings.ToLower(subItemKey): bson.M{"$eq": subItemString}}})
to
searchData, _ = bson.Marshal(bson.M{"data." + strings.ToLower(subItemKey): subItemString})
I think you want to search a nested property and therefore, you should change the query
This:
searchData, _ = bson.Marshal(bson.M{"data": bson.M{strings.ToLower(subItemKey): bson.M{"$eq": subItemString}}})
To
nestedKey := fmt.Sprintf("data.%s", strings.ToLower(subItemKey))
searchData, _ = bson.Marshal(bson.M{nestedKey: subItemString})