Golang/mgo: Cannot retrieve value of int field from MongoDB document - mongodb

I am querying a collection that includes an integer value among it's values, and loading resulting documents into this struct:
type Subscription struct {
Id bson.ObjectId "_id,omitempty"
Listen string
Job string
TimeoutSeconds int
Data string
}
var subscription Subscription
subscriptions := subscriptionsCol.Find(bson.M{"listen": "example_channel"}).Iter()
for subscriptions.Next(&subscription) {
log("Pending job: %s?%s (timeout: %d)\n",
subscription.Job,
subscription.Data,
subscription.TimeoutSeconds)
}
This is what phpMoAdmin shows me:
[_id] => MongoId Object (
[$id] => 502ed8d84eaead30a1351ea7
)
[job] => partus_test_job_a
[TimeoutSeconds] => 30
[listen] => partus.test
[data] => a=1&b=9
It puzzles me that subscription.TimeoutSeconds contains always 0, when I'm positive I have 30 in the document I inserted in the collection.
All other values are retrieved OK.
What's wrong with the int type?

Have you tried setting the "key" value for that field?
Unmarshal
The lowercased field name is used as the key for each exported field,
but this behavior may be changed using the respective field tag.
type Subscription struct {
Id bson.ObjectId "_id,omitempty"
Listen string
Job string
TimeoutSeconds int "TimeoutSeconds"
Data string
}
The other fields are working fine because their lowercase value matches your Mongo fields in the collection, whereas TimeoutSeconds is using the TitleCase. What is happening is the int field is being left at its zero value, since the Unmarshal can't map a field to it.

When UnMarshalling data, there are multiple keys that are supported.
Below are some examples:
type T struct {
A bool
B int "myb"
C string "myc,omitempty"
D string `bson:",omitempty" json:"jsonkey"`
E int64 ",minsize"
F int64 "myf,omitempty,minsize"
}
The general spec for 1 key-value pair during marshal is :
"[<key>][,<flag1>[,<flag2>]]"
`(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
GO provides support for particular keywords like bson (for mongo keys) and json for setting the json key in a resposne.
Check the Marshal GO Reference for more information.
Similarly there are some frameworks which provide further options to define the keys befor parsing. For example, in sql jinzhu github library gives support for setting default values, column ids to map, etc.
Anyone can use this feature and provide customized support.

Related

HTTP PATCH method in golang

I am implementing a HTTP PATCH method in golang and postgresql, I need to read the data from the request and update the data provided into the postgresql.
The method works fine if all the values of the struct is provided, but if only partial data is given to a request, the other fields are becoming empty. Can anyone please help me out to deal this problem.
type StudentDetails struct {
Id int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
Class int `json:"class"`
}
Query which I am using "UPDATE table_name SET name=$2, age=$3, class=$4 WHERE id=$1"
If all the fields given in the request this works fine, but if I need to update only the AGE and the request json would be {"age": 10} or some other field we dont know. Here Age is set to 10 but remaining all fields will become "" or 0 and that will be updated into the database
Anyone has the solution to this problem, how to update the requested field only and not change other fields.
I have approached using a separate query for all fields, but I think its not the proper one. Please give me a solution
Like what #David Hall said in the comments, use pointer to a type in the fields of your struct. See example below:
type ArticleModel struct {
gorm.Model
ID uuid.UUID `gorm:"primaryKey;type:uuid"`
Title *string
Body *string
}
I am using gorm, then when unmarshalling the json from http request you will get nil value if not provided. You need to add nil checkers to skip in the construction of your SQL query. Something like this.
if entity.Title != nil {
model.Title = entity.Title
}
if entity.Body != nil {
model.Body = entity.Body
}
db.Save(&model)
Now the next problem would be how to intentionally empty the field in database (explicitly nulling the value) like UPDATE table SET field = null. Unfortunately I haven't figured it out also.
What I have in mind is checking if empty string and considering it as the setting it to empty value. Although I want to see better options.

Get embedded object values in an ordered map from MongoDb Go driver

GO version: 1.18.3
Mongodb version: 4.4
Mongodb driver used: go.mongodb.org/mongo-driver/bson
I want to receive an embedded object saved in the database in the form of a map with preserved order (order matters for me). For this I have used following package:
https://github.com/elliotchance/orderedmap
from the following reference:
Preserve map order while unmarshalling bson data to a Golang map
Now I have a struct below:
type PageResp struct {
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
Status int `json:"status" bson:"status"`
AddedSections []string `json:"added_sections" bson:"added_sections"`
Sections *orderedmap.OrderedMap `json:"sections,omitempty" bson:"sections,omitempty"`
}
But Sections are empty by using type like this. I have tried it by defining type alias as well. Nothing is working.
After that I receive the data from mongodb in a temp variable with type bson.D then explicitly loop over this to build an ordered map. See the code below:
type PageResp struct {
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
Status int `json:"status" bson:"status"`
AddedSections []string `json:"added_sections" bson:"added_sections"`
SectionsTemp2 *orderedmap.OrderedMap `json:"sections_temp2,omitempty" bson:"sections_temp2,omitempty"`
SectionsTemp bson.D `json:"sections_temp,omitempty" bson:"sections_temp,omitempty"`
}
// function to convert bson.D to ordered map
func test(sections bson.D) *orderedmap.OrderedMap {
newSections := orderedmap.NewOrderedMap()
for _, section := range sections {
childMap := orderedmap.NewOrderedMap()
for _, v := range section.Value.(bson.D) {
childMap.Set(v.Key, v.Value)
}
// fmt.Println("childMap", childMap)
newSections.Set(section.Key, *childMap)
}
// fmt.Println("newSections", newSections)
return newSections
}
But this method is returning values like:
result: &{map[123:0xc0000125d0 foo:0xc000012570 qux:0xc0000125a0] 0xc000012540}
But it should return values like:
result: [["foo","bar"],["qux",1.23],[123,true]]
It shows that this package is also not maintaining the order. Also if some other method for ordered map exists, please write to me.
Thanks!

.NET Core REST API Detect Put/Post Field From Request

I have an endpoint for create/update a data object that has 5 fields in its table.
There are 5 fields for creation:
class A
{
string field1
string field2
int field3
int field4
bool field5
}
When a requester call my endpoint, I want to detect which fields posted in request body. Because i will use those posted fields for my update. I don't want to use other fields.
For ex: Requester has created a data with using 5 fields. After a while, requster just posted 3 fields those wanted to update. He doesn't want to update other 2 fields. So I need to detect which fields sent to endpoint and update only these 3 fields.
When the client doesn't include one or more fields from your model, it's called "underposting." In that case, unless you've marked the missing fields as required (using an attribute), they will be set to the default value.
So, assuming you can set establish a "magic" default value for each field (a value that would not come from the client), you can determine which were sent by the client and which were not, and change your logic accordingly. For example:
class A {
string field1 = "nosave";
string field2 = "nosave";
int field3 = -1;
int field4 = -1;
bool field5;
}
public IActionResult foo(A request) {
if(request.A != "nosave") { saveField(request.A); }
...
}
The tricky part would be fields like Booleans, where no possible values can be ignored. For that, you could probably use a nullable field.
The alternative would be to have the client send, in addition to the fields, a list of fields that should be updated.

f# Insert on MongoDB using Records

I've been trying for a while to insert on MongoDB using only records with no success.
My problem is that I want to create a simple insert function which I send a generic type and it is inserted into the database.
Like so.
let insert (value: 'a) =
let collection = MongoClient().GetDatabase("db").GetCollection<'a> "col"
collection.InsertOne value
From this function, I tried inserting the following records.
// Error that it can't set the Id
type t1 = {
Id: ObjectId
Text: string
}
// Creates the record perfectly but doesn't generate a new Id
type t2 = {
Id: string
Text: string
}
// Creates the record and autogenerates the Id but doesn't insert the Text, and there are two Ids (_id, Id#)
type t3 = {
mutable Id: ObjectId
Text: string
}
// Creates the record and autogenerates the Id but for every property it generates two on MongoDB (_id, Id#, Text, Text#)
type t4 = {
mutable Id: ObjectId
mutable Text: string
}
So does anyone can think of a solution for this or am I stuck having to use a class.
// Works!!!
type t5() =
member val Id = ObjectId.Empty with get, set
member val Name = "" with get, set
Also, does anyone has any Idea of why when the C# MongoDB library translates the mutable he gets the property with the # at the end?
I would be fine with having all my properties set as mutable, although this wouldn't be my first choice, having he create multiple properties on the DB is quite bad.
You could try annotating your records with CLIMutable (and no mutable fields).
The #s end up in the DB because MongoDB using reflection and F# implementing mutable with backing fields fieldName#

Golang revel+mgo - no data returned when struct variables having lowercase names

This is my struct type
type Category struct {
Name string `bson:"listName"`
Slug string `bson:"slug"`
}
used with the following function to return all results from a mongo collection -
func GetCategories(s *mgo.Session) []Category {
var results []Category
Collection(s).Find(bson.M{}).All(&results)
return results
}
The problem is that the field names in my db have names starting in lowercase but the Golang struct returns null when I try to use variable names starting with lower case. For e.g. this returns a JSON with corresponding fields empty -
type Category struct {
listName string `bson:"listName"`
slug string `bson:"slug"`
}
I'm actually porting a Meteor based API to Golang and a lot of products currently using the API rely on those field names like they are in the db!
Is there a workaround?
You need to make your fields visible for mgos bson Unmarshall by naming them with a starting capital letter. You also need to map to the appropiates json/bson field names
type Category struct {
ListName string `json:"listName" bson:"listName"`
Slug string `json:"slug" bson:"slug"`
}