How to find documents with multiple conditions using mongo-driver/mongo - mongodb

While querying the below data, returned cursor is empty. while there is 100s of documents which satisfy the condition.
{
"_id": "5dd68c51a39809125944ffba",
"status": "success",
"balance": "0.000",
"request_params": {
"username": "test_user",
"service_code": "MR"
}
using below code
MongoDB driver "go.mongodb.org/mongo-driver/mongo"
func saveLog(data Log) bool {
mongo, err := openMongo()
if err != nil {
log.Println(err)
fmt.Println("Connection failed")
return false
} else {
LogCollection := mongo.Database(LogDb).Collection(CollectionLog)
insertedApi, err := LogCollection.InsertOne(context.TODO(), data)
if err != nil {
log.Println(err)
fmt.Println("Insert failed")
return false
} else {
log.Println(insertedApi.InsertedID)
return true
}
}
}
func parseLog() {
db, err := openMongo()
if err != nil {
fmt.Println(err)
fmt.Println("Connection failed")
return
} else {
logCollection := db.Database(LogDb).Collection(CollectionLog)
var results [] *Log
find := bson.D{{"status","success"},{"request_params",bson.D{{"username","test_user"}}}}
fmt.Println(find)
cur, err := logCollection.Find(context.TODO(), find)
if err != nil {
log.Fatal(err)
}else {
for cur.Next(context.TODO()) {
var elem Log
err := cur.Decode(&elem)
if err != nil {
fmt.Println("Parse error : ",err)
}
fmt.Println("Log : ",elem)
results = append(results, &elem)
}
}
}
}
Log write
saveLog(Log{"success","0.000",RequestParams{"test_user","MR"}})
Log read
parseLog()
Log struct
type Log struct {
Status string `bson:"status"`
Balance string `bson:"balance"`
RequestParams RequestParams `bson:"request_params"`
}
type RequestParams struct {
Username string `bson:"username"`
ServiceCode string `bson:"service_code"`
}
MongoDB data
status only is returning whole 8k documents
bson.D{{"status","success"}}
Isn't collection.Find() function the right one for it.
Shell command is returning documents correctly
db.log.find({"status":"success","request_params.username":"test_user"}).limit(10).pretty()

The issue here is because of the query filter. There is a difference between the following queries:
// Query A: {"status": "success", "request_params": {"username":"test_user"}}
find := bson.D{{"status","success"},{"request_params",bson.D{{"username","test_user"}}}}
// Query B: {"status": "success", "request_params.username":"test_user"}
find := bson.D{{"status","success"},{"request_params.username","test_user"}}
Query A means that you would like to match an exact document of request_params where the value object exactly equal to {"username":"test_user"}. None of the documents in your collection matches this criteria. The documents also contains {"service_code":"MR"}. While query B uses dot notation, which means that you would like to match request_params field where it contains a value of {"username":"test_user"}.
See also Query on Nested Field for more information.

Related

Incorrect decoding in Mongo-go-driver

For example, I have this structure:
type Overview struct {
Symbol string `json:"Symbol,omitempty"`
AssetType string `json:"AssetType,omitempty"`
Name string `json:"Name,omitempty"`
Description string `json:"Description,omitempty"`
...
...
}
In addition to this, I have several other structures.
My function selects a suitable structure for Decode(), but when I try to get data from the database, I get the result in this form:
[
{
"Key": "_id",
"Value": "618aa6f2a64cb8105a9c7984"
},
{
"Key": "Symbol",
"Value": "IBM"
},
{
"Key": "FiscalYearEnd",
"Value": "December"
},
...
...
]
I expect a response in the form of my structure, but I get such an array. I tried declaring the structure for the response myself: var result models.Overview. After that, the problem disappeared, but this is not the solution for my problem
There is my function:
var (
models map[string]interface{}
)
func init() {
models = make(map[string]interface{})
models["Overview"] = models.Overview{}
models["Earnings"] = models.Earnings{}
...
...
}
func GetDbData(collection string, db *mongo.Database, filter bson.D) (interface{}, error) {
var result = models[collection] // Choosing a structure
res := db.Collection(collection).FindOne(context.TODO(), filter)
err := res.Decode(&result)
if err != nil {
return nil, err
}
return result, nil
}
I can't understand why this is happening, I hope that someone has already encountered this problem and will be able to help me
https://jira.mongodb.org/browse/GODRIVER-988
Another approach to solve this can be by first decoding it into bson.M type and then unmarshalling it to your struct. Yes, this is not optimal.
eg:
func GetMonthStatusByID(ctx context.Context, id string) (interface{}, error) {
var monthStatus interface{}
filter := bson.M\{"_id": id}
err := db.Collection("Months").FindOne(ctx, filter).Decode(&monthStatus)
return monthStatus, err
}
The above snippet should be changed to:
func GetMonthStatusByID(ctx context.Context, id string) (interface{}, error) {
var monthStatus interface{}
filter := bson.M\{"_id": id}
tempResult := bson.M{}
err := db.Collection("Months").FindOne(ctx, filter).Decode(&tempResult)
if err == nil {
obj, _ := json.Marshal(tempResult)
err= json.Unmarshal(obj, &monthStatus)
}
return monthStatus, err
}

How to get DecodeBytes() output without canonical extended JSON additions

I use DecodeBytes() function to get the data from mongoDB (as the struct of the data can varies) with mongo-driver for Go.
My problem is when one of the values is int/double (and not string).
In that case it add adds some stuff of canonical extended JSON, for example 3 to "$numberDouble": "3.0".
How can I remove those additions of the canonical extended JSON?
func (m *Mongoclient) Find(collection string, filter interface{}) string {
findResult := m.Db.Collection(collection).FindOne(m.Ctx, filter)
if findResult.Err() != nil {
fmt.Println(findResult.Err().Error())
return ""
}
db, err := findResult.DecodeBytes()
if err != nil {
fmt.Println(err.Error())
return ""
}
return db.String()
}
The solution was to use Decode function to bson.M and than json.Marshal it:
var document bson.M
err := findResult.Decode(&document)
if err != nil {
fmt.Println(err.Error())
return ""
}
resBytes, err := json.Marshal(document)
if err != nil {
fmt.Println(err)
return ""
}
return string(resBytes)

How to Decode the returns of Aggregate()

I'm trying to do a query in MongoDB, just like the group by in SQL. So I use the Aggregate() API, to find all the unique _id(MarketType+Symbol).
And the Aggregate() runs successful, but when I decode the return values, I got nothing. Aggregate() return type is the same as Find(), and I decode in this way when Find() is useful. Why it can't work when I Aggregate().
type MarketSymbol struct {
MarketType string `json:"Market,omitempty" bson:"MarketType"`
Symbol string `json:"Symbol,omitempty" bson:"Symbol"`
}
func GetSymbol() {
pipeline := bson.M {
"$group" :
bson.M{"_id":bson.M{"MarketType":"$MarketType","Symbol":"$Symbol"}},
}
cur,err := mongoSt.Collection.Aggregate(context.TODO(), []bson.M{pipeline})
if err != nil {
fmt.Println("Aggregate Error:", err)
} else {
fmt.Println("Aggregate Success!") //this line works
}
var results []MarketSymbol
count := 1
for cur.Next(context.TODO()) {
var elem MarketSymbol
err := cur.Decode(&elem)
if err != nil {
fmt.Printf("[count]=%d Decode Error\n", count)
count++
continue
}
fmt.Printf("[count]=%d, [MarketType]:%s , [Symbol]:%s\n", count, elem.MarketType, elem.Symbol) //this line works,but value is nil
count++
results = append(results, elem)
}
}
I got it. I also tried to use _id to save the Decode value before, but failed. So I thought it doesn't work. But it's right, what actually caused the error is I gave a wrong type of MarketType, it should be int32 but I gave it a string. So...
Anyway, the problem solved, here is the code.
type ID struct {
Id MarketSymbol `json:"ID,omitempty" bson:"_id"`
}
type MarketSymbol struct {
MarketType int32 `json:"Market,omitempty" bson:"MarketType"`
Symbol string `json:"Symbol,omitempty" bson:"Symbol"`
}
func GetSymbol{
pipeline := bson.M {
"$group" : bson.M{"_id":bson.M{"MarketType":"$MarketType","Symbol":"$Symbol"}},
}
cur,err := mongoSt.Collection.Aggregate(context.TODO(), []bson.M{pipeline})
if err != nil {
fmt.Println("Aggregate Error:", err)
} else {
fmt.Println("Aggregate Success!")
}
var results []ID
count := 1
for cur.Next(context.TODO()) {
var elem ID
err := cur.Decode(&elem)
if err != nil {
fmt.Printf("[count]=%d Decode Error\n", count)
count++
continue
}
fmt.Printf("[count]=%d, [MarketType]:%d , [Symbol]:%s\n", count, elem.Id.MarketType, elem.Id.Symbol)
count++
results = append(results, elem)
}
}

Filter in golang mongodb

Currently I learn to create restful api with golang and mongodb. Actually I am beginner in both. I use mongodb-go-driver and I learn to use filter when we want to use find() function. But I have some that I don't understand. What is the different between filter := bson.M{"_id": "abcd"} and filter := bson.M{{"_id": "abcd"}}? Thank you
Refer to the source code, https://github.com/mongodb/mongo-go-driver/blob/master/bson/primitive/primitive.go
bson.D, internally is primitive.D, which is []primitive.E, which is a struct. bson.M, internally is primitive.M, which is map[string]interface{}. You put in key/value in bson.M but use document (struct) in bson.D.
It is better to explain it using 2 parameters, e.g. search for a = 1 and b = 2. You syntax will be: bson.M{"a": 1, "b": 2} or bson.D{{"a": 1}, {"b": 2}}
//Filter Part
if filter != nil {
if len(filter.Status) > 0 {
query = append(query, bson.M{"status": bson.M{"$in": filter.Status}})
}
}
d.Shared.BsonToJSONPrint(query)
//Getting Total count
totalCount, err := col.Find(func() bson.M {
if query != nil {
if len(query) > 0 {
return bson.M{"$and": query}
}
}
return bson.M{}
}
var user []models.User
var findQuery []bson.M
coll := db.C(constants.USERTABLE)
if name != nil {
if len(name) > 0 {
query = append(query, bson.M{"name": bson.M{"$in": name}})
}
}
if status != nil {
if len(status) > 0 {
query = append(query, bson.M{"status": bson.M{"$in": status}})
}
}
findQuery = append(findQuery, bson.M{"$match": func() bson.M {
if query != nil {
if len(query) > 0 {
return bson.M{"$and": query}
}
}
return bson.M{}
}()})
shared.BsonToJSONPrint(query)
err = coll.Pipe(findQuery).All(&user)
if err != nil {
return nil, err
}
return user, nil
}
The client decodes the result to a bson.D document by default, this is represented as an array, which is not the document shape we want.
To overwrite this behaviour you have to decode to a bson.M, which is a simple map and has the same shape of the document in database
res := collection.FindOne(ctx, p.Where)
if res.Err() == mongo.ErrNoDocuments {
return nil, nil
}
if res.Err() != nil {
return nil, res.Err()
}
var document bson.M = make(bson.M) // important
err = res.Decode(document)

MongoDB bson.M query

I am trying to query using bison all JSON data in MongoDB with two fields but am getting null as result.
{
"allowedList": [
{
"List": [
{
"allow": {
"ss": 1,
},
"Information": [
{
"Id": "Id1"
}
]
}
]
}
]
}
I was able to filter all using the MongoDB at command line using
db.slicedb.find({"allowedList.List.allow.ss":1,"allowedList.List.Information.nsiId":"Id-Id21"})
but using
query := bson.M{"allowedList.List.allow": bson.M{"ss": sst}, "allowedList.List.Information": bson.M{"Id": Id}}
sst and Id are integer and string input to the query function
err := db.C(COLLECTION).Find(query).All(&specificSlices)
but is not working, am getting null even though there are json data that match the two field. Can someone help point out what was wrong with my query?
Server and database config
type SliceDataAccess struct {
Server string
Database string
}
var db *mgo.Database
const (
COLLECTION = "slicedb"
)
Establish a connection to database
func (m *SliceDataAccess) Connect() {
session, err := mgo.DialWithTimeout(m.Server, 20*time.Second)
if err != nil {
log.Fatal(err)
}
db = session.DB(m.Database)
}
Structs fields
type InstanceInfo struct {
ID string `json:"nfId" bson:"_id"`
AllowedList []AllowedNssai `json:"allowedList" bson:"allowedList"`
}
type AllowedNssai struct {
List []AllowedSnssai `json:"List,omitempty" bson:"List"`
...
}
type AllowedSnssai struct {
Allow *Snssai `json:"allow,omitempty" bson:"allow"`
Information []NsiInformation `json:"Information,omitempty" bson:"Information"`
}
type NsiInformation struct {
Id string `json:"Id" bson:"Id"`
}
type Snssai struct {
Ss int32 `json:"sst" bson:"ss"`
}
Query function defined
func (m *SliceDataAccess) FindAll(sst int32, nsiId string ([]InstanceInfo, error) {
var specificSlices []InstanceInfo
query := bson.M{"allowedList.List.allow": bson.M{"ss": sst}, "allowedList.List.Information": bson.M{"Id": nsiId}}
err := db.C(COLLECTION).Find(query).All(&specificSlices)
if err != nil {
return specificSlices, err
}
return specificSlices, nil
}
HTTP handler function for request and response
func AvailabilityGet(w http.ResponseWriter, r *http.Request)
var slice InstanceInfo
err := json.NewDecoder(r.Body).Decode(&slice)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Object body not well decoded")
return
}
sst := slice.AllowedList[0].List[0].Allow.Sst
nsiId := slice.AllowedList[0].List[0].Information[0].Id
specificSlices, err := da.FindAll(sst, nsiId)
json.NewEncoder(w).Encode(specificSlices)
}
Attached is my the full go code i have done.
this worked
query := bson.M{"allowedNssaiList.allowedSnssaiList.allowedSnssai.sst": sst, "allowedNssaiList.allowedSnssaiList.nsiInformationList.nsiId": nsiId}