User to User Database Object Structure - database

What is the best practice to structure my objects in my database in a Teacher to Student application? To be more specific, a teacher will have multiple students, and students may have multiple teachers they will interact with. Right now, each class a teacher is instructing has a "class id" and when a student enrolls, they add that same class ID to a list in their model. When a teacher wants to view a roster, I use
Student.find({"class-id": "abc123"}, function(err, student){
if(err){
console.log(err)
} else {
res.render("/roster", {student: student});
}
});
"abc123" is an example class code.
It's very basic code at the moment-- just starting a rough app. Should I make some type of different database structure with some type of hierarchy between teachers and students?
Sorry for the basic and perhaps confusing question, I'm very new to web development and just don't want to paint myself into a corner.
I'm using the MEAN stack but I think this is a more conceptual question.
Thank you

dmfay's comment is insightful. with a relational model, using mongoose is suboptimal. However, you of course can still use it. it would go something like this (by the way, you were unclear about this, but its implied that each student and teacher have multiple class-ids, im working under that assumption)
var mongoose = require("mongoose");
var StudentSchema = new mongoose.Schema({
//other student details
teachers: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Teacher"
}],
classIds: [STRING]
});
module.exports = mongoose.model("Student", StudentSchema);
and then the teacher schema would be along the same lines:
var mongoose = require("mongoose");
var TeacherSchema = new mongoose.Schema({
//other teacher details
students: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Student"
}],
classIds: [STRING]
});
module.exports = mongoose.model("Teacher", TeacherSchema);
then, how you go about it is, when a student enrols for a class, you find all teachers with that classId in their classIds array, you loop over them adding the student to each one of their students array, and you add them (the teacher) to the student's teachers array.
when you want to search for all students in a class, you go about like you did, except that the classIds is an array of strings, not a single string, you need to check if any of them matches

Related

MongoDb subdocument array populate (via Mongoose ORM) : Does it maintain array order when populate is called

Suppose I have 2 Schema's in Mongoose that look like this:
var movieSchema = mongoose.Schema({
name: String,
type: String
});
var moviePlaylistSchema = mongoose.Schema({
name: String,
movies: [{type: mongoose.Schema.Types.ObjectId, ref: 'Movie'}]
});
var Movie = mongoose.model('Movie', movieSchema);
var MoviePlaylist = mongoose.model('MoviePlaylist', moviePlaylistSchema);
If a query was made along the following lines:
MoviePlaylist.find({}).populate('movies').exec(function(err, res) {
if (err) console.log('err', err);
else {
console.log('res', res);
res.forEach(function(elem, index) {
console.log('elem.name', elem.name);
});
}
});
Would the order of the elements in the array be maintained? The objective here is to allow the user to maintain a playlist order of their movies. If, when the "populate" method fires, the array order of Movie object Ids is not maintained, then this will not serve my purpose. Hence thought I'd ask someone who is more knowledgeable in this area.
If this works, then I have another task which is allowing the user to change the order of movies in the playlist, which should be straight forward by allowing the movie object id index to be swapped in the array.
Thanks for your help in advance.
MongoDB will keep the order of the array, much like an array in any programming language.
You can view the BSON/JSON spec for reference which highlights that the array must contain integer values for keys, and be maintained in ascending numerical order.
Additionally, the Mongoose populate on an array works by calling Model.populate via forEach on each element of the array. This modifies the array in place, hence the order is preserved. You can see the relevant source code here.

Backbone.js Dynamic model creation based on JSON received from the server

I am trying to create a backbone client side application. I am receiving a list of json objects from the server on startup that will be a list of the possible tables exposed from the server, with their structure. E.g. Customers, Orders, Invoices, Employees
I want to create the models, collections and views dynamically based on the data I receive from the server.
Only when I receive the json on load will I know what the models should be and what the relationships between the models should be.
E.g. Customers structure might be Id, CustomerName, Address, Contact Numbers.
Order Structure might be Id, CustomerId, OrderDate, Amount
etc
By building Models, collections, views, controllers dynamically, I could in theory on startup point at another server who might give me a totally different set of tables e.g. : Movies, Actors etc.. with their structures.
Also, if additional fields are added I don't have to change the client side code again. E.g. Customer table might include a new field called ContactPerson
Please assist me as all the examples I saw on backbone is all based on statically defining the models on the client side up front. So create a model and collections and views for Customers, Orders, Invoices, Employees etc. etc.
Best wishes,
Andy
As already mentioned in the comments, Backbone models are dynamic by nature. So this is perfectly valid for example:
// A example dataset, this could be returned as JSON from the server
var jsonDataA = [
{
name: "Foo",
title: "Bar"
},
{
name: "a",
title: "b"
}
],
// A different example dataset
jsonDataB = [
{
make: "X",
model: "Y"
},
{
make: "Z",
model: "ZZ"
}
],
MyModel = Backbone.Model.extend({
/* Empty Model definition */
}),
MyCollection = Backbone.Collection.extend({
model: MyModel
}),
collection = new MyCollection();
collection.reset(jsonDataA);
console.log(collection.models);
collection.reset(jsonDataB);
console.log(collections.models);
Here I have reused the same Collection and Model definition to store completely different datasets.
One part is the raw data, the other part is its relations. You need to transport the metadata also, which contains the types and their relations. Model attributes will be populated automatically.
From your metadata a simple object can be constructed, where the keys describe one entity, for example:
var entites = {};
entities["Customer"] = Backbone.Model.extend({
/* Model definition based on metadata */
});
var parametersFromServer = {name: "John Doe"};
var customer = new entities["Customer"](parametersFromServer);
For building relations I would recommend using BackboneRelational plugin.

How to handle empty arrays in Firebase?

Say person can have multiple cars, and car can have multiple accidents. So we could have:
# Person with no cars
person:
name: "Misha"
cars: []
# Person with free-accident car
person:
name "Arlen"
cars:
0:
name: "Toyota"
accidents: []
Firebase stores these people as:
person:
name: "Misha"
and
person:
name "Arlen"
cars:
0:
name: "Toyota"
So in JavaScript I have to do following to restore the empty arrays: (CoffeeScript)
if person.cars?
for car in person.cars
car.accidents = [] unless car.accidents?
else
person.cars = []
Is there a better way to handle empty arrays in Firebase without writing this needless JavaScript code?
I think that, if I understand the core question, the short answer is that there is no way to force an empty array into Firebase. However, there are some paradigms that might work better than what you have above.
Keep in mind that Firebase is a real-time environment. The number of cars and accidents can (and will) change at any time. It's best to treat everything as new data arriving in real time and avoid even thinking about exists or doesn't exist.
// fetch all the people in real-time
rootRef.child('people').on('child_added', function(personSnapshot) {
// monitor their cars
personSnapshot.ref().child('cars', 'child_added', function(carSnapshot) {
// monitor accidents
carSnapshot.ref().child('accidents', 'child_added', function(accidentSnapshot) {
// here is where you invoke your code related to accidents
});
});
});
Note how there is no need for if exists/unless type logic. Note that you would probably also want to monitor child_removed on cars and people and call ref.off() to stop listening to specific children.
If for some reason you want to stick with the static model, then forEach will become your friend:
// fetch all the people as one object, asynchronously
// this won't work well with many thousands of records
rootRef.child('people').once('value', function(everyoneSnap) {
// get each user (this is synchronous!)
everyoneSnap.forEach(function(personSnap) {
// get all cars (this is asynchronous)
personSnap.ref().child('cars').once('value', function(allCars) {
// iterate cars (this is synchronous)
allCars.forEach(function(carSnap) { /* and so on */ });
});
});
});
Note how, even with forEach, there is no need for "exists or unless" sort of logic.
I usually use the DataSnapshot function numChildren() to see if it's empty of not, like this
var fire = new Firebase("https://example.firebaseio.com/");
fire.once('value', function(data){if (data.numChildren() > 0){ /*Do something*/ });

Using Backbone Relational, how to Store Array of Object IDs in Mongoose, Retrieve as Objects, Save Back as IDs

Here's my situation:
I have a Mongo Database with the following schemas:
var SurveySchema = new Schema({
start_date: Date,
end_date: Date,
title: String,
questions: [ObjectId] //this I want to refer to questions
});
var QuestionSchema = new Schema({
question_text: String,
type: String,
....... (Lots and lots of fields)
});
Full Schema file: https://github.com/nycitt/node-survey-builder-api-server/blob/master/schemas.js
Backbone models: https://github.com/nycitt/node-survey-builder/blob/master/app/js/survey-builder/models.js and collection https://github.com/nycitt/node-survey-builder/blob/master/app/js/survey-builder/collections.js
The Questions should be able to stand alone (be part of 2-3 surveys say). But a survey should refer to the question.
My ideal flow would be:
Backbone calls: GET /api/survey/123 and retreives the survey object where Mongoose translates the Object IDs to actual objects (does the join)
Backbone calls: POST /api/survey/123 posting ONLY the IDs of the questions (is there some Backbone Relational magic to do this)

Saving Entries to Database Relationship (many-to-many) Table from a Backbone Project

I currently have two Backbone models: user and project. I would like to have a Backbone view containing a form that enables the creation of a new project, and the association of currently existing users (entries from the database users table) with this project. When a user completes this form and clicks the save button, the new project should be saved into the database (in the projects table) and the relationship between the saved project and the related users should be saved into a relationship table (projects_users table, containing the corresponding project id and the user id for each relationship). Currently, I can save the project information, but could not get any data into the projects_users table using Backbone-relational.
What do you think the best approach to achieve the above functionality is? It would be great if you could point me to specific code that I could use as a template.
Thank you,
Alexandra
After some frustrating trial-and-error period, I finally managed to get my code working! It's not beautiful, but it is functional, and now I can start thinking about improving it. Hopefully, some other people will find this information useful ...
The one thing that put me on the right track was the understanding that what needed to be changed was not only in the backbone view code (the one with the new projects form), but also in the corresponding rails models.
For the rails part (Rails 3.2.2), I made sure that the following model files had the necessary information:
project.rb
class Project < ActiveRecord::Base
has_and_belongs_to_many :users
#attr_accessible :name, :description, :users_attributes
#has_many :projects_users, foreign_key: "project_id", dependent: :destroy
#has_many :users, through :projects_users
#accepts_nested_attributes_for :users
end
user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :projects
end
projects_users.rb
class ProjectsUsers < ActiveRecord::Base
belongs_to :project
belongs_to :user
end
I have read in many different places that has_and_belongs_to_many is not the best way to set many-to-many relationships in rails. Nevertheless, I could not get the same functionality using the has_many definition - the commented part in project.rb is the way I tried this different approach. The file user.rb had some corresponding code, which I removed for simplicity.
Now, what I needed to get done in the backbone form view was to send a POST request with a JSON object that the rails projects_controller.rb could recognize. Initially, I tried several POST requests without success (and no errors to guide me). But then, I remembered to have previously implemented a form for teams where users could be added to a particular team (HABTM Checkboxes - there is a railscast for this functionality). After looking at this example, I realized what was needed from my POST request. This is what I wanted to see in the rails server log file:
Started POST "/projects" for 127.0.0.1 at 2012-06-27 00:35:22 +0000
Processing by ProjectsController#create as JSON
Parameters: {"project"=>{"description"=>"with some description", "user_ids"=>["101", "1", "99"], "name"=>"some new project"}}
Backbone relevant files to achieve the above request:
project.js
App.Models.Project = Backbone.Model.extend({
urlRoot: '/projects',
// Default attributes for the project.
defaults: {
description: "",
user_ids: []
},
/* getters */
});
user.js
App.Models.User = Backbone.Model.extend({
/* getters */
});
form.js
App.Views.Projects.Common.Form = Backbone.View.extend({
...
events: {
"submit #new_project_form" : "formSubmit"
},
formSubmit: function(event) {
this.submitted($(event.target));
return false;
},
submitted: function(formElement) {
var newData = this.serializeFormData(formElement);
this.model = new App.Models.Project({
name : newData.name,
description : newData.description
});
this.saveFormData(newData);
return false;
},
serializeFormData: function(formElement) {
var fields = formElement.serializeArray();
var serializedData = {};
$.each(fields, function(index, field) {
serializedData[field.name] = field.value;
});
return serializedData;
},
// THE IMPORTANT PART FOR THE POST REQUEST
saveFormData: function(newData) {
// preserve reference to view for callbacks
var self = this;
var project = this.model;
project.set({
// a list of user ids associated with a project
"user_ids" : this.view_variables.user_ids
});
var project_object = ({
"project" : _.clone(project.attributes)
});
$.ajax({
type: 'POST',
url: '/projects',
data: project_object,
dataType: "json",
success: function() {
self.$el.hide();
self.addNewModelToCollection();
}
});
},
...
});
The code is kind of verbose, and includes some code that is specific to my project. Still, the relevant part is in the saveFormData function, where the jQuery ajax function is used.
In case you have any suggestions, either for the rails or for the Backbone part, please let me know. I will be happy to learn how to improve this solution.
I know it's old post but this looks interesting: http://backbonerelational.org/

Resources