0

I am trying to figure out how to structure my mongoose models. I have a User model, a Task model, and Project model. Projects will have users, and each user will have tasks for that specific project. The way I have this set up is the User model has a schema reference to Project model and the Task model has a scheme reference to a user. How can I do this so that when I render the information retrieved, each project will show its relevant members and each members will have their relevant tasks for that particular project. I also have an admin property of the User model which is just a boolean set default to false. The purpose of this is so that when a user created a team, the Admin property will be set to True allowing the admin to set tasks for users in the project created by admin. The problem with this is, after a team create by the user, if the admin is set to true, the ternary condition on my front end that enables a form input to show based on the boolean value of the 'admin' property will show up for all projects, even for ones the user is not an admin of.

I am using React for the rendering.

//Tasks Models

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var TodoSchema = new Schema({
  task: {
    type: String
  },
  userTasks: [{
    type: Schema.Types.ObjectId,
    ref: "User"
  }]
});

var Task = mongoose.model('Task', TodoSchema);

module.exports = Task;

//Users model

var mongoose = require("mongoose");

var Schema = mongoose.Schema;

var UserSchema = new Schema({
  name: {
    type: String,
    trim: true,
    required: "First Name is Required"
  },
  username: {
    type: String,
    trim: true,
    required: "Username is Required"
  },
  skills: {
    type: String,
    trim : true
  },
  avatar: {
    type: String
  },
  SQLid: {
    type: Number
  },
  userCreated: {
    type: Date,
    default: Date.now
  },
  lastUpdated: { 
    type: Date
  },
  userAdmin: {
    default: false
  },
  adminTeams: [{
    type: Schema.Types.ObjectId,
    ref: "Team"
  }],
  team: [{
    type: Schema.Types.ObjectId,
    ref: "Team"
  }],
  task: [{
    type: Schema.Types.ObjectId,
    ref: "Task"
  }]
});

var User = mongoose.model("User", UserSchema);

// Export the model so the server can use it
module.exports = User;

//Projects model

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var TeamSchema = new Schema({


   teamname: {
        type: String,
        trim: true,
        required: "Team Name is Required"
    },
   description: {
      type: String
   },
   tech: {
      type: String
   },
    teamMembers: [{
        type: Schema.Types.ObjectId,
        ref: "User"
    }]


});

var Team = mongoose.model('Team', TeamSchema);

module.exports = Team;

Once a User creates a team/project, they become an admin of that created team. Admins have the authority to assign task to Users, and Users can belong to many teams. I am thinking about moving the admin boolean to the Projects/Team model and giving that property the _id of the User once they create a team and then Ill use those as keys to match and use a ternary to render a form if the project they are viewing is one they created. However, I am still confused on how I can associate each user with a task, and users can belong to many teams, so I need the tasks that users have to be in the correct Project/Team section.

A lay of what I am talking about

//Projects Page (the div below is just one project out of many listed on the projects page

<div>
Project/Team 1

  User Name -> User Task
  User Name -> User Task
  User Name -> User Task
  ...
</div>

<div>
Project/Team 2

  User Name -> User Task
  User Name -> User Task
  User Name -> User Task
  ...
</div>
henhen
  • 1,038
  • 3
  • 18
  • 36

1 Answers1

0

In general you should review the mongodb documentation on associations

When you're dealing with mongodb you may want to look into holding more associated information on your documents then you would in an sql environment. Theres a good top level answer here about designing your data to use mongodb well.

Your admin problem sounds like a coding issue that will not be solved through your db. I'm hard pressed to see why your form would need a ternary at all if it's displaying on a boolean field, but can't provide any further help then:

if (user.admin) {//form}

without snippets or information on your front end architecture or the ternary in question . It sounds like you may be overcomplicating things for yourself.

edit for your update: I would change your model so projects store the user who created them.

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var TeamSchema = new Schema({


   teamname: {
        type: String,
        trim: true,
        required: "Team Name is Required"
    },
   description: {
      type: String
   },
   tech: {
      type: String
   },
    teamMembers: [{
        type: Schema.Types.ObjectId,
        ref: "User"
    }],
   // now holds the 'admin' of the team. you can keep the admin naming 
   //if you like
   creator: [{
        type: Schema.Types.ObjectId,
        ref: "User"
    }]

});

var Team = mongoose.model('Team', TeamSchema);

module.exports = Team;

also your user model could use some naming improvements, also an admin field on the user column is often used for application admins like yourself, as opposed to an admin inside a project.

var mongoose = require("mongoose");

var Schema = mongoose.Schema;

var UserSchema = new Schema({
  name: {
    type: String,
    trim: true,
    required: "First Name is Required"
  },
  username: {
    type: String,
    trim: true,
    required: "Username is Required"
  },
  skills: {
    type: String,
    trim : true
  },
  avatar: {
    type: String
  },
  SQLid: {
    type: Number
  },
  // you might want to look up timestamps createdAt and updatedAt 
  // instead of this field 
  userCreated: {
    type: Date,
    default: Date.now
  },
  lastUpdated: { 
    type: Date
  },
  userAdmin: {
    default: false
  },
  // this could be removed as its is now stored on the Team schema
  adminTeams: [{
    type: Schema.Types.ObjectId,
    ref: "Team"
  }],
  // this should be plural as it represents a one to many 
  teams: [{
    type: Schema.Types.ObjectId,
    ref: "Team"
  }],
   // tasks is removed because you can find them from the Task schema
});

var User = mongoose.model("User", UserSchema);

// Export the model so the server can use it
module.exports = User;

Since your task model needs to be team specific it can look like this:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var TodoSchema = new Schema({
  task: {
    type: String
  },
  // I would suggest an 'assignedTo', 'owner', or 'taskOwner' naming
  // as user may be a bit non-descriptive.
  user: [{
    type: Schema.Types.ObjectId,
    ref: "User"
  }],
  team: [{
    type: Schema.Types.ObjectId,
    ref: "Team"
  }]
});

var Task = mongoose.model('Task', TodoSchema);

module.exports = Task;

There are a lot of ways to load the information you want with the schema, but given this slight restructuring you could get all the information you need like this:

    var tasks;
    Team.findOne({'id': the_team_id}, function(err, team) {
      if (err) return handleError(err)
      Task.find({'team': team}).populate('user').exec(function(err, foundTasks) {
          tasks = foundTasks
        })
    })

You can now check your admin by doing something along this line:

team.creator.id === user.id 

mogoose populate

Community
  • 1
  • 1
Datise
  • 3,683
  • 2
  • 11
  • 12
  • Thank you for the response. The reason I am doing a ternary is because I am using react to render dom elements depending on whether the user is an admin for the particular group. The Users are in a project and each project has users. Each users has a task. I will edit my post with the model schemas. – henhen Mar 28 '17 at 06:29
  • include your react page as well if you can – Datise Mar 28 '17 at 06:43
  • I updated my answer for some comments on your schema that might help you out a bit and how you would get a Teams users and tasks by team in one go – Datise Mar 28 '17 at 07:12
  • Okay, will the task model which includes a reference to a user and a team, will the result of that query correctly associate each Users with the correct task assigned to them and the correct project/team its for? So when an admin assigns a task to a specific user, I guess I would do findOneAndUpdate with the team members _id I am assigning a task to and then when it is found, $push the team _id into the array of team schemas and user's _id into the array of user's schema – henhen Mar 28 '17 at 07:42
  • You can adjust the schema as necessary for the kind of queries you are hoping to do, the important takeaway here is desgining around the .populate function. In the first query example, Tasks won't have their relevent user unless you use the .populate('user') call but they will be for the correct project/team. What you could instead is var tasks; Team.findById(the_team_id).exec(function(err, team) { if (err) return handleError(err) Task.find({'team': team}).populate('user').exec( function(err, foundTasks) { tasks = foundTasks }) }). – Datise Mar 28 '17 at 19:59
  • For the admin assigning question, Under edit 2: you would create the task, update the task with the user, and update the team with the task. Under the first set of schemas, all you would do is update the Task.user to fit the user being assigned. The take away here for edit 1 is that filling up information on one document COULD limit the number of ways you can query but is probably easier to update and maintain. Edit 2 is the opposite, plenty of ways to find the information you're looking for but requires more places to update and maintain (and as such probably more bugs) – Datise Mar 28 '17 at 20:01
  • Okay @Datise are you suggesting that my Team model have a reference to the Task model, iterate through the Task array schema in the Team model, when match has been found, populate user, which would mean the Task model has a schema referencing users? – henhen Mar 29 '17 at 03:23
  • As I said, there are a lot of different ways to do it. I would probably do it by having a team and user reference on the Task model and populating user when I query for tasks by team. – Datise Mar 29 '17 at 16:25
  • I updated the query example and removed the second edit as I think it was confusing you. You should be able to accomplish what you want with this schema even if you can't always query the way you want. I'd suggest trying to implement something like the schema I provided I'm not sure what more you'll get without just testing it out at this point. – Datise Mar 29 '17 at 16:33