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
Related
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
I have a one-to-many relationship in postgres (Event has many EventUser), and would like to scan and store into a struct Event.
// EventUser struct
type EventUser struct {
ID int64
CheckedIn bool
PaidAmount float32
}
// Event struct
type Event struct {
ID int64 `json:"id"`
Name string `json:"name"`
StartTime string `json:"startTime"`
EventUsers string
}
Here is the query:
SELECT events.id, events.name, events."startTime", e."eventUsers" as "eventUsers"
FROM "Events" as events
LEFT JOIN (
SELECT events.id as id, array_to_json(array_agg(eu.*)) as "eventUsers"
FROM "EventUsers" as eu
JOIN "Events" AS "events" ON events.id = eu."eventId"
WHERE eu.status = 'RESERVED'
GROUP BY events.id
) AS e USING (id)
WHERE events.status = 'COMPLETED'
The query returns this:
{
id: 2,
name: "2 Events are 48 days from now",
startTime: 1590471343345,
eventUsers: [
{
id: 2,
checkedIn: false,
paidAmount: 8
},
{
id: 3,
checkedIn: false,
paidAmount: 8,
},
],
};
This is what I am trying to do, which directly scan each item under eventUsers into struct, and store in the Event's struct.
got := []Event{}
for rows.Next() {
var r Event
err = rows.Scan(&r.ID, &r.Name, &r.StartTime, &r.EventUsers)
if err != nil {
panic(err)
}
}
I think I could achieve this by storing the array as a string and then unmarshal it.
What I want is something similar to sql.NullString.
You could define a slice type that implements the sql.Scanner interface. Note that when you pass an instance of a type that implements Scanner to a (*sql.Rows).Scan or (*sql.Row).Scan call, the impelmenter's Scan method will be invoked automatically.
type EventUserList []*EventUser
func (list *EventUserList) Scan(src interface{}) error {
if data, ok := src.([]byte); ok && len(data) > 0 {
if err := json.Unmarshal(data, list); err != nil {
return err
}
}
return nil
}
Then, assuming that the e."eventUsers" in the select query is a json array, you could use it like this:
// EventUser struct
type EventUser struct {
ID int64
CheckedIn bool
PaidAmount float32
}
// Event struct
type Event struct {
ID int64 `json:"id"`
Name string `json:"name"`
StartTime string `json:"startTime"`
EventUsers EventUserList `json:"eventUsers"`
}
// ...
var events []*Event
for rows.Next() {
e := new(Event)
if err := rows.Scan(&e.ID, &e.Name, &e.StartTime, &e.EventUsers); err != nil {
return err
}
events = append(events, e)
}
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 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}
I'm trying to append a new item to an array in a one-to-many relationship. The problem is that one of the IDs is always undefined and the model I want to append to does not get updated.
I have the following models:
type Station struct {
gorm.Model
Name string
Measurements []Measurement
PlantID uint64
Plant Plant
}
type Measurement struct {
ID uint64 `gorm:"primary_key"`
StationID uint64
TempSensor float32
LightSensor float32
HumiditySensor float32
CreatedAt time.Time
}
type Plant struct {
gorm.Model
Name string
}
This is the route to which I'm sending the post request:
/stations/:id/measurements
This is the current route handler that I have:
func CreateMeasurement(c *gin.Context) {
id := c.Params.ByName("id")
var station Station
if err := db.Where("id = ?", id).First(&station).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
var measurement Measurement
c.BindJSON(&measurement)
// Convert params string to uint
convertedID, err := strconv.ParseUint(id, 10, 64)
if err != nil {
fmt.Println(err)
}
measurement.StationID = convertedID
db.Model(&station).Association("Measurements").Append(&measurement)
db.Save(&station)
c.JSON(200, station)
}
}
Question: How can I create a new Measurement item and append it to the []Measurement array in a specific Station which is specified by the route parameters?
Solved the problem. It turns out there was some problem with the database table. Although I got auto migrate enable, there was a problem with some ids that were null.
Here is the working route:
func CreateMeasurement(c *gin.Context) {
id := c.Params.ByName("id")
var station Station
if err := db.Where("id = ?", id).First(&station).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
var measurement Measurement
c.BindJSON(&measurement)
db.Model(&station).Association("Measurements").Append(&measurement)
c.JSON(200, station)
}
}