Golang GraphQL MongoDB Struggling to get date and id out of the Database - mongodb

I am new to Golang and Graphql so I probably messed a lot of the configuration up but I am struggling to get values returned from my database using the GraphQL API I created. Whenever I query my database using the GraphQL API I created in Golang It throws the error cannot decode UTC datetime into a string type and struggles to get the id out.
Here is my GrapqhQL schema:
type User {
_id: ID!
username: String!
passwordHash: String!
email: String!
userInfo: userStats
profileStats: profileInfo
}
type userStats {
firstName: String
lastName: String
birthday: String
dateCreated: String!
nativeLanguage: String
currentlyLearning: String
location: Location
}
type Location {
city: String
state: String
zipcode: Int
country: String
}
type profileInfo {
level: Int
xp: Int
xpTillNextLevel: Int
posts: Int
}
input NewUser {
id: ID!
username: String!
passwordHash: String!
email: String!
userStats: String
profileInfo: String
}
type Mutation {
createUser(input: NewUser!): User!
}
type Query {
users: [User!]!
user(id: ID!): User!
}
Here is my code that executes when a query is provided:
func (u *UserRepo) GetUsers() ([]*model.User, error) {
var users []*model.User
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
usersCollection := u.DB.Collection(u.KEYS["collection"].(string))
cursor, err := usersCollection.Find(ctx, bson.M{})
if err != nil {
fmt.Println(err)
return nil, err
}
if err = cursor.All(ctx, &users); err != nil {
fmt.Println(err)
return nil, err
}
fmt.Println(users[0])
return users, nil
}
func (u *UserRepo) GetUserById(id string) (*model.User, error) {
var user model.User
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
usersCollection := u.DB.Collection(u.KEYS["collection"].(string))
userID, err := primitive.ObjectIDFromHex(id)
if err != nil {
fmt.Println("Invalid ObjectID")
}
err = usersCollection.FindOne(ctx, bson.M{"_id": userID}).Decode(&user)
if err != nil {
fmt.Println("error retrieving user userid : " + id)
fmt.Printf("error: %d", err)
//return nil, err
}
fmt.Println(err)
fmt.Println(user)
return &user, nil
}
If I uncomment the return nil,err on the bottom query for selecting one user by the id, it will just return the error of the date and no information so I am leaving it commented out for testing purposes.
my query and result
query:
query getUsers {
user(id: "5ea75c4c67f9266c89dfb659") {
_id
username
email
passwordHash
userInfo{
lastName
dateCreated
location{
state
}
}
profileStats{
level
}
}
}
result:
{
"data": {
"user": {
"_id": "",
"username": "Aerith",
"email": "Aerith#LanguageLearning.com",
"passwordHash": "testingpassword",
"userInfo": {
"lastName": "Gainsborough",
"dateCreated": "",
"location": null
},
"profileStats": null
}
}
}
and here is example dataset that I made for testing in my MongoDB database
db.users.findOne({_id: ObjectId("5ea75c4c67f9266c89dfb659")})
{
"_id" : ObjectId("5ea75c4c67f9266c89dfb659"),
"username" : "Aerith",
"passwordHash" : "testingpassword",
"email" : "Aerith#LanguageLearning.com",
"userInfo" : {
"firstName" : "Aerith",
"lastName" : "Gainsborough",
"birthday" : ISODate("1985-02-07T00:00:00Z"),
"dateCreated" : ISODate("2020-04-27T22:27:24.650Z"),
"nativeLanguage" : "English",
"currentlyLearning" : "Japanese",
"location" : {
"city" : "Sector 5",
"state" : "Midgar",
"zipcode" : 77777,
"country" : "FF7"
}
},
"profileStats" : {
"level" : 1,
"xp" : 0,
"xpTillNextLevel" : 1000,
"comments" : 0,
"posts" : 0
}
}
Also the location and profile stats are just coming back empty and null and I have no clue why.
Sorry for the long amount of code but I am trying to provide the most information possible to assist with finding the answer. Hopefully, this helps and I can get some assurance on how to fix this issue. Thank you for all your help in advance.
edit: after some testing in the userStats type I can get the firstName and lastName but it fails and the cursor crashes because of the data error when it hits birthday. This is why everything is null under birthday. So the issues is how do I decode the mongo date so I can put in the userStates. I am tempted to just pull everything as bson and convert it to correct model structs but that seems like to much extra work and I really do not want to resort to this.

Some BSON types doesn't have direct mapping with Go primitive types, so you need types with custom unmarshalling, either your own made or already done on bson/primitive package
Try defining your user stats struct that way:
import "go.mongodb.org/mongo-driver/mongo/primitive"
type UserStats {
...
BirthDay primitive.DateTime `bson:"birthday"`
//OR BirthDay primitive.Timestamp `bson:"birthday"`
...
}
https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#v1.3.3?tab=doc#hdr-Native_Go_Types
https://pkg.go.dev/go.mongodb.org/mongo-driver/bson/primitive
https://pkg.go.dev/go.mongodb.org/mongo-driver/bson/primitive?tab=doc#DateTime
https://pkg.go.dev/go.mongodb.org/mongo-driver/bson/primitive?tab=doc#Timestamp

Related

How to handle null value in golang?

User model
type UserExample struct {
Id primitive.ObjectID `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Location string `json:"location,omitempty"`
Title string `json:"title,omitempty"`
}
Update User
func UpdateUserExample() gin.HandlerFunc {
return func(c *gin.Context) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
userId := c.Param("userId")
var user models.UserExample
defer cancel()
objId, _ := primitive.ObjectIDFromHex(userId)
//Validate the request body
if err := c.BindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, responses.UserResponseExample{
Status: http.StatusBadRequest,
Message: "Error",
Data: map[string]interface{}{
"data": err.Error()},
})
}
update := bson.M{
"name": user.Name,
"location": user.Location,
"title": user.Title,
}
result, err := userCollectionExample.UpdateOne(ctx, bson.M{
"id": objId,
}, bson.M{
"$set": update,
})
if err != nil {
c.JSON(http.StatusInternalServerError, responses.UserResponseExample{
Status: http.StatusInternalServerError,
Message: "Error",
Data: map[string]interface{}{
"data": err.Error(),
}})
return
}
//Get Update UserExample Detail
var updateUser models.UserExample
if result.MatchedCount == 1 {
err := userCollectionExample.FindOne(ctx, bson.M{
"id": objId,
}).Decode(&updateUser)
if err != nil {
c.JSON(http.StatusInternalServerError, responses.UserResponseExample{
Status: http.StatusInternalServerError,
Message: "Error",
Data: map[string]interface{}{
"data": err.Error(),
}})
return
}
}
c.JSON(http.StatusOK, responses.UserResponseExample{
Status: http.StatusOK,
Message: "Success",
Data: map[string]interface{}{
"data": updateUser,
},
})
}
}
i have try update data via postman, but if value == null will be delete from collection
In this case, i want Update Title of the User, before update all data already exist
Postman
{
"title": "User One"
}
its working to change title in collection. but, for other data (name and location)has gone
"data": {
"id": "63d2ac86aeb9d78d3d5daf21",
"title": "User One",
}
so, how to handle null value from request body?
i just want change title for this case
Usually, such partial updates are handled using a structure that looks like this:
type UserUpdateRequest struct {
Id primitive.ObjectId `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Location *string `json:"location,omitempty"`
Title *string `json:"title,omitempty"`
}
Note the pointers. This way, the API caller can send non-nil values for the field it wants to update. It can also use an empty string to set the field values to empty.
Then on the database side, you have to create an update statement:
updateFields:=bson.M{}
if request.Name!=nil {
updateFields["name"]=*request.Name
}
if request.Location!=nil {
updateFields["location"]=*request.Location
}
// etc.
update:=bson.M{"$set":updateFields}
Then use the update to update the database record.

Saving StripeID to Firestore Collection Document

I'm switching data from Firebase Realtime Database to Firestore because I need more querying capabilities. However, I'm having trouble with saving my customer's stripeID to their collection document. My Cloud Function is hitting perfectly because Stripe is creating the customer correctly, but it's not assigning to the collection reference. What do I need to fix so the collection reference could recognize the stripeID as well? Thank you!
What I'm Seeing
Customer Model
struct Customer {
let uid: String
let stripeId: String
let fullname: String
let email: String
let username: String
let profileImageUrl: String
init(dictionary: [String : Any]) {
self.uid = dictionary["uid"] as? String ?? ""
self.stripeId = dictionary["stripeId"] as? String ?? ""
self.fullname = dictionary["fullname"] as? String ?? ""
self.email = dictionary["email"] as? String ?? ""
self.username = dictionary["username"] as? String ?? ""
self.profileImageUrl = dictionary["profileImageUrl"] as? String ?? ""
}
}
AuthServices
struct CustomerCredentials {
let email: String
let password: String
let fullname: String
let username: String
let profileImage: UIImage
}
static func createCustomer(credentials: CustomerCredentials, completion: CollectionCompletion) {
Auth.auth().createUser(withEmail: credentials.email, password: credentials.password) { result, error in
if let error = error {
debugPrint(error.localizedDescription)
return
}
guard let uid = result?.user.uid else { return }
let values = ["uid" : uid,
"email" : credentials.email,
"fullname" : credentials.fullname,
"username" : credentials.username,
"profileImageUrl" : profileImageUrl]
REF_CUSTOMERS.document(uid).setData(values, completion: completion)
}
}
}
RegistrationController
#objc func handleCreateAccount() {
let credentials = CustomerCredentials(email: email, password: password, fullname: fullname,
username: username, profileImage: profileImage)
AuthService.createCustomer(credentials: credentials) { error in
if let error = error {
Auth.auth().handleFireAuthError(error: error, vc: self)
self.showLoader(false)
return
}
Functions.functions().httpsCallable("createStripeCustomer").call(["email" : email]) { result, error in
if let error = error {
debugPrint(error.localizedDescription)
return
}
}
}
}
Assuming that REF_CUSTOMERS points to the "customers" collection, then when you're using the following line of code:
REF_CUSTOMERS.document(uid).setData(values, completion: completion)
For writing the following values:
let values = ["uid" : uid,
"email" : credentials.email,
"fullname" : credentials.fullname,
"username" : credentials.username,
"profileImageUrl" : profileImageUrl]
The document that will be added will always contain only the data that exists in the values object. Since there is no stripeId present, the document that will be written will not contain the stripeId. If you need to have the stripeId as a field in the user document, then you have to add it like this:
let values = ["uid" : uid,
"email" : credentials.email,
"fullname" : credentials.fullname,
"username" : credentials.username,
"profileImageUrl" : profileImageUrl
"stripeId" : stripeId] //👈
In my RegistrationController, I had to add an updateData value inside my Cloud Function code snippet in order to add it to my customer reference.
RegistrationController
Functions.functions().httpsCallable("createStripeCustomer").call(["email" : email]) { result, error in
if let error = error {
debugPrint(error.localizedDescription)
return
}
let value = ["stripeId" : result?.data]
guard let uid = Auth.auth().currentUser?.uid else { return }
REF_CUSTOMERS.document(uid).updateData(value as [AnyHashable : Any])
}

pq: malformed array literal:

I want to insert json array request like
{
"name": "Sales Commission",
"commission_type":"ZCo6QCHOBth7MD-8sohHQ",
"time_period_from": "2022-05-13 12:00:00",
"time_period_to": "2022-05-18 12:00:00",
"service_id": [
{
"service_id": "1",
"service_type": "1",
"commission_type": "1",
"commission_rate": "1.50"
}
],
"package_id": [
{
"package_id": "1",
"package_type": "1",
"commission_type": "1",
"commission_rate": "1.50"
}
],
"product_id": [
{
"product_id": "1",
"product_type": "1",
"commission_type": "1",
"commission_rate": "1.50"
}
],
"employee_id": [
{
"employee_id": "1",
"commission_type": "1",
"commission_rate": "10.50"
}
],
"status": "active"
}
I am using Golang with Postgres. I had made struct of the Model which is below:
type AutoGenerated struct {
Name string `json:"name"`
CommissionType string `json:"commission_type"`
TimePeriodFrom string `json:"time_period_from"`
TimePeriodTo string `json:"time_period_to"`
Services []Services `json:"service_id"`
Packages []Packages `json:"package_id"`
Products []Products `json:"product_id"`
Employees []Employees `json:"employee_id"`
Status string `json:"status"`
}
type Services struct {
ServiceID string `json:"service_id"`
ServiceType string `json:"service_type"`
CommissionType string `json:"commission_type"`
CommissionRate string `json:"commission_rate"`
}
type Packages struct {
PackageID string `json:"package_id"`
PackageType string `json:"package_type"`
CommissionType string `json:"commission_type"`
CommissionRate string `json:"commission_rate"`
}
type Products struct {
ProductID string `json:"product_id"`
ProductType string `json:"product_type"`
CommissionType string `json:"commission_type"`
CommissionRate string `json:"commission_rate"`
}
type Employees struct {
EmployeeID string `json:"employee_id"`
CommissionType string `json:"commission_type"`
CommissionRate string `json:"commission_rate"`
}
Request which is being decoded
var ReqMapData model.AutoGenerated
err := json.NewDecoder(r.Body).Decode(&ReqMapData)
if err != nil {
log.Println(err)
}
While performing data insertion I am facing this issue like "pq: malformed array literal: "[{"service_id":"1","service_type":"1","commission_type":"1","commission_rate":"1.50"}]"""
Code which inserts data
serJson, errJson := json.Marshal(reqMap.Services)
if errJson != nil {
util.CheckErrorLog(r, errJson)
return false
}
pkgJson, errJson1 := json.Marshal(reqMap.Packages)
if errJson1 != nil {
util.CheckErrorLog(r, errJson1)
return false
}
proJson, errJson2 := json.Marshal(reqMap.Products)
if errJson2 != nil {
util.CheckErrorLog(r, errJson2)
return false
}
empJson, errJson3 := json.Marshal(reqMap.Employees)
if errJson3 != nil {
util.CheckErrorLog(r, errJson3)
return false
}
Qry.WriteString("INSERT INTO commission_template(id, name, commission_type, time_period_from, time_period_to, service_id, package_id, product_id, employee_id, status) ")
Qry.WriteString(" VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);")
err := ExecuteNonQuery(Qry.String(), nanoid, reqMap.Name, reqMap.CommissionType, reqMap.TimePeriodFrom, reqMap.TimePeriodTo, string(serJson), string(pkgJson), string(proJson), string(empJson), reqMap.Status)
if err != nil {
util.CheckErrorLog(r, err)
return false
}
// ExecuteNonQuery - Update, Insert Query
func ExecuteNonQuery(SQLQry string, params ...interface{}) error {
_, err := VDB.Exec(SQLQry, params...)
return err
}

Golang find a value from a map of nested json data from MongoDB

I am trying to receive data from my MongoDB using MGO in a map of type []map[string]interface{}
My JSON looks like this -
{
"_id":"string",
"brandId":123,
"category":{
"television":[
{
"cat":"T1",
"subCategory":[
{
"subCat":"T1A TV",
"warrantyPeriod":6
}
],
"warrantyPeriod":12
},
{
"cat":"T2",
"subCategory":[
{
"subCat":"T2A",
"warrantyPeriod":18
},
{
"subCat":"T2B",
"warrantyPeriod":9
}
],
"warrantyPeriod":15
},
{
"cat":"T3",
"subCategory":[
{
"subCat":"T3A",
"warrantyPeriod":3
},
{
"subCat":"T3B",
"warrantyPeriod":5
},
{
"subCat":"T3C",
"warrantyPeriod":7
},
{
"subCat":"T3D",
"warrantyPeriod":11
}
],
"warrantyPeriod":4
}
],
"television_warrantyPeriod":24
},
"title":"BrandName"
}
I would ideally pass in the category name i.e. 'television' and cat and subCat values which could be optional.
For e.g. something like this -
{
"categorySlug": "television",
"brandId": "123",
"model": "T2"
}
In which case I would expect to find '15' which is the warrantyPeriod value for T2 if there are no T2A or T2B specified.
My query functions look like this -
var data map[string]string
err := json.NewDecoder(r.Body).Decode(&data)
log.Println(err)
var buffer bytes.Buffer
buffer.WriteString("category.")
buffer.WriteString(data["categorySlug"])
brandId, _ := strconv.Atoi(data["brandId"])
concernedbrandandcategory := database.GetMappedFields("Brands", bson.M{"brandId": brandId, buffer.String(): bson.M{"$exists": true}}, bson.M{buffer.String(): 1})
categorymap := concernedbrandandcategory[0]
log.Println(categorymap["category"]["television"], reflect.TypeOf(categorymap))
My GetMappedFields function looks like this -
func GetMappedFields(collectionName string, query interface{}, selector interface{}) (result []map[string]interface{}) {
MgoSession.DB(Dbname).C(collectionName).Find(query).Select(selector).All(&result)
return
}
I'm just not able to wrap my head around this nested structure which sometimes returns a map and sometimes an interface!
Any help would be highly appreciated!
you can do something like this
majorCat := body["categorySlug"]
category := body["category"]
subCategory := body["subCategory"]
brandId, err := strconv.Atoi(body["brandId"])
if err != nil {
log.Println(err)
}
result := database.GetMappedFields("Brands", bson.M{"brandId": brandId}, bson.M{"category": 1, "_id": 0})
internalObj := result[0]["category"].(map[string]interface{})
finalValue := internalObj["television_warrantyPeriod"]
if category != "" {
for _, v := range internalObj[majorCat].([]interface{}) {
subObj := v.(map[string]interface{})
if subObj["cat"] == category {
finalValue = subObj["warrantyPeriod"]
if subCategory != "" {
minorObj := subObj["subCategory"].([]interface{})
for _, iter := range minorObj {
kevVal := iter.(map[string]interface{})
if kevVal["subCat"] == subCategory {
finalValue = kevVal["warrantyPeriod"]
}
}
}
}
}
}
Hopefully this will do dynamically or you can create a struct so that it can directly be decoded into that cheers

Meteor, Mongo insert field value via function issue

I'm new to Mongo, so maybe this is silly Q, but I cant find similar problem online. When I try insert document with a field value calculated in function, field simply is not in object. Result is something like this
var token = function(){
return "xxxyyy";
}
var companyId = Companies.insert({
name: company.name,
email: company.email,
country: company.country,
city: company.city,
token: function(){
return token();
}
});
Result is this document:
{
"_id" : "fm36vAH58787fJq66",
"name" : "TestCo",
"email" : "email#email.com",
"country" : "",
"city" : ""
}
Do you really want a function in your JSON or only the result? If you want the result, try this:
var token = function(){
return "xxxyyy";
}
var companyId = Companies.insert({
name: company.name,
email: company.email,
country: company.country,
city: company.city,
token: token()
});