go mongodb driver and struct, find messes with uppercase and lowercase - mongodb

var Messages []Token
c2 := session.DB("mydatabase").C("pages")
query2 := c2.Find(bson.M{}).All(&Messages)
fmt.Print(Messages)
Here's the structure in my Mongo DB:
id_
pageUrl
token
pageId
I first tried the structure as this:
type Token struct {
PageUrl string
Token string
PageId string
}
but only the token was being printed, perhaps because it's all lowercase. The other two fields were not being retrieved because they contain uppercase. Then I tried this:
type Token struct {
PageUrl string `json: "pageUrl" bson: "pageUrl"`
Token string `json: "token" bson: "token"`
PageId string `json: "pageId" bson: "pageId"`
}
what are those bson and json things? I've only put it there because I've seen in the internet, but it doesn't work, I still get only the token field
UPDATE with solution and tested example for nested documents
I've seen that there was no posts regarding this question so remember that the solution was to remove the spaces between json: and bson:
Also, to help someone who might be wondering how to do it for nested structs, here I give two structures that worked for me:
type Token struct {
PageUrl string `json:"pageUrl" bson:"pageUrl"`
Token string `json:"token" bson:"token"`
PageId string `json:"pageId" bson:"pageId"`
}
type Message struct {
Sender struct {
Id string `json:"id" bson:"id"`
} `json:"sender" bson:"sender"`
Recipient struct {
Id string `json:"id" bson:"id"`
} `json:"recipient" bson:"recipient"`
Message struct {
Mid string `json:"mid" bson:"mid"`
Seq int `json:"seq" bson:"seq"`
Message string `json:"text" bson:"text"`
}
}

these json and bson stuff is called tags
My best guess is that because Go requires a variable or function to be public by Capitalize the first character, so serialize frameworks like json or bson require the struct Capitalize its first character to expose the field(so that it could see the field). Thus the exposed field name should be defined with a tag (to avoid the restriction).
the space between bson: and "token" seems to have cause the problem
I tried following code snippet and seems works fine.
type Token struct {
PageUrl string `json:"pageUrl" bson:"pageUrl"`
Token string `json:"token" bson:"token"`
PageId string `json:"pageId" bson:"pageId"`
}

Related

Go not interpreting http request correctly

I have a mongoDb instance connected and my request (from my React app) gets a 200 back. I can see the collection in Mondodb as "cats" with a record for each cat but the cat records looks like:
_id:5f66641bad25efa40b810d83 - fine
name:"" - should not be blank
favorite:false -should be true
I had had some cors problems at first so I changed what was "Content-Type" to "content-type" (for Access-Control-Allow-Headers) and that "fixed" the cors issue. I have no idea if that is what is breaking the code though.
Since this is my first time setting up a database on my own I am just not sure where I have broken the code. Any help would be appreciated.
Here is my CreateCat method:
func CreateCat(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Context-Type", "application/json; charset=utf-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST")
w.Header().Set("Access-Control-Allow-Headers", "content-type")
var cat models.Cat
_ = json.NewDecoder(r.Body).Decode(&cat)
fmt.Println(cat)
insertOneFavoriteCat(cat)
json.NewEncoder(w).Encode(cat)
}
Here is my model:
type Cat struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
Name string `json:"name,string"`
Favorite bool `json:"favorite,string"`
}
This is what println prints: { false}
and if you are curious if it's my frontend, this is the request getting sent:
{"cat":{"name":"tabby","favorite":true}}
Problem is that JSON MongoDB returns doesn't match how your struct would be decoded. That false comes from Favorite field which cannot be found in JSON object with cat key returned by MongoDB. To decode this type of response you need to define a wrapping struct (as CatModel in my example below) because your Cat is not top level object but just a member key of upper level JSON object:
type CatModel struct {
Cat struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
Name string `json:"name"`
Favorite bool `json:"favorite"`
} `json:"cat"`
}
Also you have typo (Context-Type instead of Content-Type) in header name. Correct Header().Set() is:
w.Header().Set("Content-Type", "application/json; charset=utf-8")

Ambiguous column reference when joining

Structs
type Client struct {
Id int64
Name string
}
type Trade struct {
Id int64
ClientId int64
Client *Client
}
Query
db.Model(&Trade).Where("id = ", tradeId).Relation("Client").Select()
Error encountered: Column Id ambiguous. Not sure what's the proper way to work around this
Would be great if someone can help
You may try qualifying the Id column with either an alias or the full table name, e.g.
db.Model(&Trade).Where(`"Trade".id = ?`, tradeId).Relation("Client").Select()

Losing underscores in fields name when inserting in mongo with mgo [duplicate]

Some JSON data I am getting have spaces in the key names. I am using standard encoding/json library to unmarshal the data. However it is unable to understand the keys with spaces in the schema. For e.g. following code:
package main
import (
"encoding/json"
"fmt"
)
func main() {
var jsonBlob = []byte(`[
{"Na me": "Platypus", "Order": "Monotremata"},
{"Na me": "Quoll", "Order": "Dasyuromorphia"}
]`)
type Animal struct {
Name string `json: "Na me"`
Order string `json: "Order,omitempty"`
}
var animals []Animal
err := json.Unmarshal(jsonBlob, &animals)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v", animals)
}
Gives the output as:
[{Name: Order:Monotremata} {Name: Order:Dasyuromorphia}]
So in the schema the library removes the space(from Na me) and try to find the key (Name), which is obviously not present. Any suggestion what can I do here?
Your json tag specification is incorrect, that's why the encoding/json library defaults to the field name which is Name. But since there is no JSON field with "Name" key, Animal.Name will remain its zero value (which is the empty string "").
Unmarshaling Order will still work, because the json package will use the field name if json tag specification is missing (tries with both lower and upper-case). Since the field name is identical to the JSON key, it works without extra JSON tag mapping.
You can't have a space in the tag specification after the colon and before the quotation mark:
type Animal struct {
Name string `json:"Na me"`
Order string `json:"Order,omitempty"`
}
With this simple change, it works (try it on the Go Playground):
[{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]

Anonymous structs return empty field value

type (
Id struct {
// I previously forgot to add the `ID` field in my question, it is present in my code but not on this question as #icza pointed it out to me
ID bson.ObjectId `json:"id" bson:"_id"`
}
User struct {
// When using anonymous struct, upon returning the collection, each of the document will have an empty `id` field, `id: ""`
Id
Email string `json:"email" bson:"email"`
...
}
// This works
User2 struct {
ID bson.ObjectId `json:"id" bson:"_id"`
Email string `json:"email" bson:"email"`
}
)
I might not have fully understood the concept of anonymous structs yet. In the example above, when querying all users from a collection, the id field is going to be an empty string "". However, if I directly define the ID field in the User struct, the id shows up fine. Is this not what anonymous structs are for? Basically extending struct so you won't have to type them again and again?
More example:
type SoftDelete struct {
CreatedAt time.Time `json:"created_at" bson:"created_at"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
DeletedAt time.Time `json:"deleted_at" bson:"deleted_at"`
}
type UserModel struct {
SoftDelete
}
type BlogPost struct {
SoftDelete
}
The problem here is that fields having struct types (including embedded structs) appear as embedded documents in MongoDB. If you don't want this, you have to specify the "inline" bson flag when embedding a struct:
User struct {
Id `bson:",inline"`
Email string `json:"email" bson:"email"`
}
What the "inline" tag does is in MongoDB it "flattens" the fields of the embedded struct as if they were part of the embedder struct.
Similarly:
type UserModel struct {
SoftDelete `bson:",inline"`
}
type BlogPost struct {
SoftDelete `bson:",inline"`
}
Edit: the following section applies to the original Id type which embedded bson.ObjectId. The asker later clarified that this was just a typo (and edited the question since), and it is a named, non-anonymous field. Still think the info below is useful.
One thing to note about your Id type: Your Id type also embeds bson.ObjectId:
Id struct {
bson.ObjectId `json:"id" bson:"_id"`
}
Id not just has a field of bson.ObjectId, but it embeds it. This matters because this way you Id type will have a String() method promoted from bson.ObjectId, and so will User which embeds Id. Having said that, it will be hard trying to print or debug values of type User, because you will always see it printed just as a single ObjectId.
Instead you can make it a regular field in Id, embedding Id in User will still work as expected:
Id struct {
ID bson.ObjectId `json:"id" bson:"_id"`
}
See related question+asnwer: Enforce a type mapping with mgo

how to get distinct values in mongodb using golang

I tried to retrieve a document from my collection with unique id.
I have a collection with fields: name, age, city, and rank. I want to get 'city' results from mongodb using golang.
My struct code
type exp struct {
name string `bson:"name"`
age int `bson:"age"`
city string `bson:"city"`
rank int `bson:"rank"`
}
With the following code to retrieve results from mongodb:
var result []exp //my struct type
err = coll.Find(bson.M{"City":bson.M{}}).Distinct("City",&result)
fmt.Println(result)
With this code I get an empty array as the result. How would I get all the cities?
Try this code
var result []string
err = c.Find(nil).Distinct("city", &result)
if err != nil {
log.Fatal(err)
}
fmt.Println(result)
Due to restrictions in reflection, mgo (as well as encoding/json and other similar packages) is unable to use unexported fields to marshal or unmarshal data. What you need to do is to export your fields by capitalize the first letter:
type exp struct {
Name string `bson:"name"`
Age int `bson:"age"`
City string `bson:"city"`
Rank int `bson:"rank"`
}
A side note: you do not need to specify the bson tags if the desired name is the same as the lowercase field name. The documentation for bson states:
The lowercased field name is used as the key for each exported field,
but this behavior may be changed using the respective field tag.
Edit:
I just realized you did get an empty slice and not a slice with empty struct fields. My answer is then not an actual answer to the question, but it is still an issue that you need to consider.