How to add subdocument as reference for user profile

I have two collection here users(which is Meteor Accounts system) and userProfile(subducument for Meteor users)
I want my userProfile will be reference for Meteor users collection, I have my following code …

Meteor.methods({
  'users.insert'(userData, userProfile) {
    // Validation for Users Data Entry
    check(userData, {
      email: String,
      password: String,
      profile: {
        firstName: String,
        lastName: String,
        userType: String,
        address: String,
      }
    });
    check(userProfile,{
      firstName: String,
      lastName: String,
      userType: String,
      address: String,
    });

    try{
      var getProfile = UserProfiles.insert({
        firstName: userProfile.firstName,
        lastName: userProfile.lastName,
        userType: userProfile.userType,
        address: userProfile.address,
        createdAt: new Date(),
      });

      Accounts.createUser({
        email: userData.email,
        password: userData.password,
        profile: getProfile,
      });
    } catch(error) {
      throw new Meteor.Error('error', error.error);
    }
  },
});

I would like to insert (on the server) a user profile document with the current user, as a reference (not as an embedded document).
example:
what I am expecting for my users is

{
  "_id" : "jzyNQJqXuHneNgqCY",
  "createdAt" : ISODate("2018-12-13T02:11:39.623Z"),
  "services" : {
      "password" : {
          "bcrypt" : "$2b$10$DXCxgtcvuCNxq835OC0Z7uc1s7N0e4vnE6fx9tLoi1T7Ll9V7Eyoi"
      }
  },
  "emails" : [ 
      {
          "address" : "cc@gmail.com",
          "verified" : false
      }
  ],
  "profile" : {
      "id": "uPneJEb3abETkizHE",
      "firstName" : "cc",
      "lastName" : "cc",
      "userType" : "admin",
      "address" : "cc"
  }
}

I searched many times in google but there is no good answer for me … Anyone can help me here ?

I did the same thing, made a separate collection called UserMeta.

I bound the data to Meteor.user().username.

So in your UserProfiles.insert you’ll want to inject that record. Now you can find:

Meteor.user().username

and

UserProfiles.findOne({username:Meteor.user().username});

Just make sure if your user ever changes their username, to update both collections!

This works… Slightly better might be be to use a field called userId that is set to the _id of the user. With both of these you will need to make sure you add the appropriate indexes so you’re not doing full table scans when looking up a profile. My preferred method though is to just specify the _id when inserting the profile documents as the _id of the user document. That way both will have the same _id and there’s no special indexing required when looking up the users profile.

2 Likes

hi @SkyRooms, how did you bound it ? what do you mean is this ?

try{
      UserProfiles.insert({
        username: userProfile.username,
        firstName: userProfile.firstName,
        lastName: userProfile.lastName,
        userType: userProfile.userType,
        address: userProfile.address,
        createdAt: new Date(),
      });

      var getProfile = UserProfiles.findOne({username:Meteor.user().username});

      Accounts.createUser({
        email: userData.email,
        password: userData.password,
        profile: getProfile,
      });
    } catch(error) {
      throw new Meteor.Error('error', error.error);
    }

mmmm yeah good point. Oh, actually I bound them both.

@SkyRooms how did you bound them both ?

So what’s actually wrong with the implementation in the original post?

The example you gave for what you expect looks exactly like an embedded document, rather than a reference

1 Like

hi @coagmano, my goal is to insert user profile with both collection, the users collection have a subdocument from another collection called userProfile. All I want is when I insert document it will have a subdocument from userProfile with its Id …

sorry guys I am new to meteorjs please guide for the best practices

The usual way to deal with this is to take the id of one document and add it to another document as a reference (or foreign key) exactly as you have done.

Then when you want to get the data from the database, you need to do the fetching / joining yourself:

// Example method implementation so we can access the current user
Meteor.methods({
  example: function () {
    const user = Meteor.users.findOne(this.userId);
    const profile = UserProfiles.findOne(user.profile);

    // Merge the two together into one document:
    return { ...user, profile };
  }
});

Although normally you wouldn’t merge them together, just use what you need where you need it

Hey man, do NOT say sorry. I have been learning and evaluating Meteor for 3+ years. It is… complex. So, welcome!!! This community here on the forums has historically been amazing. You are in good heands with @copleykj

Listen wisely

I get sir Im trying that right now

I solved it thankyou guys

import { UserProfiles } from './userProfiles.js';

Meteor.methods({
  'users.insert': function(userData) {
    check(userData, {
      email: String,
      password: String,
      profile: {
        userName: String,
        firstName: String,
        lastName: String,
        userType: String,
        address: String,
      }
    });
    check(userProfile,{
      userName: String,
      firstName: String,
      lastName: String,
      userType: String,
      address: String,
    });

    try {
      return UserProfiles.insert({
        firstName: userData.userProfile.firstName,
        lastName: userData.userProfile.lastName,
        address: userData.userProfile.address,
        userType: 'user',
        createdAt: new Date(),
      }, function(error, response) {
        var userProfile = UserProfiles.findOne(response);

        Accounts.createUser({
          email: userData.emailAddress,
          password: userData.password,
          username: userData.username,
          profile: userProfile,
        });
      });
    } catch(error) {
      throw new Meteor.Error('error', error.error);
    }
  },
});

I finally insert user profile in both collection the Id of the userProfile collection is the same in my users collection
in my users collection

{
    "_id" : "eTCNEmT8erWM9dC8Y",
    "createdAt" : ISODate("2018-12-13T06:12:17.500Z"),
    "services" : {
        "password" : {
            "bcrypt" : "$2b$10$pAJAyCCpou3wTEEebprFfuR6p28bHNNEbxcNbvUFIWF5FAUvbr72."
        }
    },
    "username" : "test",
    "emails" : [ 
        {
            "address" : "test@email.com",
            "verified" : false
        }
    ],
    "profile" : {
        "_id" : "6ioWvTEA6ru7oBJFY",
        "firstName" : "test",
        "lastName" : "test",
        "address" : "address",
        "userType" : "user",
        "createdAt" : ISODate("2018-12-13T06:12:17.316Z")
    }
}

and in my userProfile collection

{
    "_id" : "6ioWvTEA6ru7oBJFY",
    "firstName" : "test",
    "lastName" : "test",
    "address" : "address",
    "userType" : "user",
    "createdAt" : ISODate("2018-12-13T06:12:17.316Z")
}

Why do you want the userProfile in two places though?
If you’re embedding it in the Users collection, you could add it directly after creating a user?