Your Question

Hello !

I have been experimenting with your library for quite a while now, but it seems that I do not actually understand how this library works.

I have a rather complex configuration for my Postgres database. (see below)

Configuration:

type ProfileData struct {
    gorm.Model

    ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
    RegistrationFormID *string

    Username *string `json:"username"`
    Password *string `json:"password"`
}

func (ProfileData) TableName() string {
    return "profile_data"
}

func (i *ProfileData) BeforeCreate(db *gorm.DB) error {
    i.ID = uuid.New()

    return nil
}

type Address struct {
    gorm.Model

    ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
    RegistrationFormID *string

    StateProvidence  *string `json:"state_providence"`
    Country          *string `json:"country"`
}

func (Address) TableName() string {
    return "address"
}

func (i *Address) BeforeCreate(db *gorm.DB) error {
    i.ID = uuid.New()

    return nil
}

type EmergencyContact struct {
    gorm.Model

    ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
    RegistrationFormID *string

    Phone                *Phone  `json:"phone" gorm:"foreignKey:EmergencyContactID"`
    PersonalRelationship *string `json:"personal_relationship"`
    FullName             *string `json:"full_name"`
}

func (EmergencyContact) TableName() string {
    return "emergency_contact"
}

func (i *EmergencyContact) BeforeCreate(db *gorm.DB) error {
    i.ID = uuid.New()

    return nil
}

type Country struct {
    gorm.Model

    ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
    RegistrationFormID *string

    Name     *string `json:"name"`
}

func (Country) TableName() string {
    return "country"
}

func (i *Country) BeforeCreate(db *gorm.DB) error {
    i.ID = uuid.New()

    return nil
}

type Phone struct {
    gorm.Model

    ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
    ContactInfoID      *string
    EmergencyContactID *string

    Number             *string  `json:"number"`
    Country            *Country `json:"country" gorm:"foreignKey:RegistrationFormID;references:ID"`
}

func (Phone) TableName() string {
    return "phone"
}

func (i *Phone) BeforeCreate(db *gorm.DB) error {
    i.ID = uuid.New()

    return nil
}

type ContactInfo struct {
    gorm.Model

    ID                 uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
    RegistrationFormID *string

    Email             *string `json:"email"`
    Phone             *Phone  `json:"phone" gorm:"foreignKey:ContactInfoID"`
}

func (ContactInfo) TableName() string {
    return "contact_info"
}

func (i *ContactInfo) BeforeCreate(db *gorm.DB) error {
    i.ID = uuid.New()

    return nil
}

type UserInfo struct {
    gorm.Model

    ID                 uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
    RegistrationFormID *string

    FirstName *string `json:"first_name"`
    LastName  *string `json:"last_name"`
}

func (UserInfo) TableName() string {
    return "user_info"
}

func (i *UserInfo) BeforeCreate(db *gorm.DB) error {
    i.ID = uuid.New()

    return nil
}

type RegistrationForm struct {
    gorm.Model

    ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`

    UserInfo          *UserInfo           `json:"user_info" gorm:"foreignKey:RegistrationFormID;references:ID"`
    ContactInfo       *ContactInfo        `json:"contact_info" gorm:"foreignKey:RegistrationFormID;references:ID"`
    EmergencyContacts []*EmergencyContact `json:"emergency_contacts" gorm:"foreignKey:RegistrationFormID;references:ID"`
    Addresses         []*Address          `json:"addresses" gorm:"foreignKey:RegistrationFormID;references:ID"`
    ProfileData       *ProfileData        `json:"profile_data" gorm:"foreignKey:RegistrationFormID;references:ID"`
}

func (RegistrationForm) TableName() string {
    return "registration_form"
}

func (i *RegistrationForm) BeforeCreate(db *gorm.DB) error {
    i.ID = uuid.New()

    return nil
}

In a nutshell, a form is filled, and a json is received as an input. Let's assume that everything is a string for simplicity since variables types are not part of the problem., and the models are also stripped down so that it can be more readable.

As you can see, many models are part of other models fields, with arrays too.

Migration works fine.

Upon creating a record from a sample JSON (see below), everything works as expected.

Function used: db.Create(&registrationForm)

sample.json

{
  "user_info": {
    "first_name": "dfds",
    "last_name": "dsf"
  },
  "contact_info": {
    "email": "sdfsdf@gmail.com",
    "phone": {
      "number": "+12 123124",
      "country": {
        "name": "raddd"
      }
    }
  },
  "emergency_contacts": [
    {
      "phone": {
        "number": "698 888 8888",
        "country": {
          "name": "random"
        }
      },
      "personal_relationship": "Parent",
      "full_name": "qweqwe"
    },
    {
      "phone": {
        "number": "698 877 8877",
        "country": {
          "name": "asd"
        }
      },
      "personal_relationship": "Parent",
      "full_name": "wqeqweqwe"
    }
  ],
  "addresses": [
    {
      "state_providence": "weqwe",
      "country": "eweq"
    }
  ],
  "profile_data": {
    "username": "",
    "password": ""
  }
}

The next step is to query the database based on email.

First of all, we have to Preload everything, so that the it can return the proper data.

Preloading:

    tx := db.Preload(
        "UserInfo." + clause.Associations,
    ).Preload(
        "ContactInfo." + clause.Associations,
    ).Preload(
        "EmergencyContacts." + clause.Associations,
    ).Preload(
        "ProfileData." + clause.Associations,
    ).Preload(
        "Addresses." + clause.Associations)

After that, I use tx to query the database.

Query: tx.Take(&queryResult, registrationForm)

This query returns a random entry from the database even if the 'registrationForm' value is empty, or even if it is just a random JSON.

Despite that, the entry returns properly as nested struct that is properly mapped to every table.

Question

Is the registrationForm variable supposed to be like this, in order to query the DB by email ?

{
  "contact_info": {
    "email": "asdsadasd@gmail.com"
  }
}

And if that's the proper way, why is it returning something random every time ?

It is very frustrating so far, because I've tried almost everything, but with that kind of complexity I'm kinda lost right now.

The document you expected this should be explained

https://gorm.io/docs/advanced_query.html

Expected answer

How to query the database with that kind of complexity, and if this kind of database structure is actually right. One thing to take in consideration is that the input JSON upon registration cannot change. Moreover, the same information needs to be available for querying by a ReadByEmail CRUD function.

Do you think that a simpler model and a mapping to the incoming JSON is a more suitable way for my case ?

Thank you in advance.