Get _id from mongoDB using gqlgen - mongodb

I'm using Go and gqlgen to access my mongoDB database and was wondering how do I access the id field from the database? This is what I have currently and _id returns an empty string
type Post {
_id: ID!
title: String!
content: String!
comments: [Comment!]
author: User!
created: Date!
}
type Query {
post(_id: ID!): Post
...
}
func (r *queryResolver) Post(ctx context.Context, id string) (*model.Post, error) {
var post model.Post
_id, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
err = db.Posts.FindOne(context.TODO(), bson.D{{Key: "_id", Value: _id}}).Decode(&post)
if err != nil {
return nil, err
}
return &post, nil
}

The ID type in gqlgen creates a string, but _id in mongo is probably a primitive.ObjectId which can create issues depending on how you interact with mongo.
Better to set a bson tag as _id
consider setting the following struct to overwrite the gql generated flow. You can convert the id into a string using the Hex() method.
type Post struct{
ID primitive.ObjectID `bson:"_id" json:"id"`
title: string
content: string
comments: []Comment
author: User
created: time.Time
}
You might want to do this automatically if you have many structs with _ids. To avoid having to overwrite, you can implement a hook to autogenerate the bson tags for you
type Post {
id: ID!
title: String!
content: String!
comments: [Comment!]
author: User!
created: Date!
}
now a new directory in your file structure called "hooks" and create a new file "bson.go"
copy and paste the following
package main
import (
"fmt"
"os"
"github.com/99designs/gqlgen/api"
"github.com/99designs/gqlgen/codegen/config"
"github.com/99designs/gqlgen/plugin/modelgen"
)
func mutateHook(b *modelgen.ModelBuild) *modelgen.ModelBuild {
for _, model := range b.Models {
for _, field := range model.Fields {
name := field.Name
if name == "id" {
name = "_id"
}
field.Tag += ` bson:"` + name + `"`
}
}
return b
}
func main() {
cfg, err := config.LoadConfigFromDefaultLocations()
if err != nil {
fmt.Fprintln(os.Stderr, "failed to load config", err.Error())
os.Exit(2)
}
p := modelgen.Plugin{
MutateHook: mutateHook,
}
err = api.Generate(cfg,
api.NoPlugins(),
api.AddPlugin(&p),
)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(3)
}
}
now in your main.go add this to the top
//go:generate go run hooks/bson.go
now when you run go generate not only will gqlgen do it's normal generation, but it will also add bson tags to all of our models. As well as any field with the name id to have a bson tag of _id
source : https://github.com/99designs/gqlgen/issues/865

Related

How to make a mutation for graphql that had a relation in schema

I have a schema.graphqls like this :
type User {
id:ID!
name:String!
username:String!
email:String!
password:String!
posts: [Post]
}
type Post{
id:ID!
caption:String!
userid:Int!
}
I connect this with a postgre sql database like below:
My question is how to make a mutation for create new user???
I tried to do this on graphql playground:
createUser(input:{
name:"Logan Giqualen"
username:"logichekit"
email:"logangiqualen#ymail.com"
password:"123123"
})
{
name
}
}
And this is my schema.resolvers.go :
func (r *mutationResolver) CreateUser(ctx context.Context, input model.NewUser) (*model.User, error) {
// hash, _ := HashPassword(input.Password)
var posts []*model.Post
user := model.User{
Name: input.Name,
Username: input.Username,
Email: input.Email,
Password: input.Password,
Posts: posts,
}
_, err := r.DB.Model(&user).Insert()
if err != nil {
return nil, errors.New("insert new user error")
}
return &user, nil
}
But is give an internal server error in graphql playground.
Anybody know how to do it?

scan to a struct fields that some of them are pointers

I'm writing a GO application and I'm trying to find an easy method to scan a row from the database to struct fields.
I use pgx to connect to a postgresql database
gqlgen generated this class:
type Profile struct {
Name string `json:"name"`
JoinedAt time.Time `json:"joined_at"`
Bio *string `json:"bio"`
}
and then I'm got the function to get the user profile from db:
func GetUserProfile(ctx context.Context, profileDir string) (*model.Profile, error) {
sqlQuery := `select name,joined_at::timestamptz,bio from foo where profile_dir=$1`
var profile model.Profile
if err := Connection.QueryRow(ctx, sqlQuery, profileDir).
Scan(&profile.Name, &profile.JoinedAt, &profile.Bio); err != nil {
return nil, err
} else {
return &profile, nil
}
}
now since Bio is a pointer, I need to create a variable who's not a pointer, scan to it and assign it's address after that to the struct:
var profile model.Profile
var mybio string
...
Connection.QueryRow(...).Scan(...,&mybio)
profile.Bio=&mybio
is there an easier way to scan a row to a struct that might have pointers ?
If Bio is already a pointer, you don't need to take an extra pointer in the Scan call:
profile := Profile{
Bio: new(string),
}
if err := Connection.QueryRow(ctx, sqlQuery, profileDir).
Scan(&profile.Name, &profile.JoinedAt, profile.Bio); err != nil {
return nil, err
} else {
return &profile, nil
}

Projections in Mongodb not working properly

I have this data in my mongoDB database.
{
"_id":"5d9ce9fd270eae22adb95d70",
...
"isdriver":true,
"driver":{
"walletmoney":0,
"license":"6eef8271-62d7-4a1c-972a-2c40a773b35a",
"vehicle":{
"image":"b6c3619b-86e6-49d0-8734-e2c48815dfc1",
"insurance":"5f8229c4-4700-4059-8b72-9344a2bc6092",
"manufacturer":"Tesla",
"model":"Model 3",
"vin":"12345678912345678",
"year":2018
},
"verified":false
...
}
}
Here is my driver struct
type Driver struct {
...
Verified bool `json:"verified,omitempty"`
License string `json:"licenseimage,omitempty"`
...
Vehicle Vehicle `json:"vehicle,omitempty"`
}
Here is my Student Struct
type Student struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
...
IsDriver bool `json:"isdriver,omitempty"`
Driver Driver `json:"driver,omitempty"`
}
Vehicle struct
type Vehicle struct {
Image string `json:"vehicleimage,omitempty"`
Insurance string `json:"insuranceimage,omitempty"`
VIN string `json:"vin,omitempty"`
Manufacturer string `json:"manufacturer,omitemptyr"` <-----(Edit) Find out this is also wrong
Model string `json:"model,omitempty"`
Year uint16 `json:"year,omitempty"`
}
And I'm using this function to get all the drivers from the database
func GetAllDrivers() []model.Driver {
// Options
projections := bson.D{
{"driver", 1},
/* {"driver.verified", 1},
{"driver.license", 1}, */
}
// Filter for search
filter := bson.M{"isdriver": true}
// Return student collection (*mongo.Collection)
studentCollection := GetStudentCollection()
cur, err := studentCollection.Find(context.TODO(), filter, options.Find().SetProjection(projections))
// Error while finding documents
if err != nil {
fmt.Print(err)
return []model.Driver{}
}
var drivers []model.Driver
var driver model.Driver
// Get the next result from the cursor
for cur.Next(context.TODO()) {
err := cur.Decode(&driver)
if err != nil {
fmt.Print(err)
}
drivers = append(drivers, driver)
}
if err := cur.Err(); err != nil {
fmt.Print(err)
}
cur.Close(context.TODO())
return drivers
}
But the response I'm getting in the postman is ridiculous
[
{
"vehicle": {
"manufacturer": ""
}
},
{
"vehicle": {
"manufacturer": ""
}
}
]
One thing is okay that is in response I'm getting two objects which are fine because as my filter suggestest isdriver: true I have total three documents in the database in which two of those have isdriver: true.
Can anybody help me with this? Why I'm getting this response?
You are doing a find in the students collection, but you decode into a driver.
This needs to be changed.
var drivers []Driver
var student Student
// Get the next result from the cursor
for cur.Next(context.TODO()) {
err := cur.Decode(&student)
if err != nil {
fmt.Println(err)
}
drivers = append(drivers, student.Driver)
}
Furthermore , you are lacking an Inline struct tag for the Driver field of Student:
type Student struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
IsDriver bool `json:"isdriver,omitempty"`
// Note that Inline is uppercase.
Driver Driver `json:"driver,omitempty" bson:"driver,Inline"`
}
Same, of course, goes for all referenced structs. Working sample code: https://gist.github.com/mwmahlberg/c46ec3ad3ccee028f0666ff7d5d8d98b

GORM database colum with json data

I am trying to write an email service, where I want to store some data related to email into a Postgres DB using gorm. I have a field which needs to be stored as a JSON blob, which in request passed as a JSON object. When I am trying to migrate, it errors keep saying unsupported type map. When manually add the DB, then run gorm, it doesn't write the row to the database.
I have seen some example where it's using postgres.Json as field types, but I want the field loaded from the request as map[string]string.
// Email : Base with injected fields `UUID`, `CreatedAt`, `UpdatedAt`
type Email struct {
gorm.Model
Status string `grom:"type:varchar(32);not null"`
TemplateID string `grom:"type:varchar(256)"`
ToEmai string `grom:"type:varchar(256);not null"`
FromEmail string `grom:"type:varchar(256);not null"`
ToName string `grom:"type:varchar(256);not null"`
FromName string `grom:"type:varchar(256);not null"`
Subject string `grom:"type:varchar(256)"`
Message string `grom:"type:varchar"`
DynamicData *map[string]string `grom:"type:json"`
}
this is my model.
then I do a gin request:
// SendEmail : sends an email
func SendEmail(c *gin.Context) {
body, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
log.Error("Error reading request body to get rate estimates")
}
var newEmail = models.Email{
Status: "PENDING",
}
jsonErr := json.Unmarshal(body, &newEmail)
if jsonErr != nil {
log.Error(jsonErr)
}
database.DB.Create(&newEmail)
defer c.Request.Body.Close()
err = newEmail.SendSendgridEmail()
if err != nil {
c.JSON(http.StatusBadRequest, err)
} else {
c.JSON(http.StatusOK, "Successfully sent email")
}
}
which then looks into this function
func (e Email) dynamicTemplateEmailBody() []byte {
newMail := mail.NewV3Mail()
emailFrom := mail.NewEmail(e.FromName, e.FromEmail)
newMail.SetFrom(emailFrom)
newMail.SetTemplateID(e.TemplateID)
p := mail.NewPersonalization()
tos := []*mail.Email{
mail.NewEmail(e.ToName, e.ToEmai),
}
p.AddTos(tos...)
if e.DynamicData != nil {
for key, value := range *e.DynamicData {
log.Infof("%s %s", key, value)
p.SetDynamicTemplateData(key, value)
}
}
newMail.AddPersonalizations(p)
return mail.GetRequestBody(newMail)
}
I would like to be able to run DB.AutoMigrate(&models.Email{}) and automatically migrate the objects, or and when I make a request to the endpoint, the row gets added to my email table.

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}