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
}
Related
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.
I'm creating a Golang API, but hit a blocker. For every POST, this is what I get:
"error": "sql: converting argument $2 type: unsupported type main.Data, a struct"
I'd like my data to be of format
"name": "test",
"other": {
"age": "",
"height": ""
}
}
How can I achieve this? See the code below, what I've tried so far.
model.go
type Data struct {
ID int `json:"id"`
Name string `json:"name,omitempty"`
Other *Other `json:"other,omitempty"`
}
type Other struct {
Age string `json:"host,omitempty"`
Height string `json:"database,omitempty"`
}
func (d *Data) Create(db *sql.DB) error {
err := db.QueryRow(
"INSERT INTO configs(name, other) VALUES($1, $2) RETURNING id",
d.Name, &d.Other).Scan(&d.ID)
if err != nil {
return err
}
return nil
}
controller.go
...
func (a *App) createData(w http.ResponseWriter, r *http.Request) {
var d Data
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&d); err != nil {
fmt.Print(err)
fatalError(w, http.StatusBadRequest, "Invalid request")
return
}
defer r.Body.Close()
if err := d.Create(a.DB); err != nil {
fatalError(w, http.StatusInternalServerError, err.Error())
return
}
jsonResponse(w, http.StatusCreated, d)
}
I expected the db to be populated with the data of format
"name": "test",
"other": {
"age": "",
"height": ""
}
}
But fails with error:
"error": "sql: converting argument $2 type: unsupported type main.Data, a struct"
You can make this modification:
import "encoding/json"
func (d *Data) Create(db *sql.DB) error {
var jsonOther string
if d.Other != nil {
jsonOther, _ = json.Marshal(d.Other)
err := db.QueryRow(
"INSERT INTO configs(name, other) VALUES($1, $2) RETURNING id",
d.Name, jsonOther).Scan(&d.ID)
if err != nil {
return err
}
return nil
}
Notice that the Other object is explicitly serialized to JSON and passed as a string
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}
There is an array object which is retrieved from mongodb. The data looks like below:-
[{
1
fruits
Apple
Apple is my favorite fruit.
}
{
2
colors
Red
Red color is always charming.
}
{
3
flowers
Lotus
It is one of the most beautiful flowers in this world.
}]
This is the code for retrieving the above data
The struct is:
type Item struct {
Id int `json:"id"`
Category string `json:"category"`
Name string `json:"name"`
Description string `json:"description"`
}
type Items []Item
func GetData(Query interface{}) (result Items, err error) {
mongoSession := ConnectDb()
sessionCopy := mongoSession.Copy()
defer sessionCopy.Close()
getCollection := mongoSession.DB("custom").C("custom")
err = getCollection.Find(Query).All(&result)
if err != nil {
return result, err
}
return result, nil
}
/*
* Retrieve the data used by main function
*/
func retrieve(c *gin.Context) {
//mongoSession := ConnectDb()
conditions := bson.M{}
data, err :=GetData(conditions)
if err != nil {
fmt.Println("There is somthing wrong")
}
arrange(data)
return
}
func arrange(data Items) {
pagesJson, err := json.Marshal(data)
if err != nil {
log.Fatal("Cannot encode to JSON ", err)
}
fmt.Println(string(pagesJson))
}
Running json.Marshal, the output looks like below:
[{
"id":1,
"category":"fruits",
"name":"Apple",
"description":"Apple is my favorite fruit."
},
{
"id":2,
"category":"colors",
"name":"Red",
"description":"Red color is always charming."
},
{
"id":3,
"category":"flowers",
"name":"Lotus",
"description":"It is one of the most beautiful flowers in this world."
}]
Expected output
{
"id":1,
"category":"fruits",
"name":"Apple",
"description":"Apple is my favorite fruit."
}
{
"id":2,
"category":"colors",
"name":"Red",
"description":"Red color is always charming."
}
{
"id":3,
"category":"flowers",
"name":"Lotus",
"description":"It is one of the most beautiful flowers in this world."
}
The issue is the data is in the array object for my use I need the String structure data between {} as shown above. I posted this question before but not getting any success answer. I am trying it from a lot of time help me thankyou.
Based on what OP has discussed, marshaling the struct Items would give this:
[{
"id":1,
"category":"fruits",
"name":"Apple",
"description":"Apple is my favorite fruit."
},
{
"id":2,
"category":"colors",
"name":"Red",
"description":"Red color is always charming."
},
{
"id":3,
"category":"flowers",
"name":"Lotus",
"description":"It is one of the most beautiful flowers in this world."
}]
From this, OP wants to get a string that looks like this:
{
"id":1,
"category":"fruits",
"name":"Apple",
"description":"Apple is my favorite fruit."
},
{
"id":2,
"category":"colors",
"name":"Red",
"description":"Red color is always charming."
},
{
"id":3,
"category":"flowers",
"name":"Lotus",
"description":"It is one of the most beautiful flowers in this world."
}
There are two approaches that I considered:
Looping through items and appending each marshaled items into a string (clearly not efficient)
func getMyString(items Items) (string, error) {
var buffer bytes.Buffer
var err error
var b []byte
for _, item := range items {
b, err = json.Marshal(item)
if err != nil {
return "", err
}
buffer.WriteString(string(b) + ",")
}
s := strings.TrimSpace(buffer.String())
// trim last comma
s = s[:len(s)-1]
return s, nil
}
Or just trimming off the leading and trailing square brackets:
func getMyString2(items Items) (string, error) {
b, err := json.Marshal(items)
if err != nil {
return "", err
}
s := string(b)
s = strings.TrimSpace(s)
// trim leading and trailing spaces
s = s[1 : len(s)-1]
return s, nil
}
Link to code: https://play.golang.org/p/F1SUYJZEn_n
EDIT: Since OP want it to be space separated, I've modified approach number 1 to fit the requirements:
func getMyString(items Items) (string, error) {
var buffer bytes.Buffer
var err error
var b []byte
for _, item := range items {
b, err = json.Marshal(item)
if err != nil {
return "", err
}
// use space to separate each json string in the array
buffer.WriteString(string(b) + " ")
}
s := strings.TrimSpace(buffer.String())
return s, nil
}
Link to new code: https://play.golang.org/p/dVvsQlsRqZO
I am working on REST api build with golang when I am returning a json of employees like
{
"data":{
"10":{
"1517616000":[1000]
},
"15":{
"1517616000":[1200]
},
"29":{
"1517616000":[1200]
},
"42":{
"1517616000":[1200]
}
}
}
it should be showing in 42,15,29,10 order as I am sorting the data with priority parameter. When I check with console it shows me correct output but not on postman and browser
Used following function to return json
c.JSON(200, gin.H{
"status": response,
})
If you store the 42,15,29,10 as keys in a Go map, the JSON mapEncoder is going to sort them by key when printing to JSON.
Instead we can store the data as elements in a slice, which preserves the order we specify. In order to have the slice print as a JSON map rather than a JSON array we need to plug in a bit of our own JSON marshaling logic.
package main
import (
"bytes"
"encoding/json"
"fmt"
)
type Response struct {
Data OrderedMap `json:"data"`
}
type OrderedMap []Entry
type Entry struct {
Key string
Value interface{}
}
func (m OrderedMap) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
buf.WriteByte('{')
for i, entry := range m {
if i != 0 {
buf.WriteByte(',')
}
key, err := json.Marshal(entry.Key)
if err != nil {
return nil, err
}
buf.Write(key)
buf.WriteByte(':')
value, err := json.Marshal(entry.Value)
if err != nil {
return nil, err
}
buf.Write(value)
}
buf.WriteByte('}')
return buf.Bytes(), nil
}
func main() {
response := Response{
Data: OrderedMap{
{"42", map[string][]int{"1517616000": []int{1200}}},
{"15", map[string][]int{"1517616000": []int{1200}}},
{"29", map[string][]int{"1517616000": []int{1200}}},
{"10", map[string][]int{"1517616000": []int{1000}}},
},
}
j, err := json.MarshalIndent(response, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(j))
}
The result is the same JSON representation you were seeing before but with the keys in the order specified by our OrderedMap.
{
"data": {
"42": {
"1517616000": [1200]
},
"15": {
"1517616000": [1200]
},
"29": {
"1517616000": [1200]
},
"10": {
"1517616000": [1000]
}
}
}