MongoDB Schema Design - New Collection or Reference? - mongodb

I bumped into a question regarding the schema for MongoDB. With reference to the example on MongoDB Schema Design, regarding the db.students and db.courses.
As I am more used to SQL structured, I am still confused with this reference or embed problem. So, the example is only showing the course within the db.students, is referenced to the db.courses. So if I were to categorize my courses such as humanities, languages, etc, how should I do it?
What would be a better way to do it?
1. create a collection called `db.categories` and reference the db.courses to it?
// db.courses
{ name: Biology, cat: 1 }
{ name: English, cat: 2 }
// db.categories
{ cat: 1, name: Humanities }
{ cat: 2, name: Languages }
2. to just embed within the courses?
// db.courses
{ name: Biology, cat: Humanities }
{ name: English, cat: Languages }
{ name: History, cat: Humanities }
Could anyone please kindly advise, what should I be doing?
Thank you.

Introduction:
In yours case both variants will be good because category just enumeration an you don't need load category in order to display course, you just need create some enumeration and get category name by id.
But in for example if you have table db.users and each user have collection of db.courses you don't need create separate document your just need nested collection courses. And it's really cool because in sql you need create separate table with one to many reference.
The one big benefit of document database is that you can create big documents with nested collections and no need to join tables.
Answer:
So in your case i suggest two ways:
Create enumeration for categories and get category name by id( but not load from mongo).
Just copy category name in course( But first case better because in case if category name was changed your need update each course with new category name).

Related

Want to change a model ObjectID value

I am creating a Student and Course relationship
A student may have multiple courses. A one to many relationship.
This is made in Express and I'm using MongoDB. I have shorten the models to keep it simple
Student Model
const studentSchema = new mongoose.Schema({
name: {type: String},
courses: [{
type: ObjectId,
ref: 'class'
}]})
Course Model
const classSchema = new mongoose.Schema({
ClassId: {type: String,},
Grade: {type: Number,}, })
Currently, what I have is when I update the grade, it will update the grade values for the course itself and not the course in the user courses.
router.put(....)
const{username, courseId, grade} = req.params
const existingUser = await Student.findOne({username}).populate({
path: 'courses',
select:['ClassId','Grade']
})
const findCourse = existingUser.courses.find(
x => x.ClassId == courseId
)
findCourse.Grade = parseInt(grade)
await findCourse.save()
The problem is this will change the grade for the course itself. Meaning any student that adds this course will have that grade too.
I'll explain what I want to do in Java/OOP terms if that helps.
I want the student object to have it's own course objects. At the moment, it seems like classes are static class objects.
I want to access that specific student courses and change that student grade of that specific course.
Please help, I already spent a couple of hours on this. In SQL, the student would have a reference key and be able to easily change their values, I'm having trouble in MongoDB.
Alright, I finally figured it out. In hindsight, it makes sense. Gave myself a break from coding and came back to see the problem.
Lets pretend we have two students and one course. This courses is seeded with data.
When a student A picks that course, they add it to their course array. When student B wants that course, they also get that exact course. Now they are sharing the course. Basically, they are sharing the same reference.
The solution to this is to still find the course. Now make a new course object, copy every value of the original to the copy. Save the copy to the database and now you add that course to the student. Now we can still register for courses and use the seeded data and students don't share anymore.

In Objection.js, what's the benefit of setting up relationMappings?

I'm kind of confused about what relationMappings do inside a Objection.js Model class.
I thought once we setup the relationMapping inside a Model, we will get related data in every query. But, it turns out that I still only the Model properties itself.
Is there anything else I should use to get related data in query?
Relation mappings gives model semantics how relations can be fetched when they are needed. It would be really bad for performance to always query all related rows in addition to main table's row. When you create relation mappings to model, you will not need to write joins manually every time you need to query relations. Also they enable many other objection features, which requires information how row relations goes in DB.
To use relation mappings in query Objection.js requires that within every query you must tell which relations you want to fetch with the main row with .withGraphFetched or .withGraphJoined methods https://vincit.github.io/objection.js/guide/query-examples.html#eager-loading
for example:
class Person extends Model {
static get tableName() {
return 'persons';
}
static get relationMappings() {
return {
pets: {
relation: Model.HasManyRelation,
modelClass: Animal,
join: {
from: 'persons.id',
to: 'animals.ownerId'
}
}
};
}
}
const people = await Person.query().withGraphFetched('pets');
// Each person has the `pets` property populated with Animal objects related
// through the `pets` relation.
console.log(people[0].pets[0].name);
console.log(people[0].pets[0] instanceof Animal); // --> true
Mappings are also used when you insert nested object data with .insertGraph so that related objects are inserted to related tables and foreign key references etc. are automatically filled according to relation mapping declarations.
There are many other places where those are used, but I hope this gives a rough idea why they exist.

API Design: Caching “partial” nested objects

Let's say we have schools with some data including a name and a list of students, and students with some data including courses they're enrolled in and a reference to their school. On the client:
I'd like to show a screen that shows information about a school, which includes a list of all of its students by name.
I'd like to show a screen that shows information about a student, including the name of their school and the names of courses they're taking.
I'd like to cache this information so that I can show the same screen without waiting on a new fetch. I should be able to go from school to student and back to school without fetching the school again.
I'd like to show each screen with only one fetch. Going from the school page to the student page can take a separate fetch, but I should be able to show a school with the full list of student names in one fetch.
I'd like to avoid duplicating data, so that if the school name changes, one fetch to update the school will lead to the correct name being shown both on the school page and the student pages.
Is there a good way to do all of this, or will some of the constraints have to be lifted?
A first approach would be to have an API that does something like this:
GET /school/1
{
id: 1,
name: "Jefferson High",
students: [
{
id: 1
name: "Joel Kim"
},
{
id: 2,
name: "Chris Green"
}
...
]
}
GET /student/1
{
id: 1,
name: "Joel Kim",
school: {
id: 1,
name: "Jefferson High"
}
courses: [
{
id: 3
name: "Algebra 1"
},
{
id: 5,
name: "World History"
}
...
]
}
An advantage of this approach is that, for each screen, we can just do a single fetch. On the client side, we could normalize schools and students so that they reference eachother with IDs, and then store the objects in different data stores. However, the student object nested inside of school isn't a full object -- it doesn't include the nested courses, or a reference back to the school. Likewise, the school object inside of student doesn't have a list of all attending students. Storing partial representations of objects in data stores would lead to a bunch of complicated logic on the client side.
Instead of normalizing these objects, we could store schools and students with their nested partial objects. However, this means data duplication -- each student at Jefferson High would have the name of the school nested. If the school name changed just before doing a fetch for a specific student, then we'd show the right school name for that student but the wrong name everywhere else, including on the "school details" page.
Another approach could be to design the API to just return the ids of nested objects:
GET /school/1
{
id: 1,
name: "Jefferson High",
students: [1, 2]
}
GET /student/1
{
id: 1,
name: "Joel Kim",
school: 1,
courses: [3, 5]
}
We'd always have "complete" representations of objects with all of their references, so it's pretty easy to store this information in data-stores client side. However, this would require multiple fetches to show each screen. To show information about a student, we'd have to fetch the student and then fetch their school, as well as their courses.
Is there a smarter approach that would allow us to cache just one copy of each object, and to prevent multiple fetches to show basic screens?
You might be mixing two concepts: Storage and Representations. You can give back a non-normalized representation (the first option you suggested) without also storing those "partial" object in your database.
So I would suggest to try to return non-normalized representations, but storing them normalized (if you are using a relational DB).
Also, an improvement suggestion: You may want to use proper URIs instead of Ids in your representations. You probably want the clients to know "where" to get that object from, it's easier therefore to just supply the URI. Otherwise the client needs to figure out how to produce a URI out of an Id, and that usually ends up being hard-coded in the client, which is a no-no in REST.

Can I rename GORM's "version" field? (Grails 2.2 + GORM MongoDB)

I've got a domain object that already has a property called versions, so I'd like to give a different name to the built-in version property (used in GORM for optimistic locking). For instance, I'd like to call it updateCount instead.
Note that I do want the semantics of optimistic locking; I just want to give the field another name. Here's what I've naively tried (and it didn't work):
class Item {
ObjectId id
static hasMany = [versions: ItemVersion]
static mapping = {
table 'item'
version column: 'updateCount' // <-- This was my attempt
}
}
I would definitely appreciate any help in...
Determining whether this is possible, and
If so, making it work :-)
Thanks!
First thing first. MongoDB (NoSQL) deals with Documents and Collections instead of Table and rows.
Being said that, the domain class should look like:
class Item {
ObjectId id
String itemName
static hasMany = [versions: ItemVersion]
static mapping = {
//Collection in Mongodb is to Table in relational world
collection 'item'
//attr in Mongodb is to column in relational world
itemName attr: 'item_name'
//After spending some time investigating it was found that
//attr for version does not make any difference
//The below would not work for implicit GORM variable "version"
//default attribute name is the variable name.
//version attr: 'updateCount'
}
}
In case you want to configure default property across the domains to switch on/off the versioning then have a look at Global Mapping Configuration.

Can I use NoSQL instead of a Relational Database?

For a library, I need to keep track of users and books. Basically I need to be able to know:
the list of books currently borrowed by a user
the current borrower of a given book
The app is done with node.js and mongoDB (with moogoose). I have the following schema:
BookSchema = new Schema({
title : String,
author : String,
current_borrower_email: String,
});
mongoose.model('Book', BookSchema);
// Define User model
UserSchema = new Schema({
lastname : String,
firstname : String,
email : String,
books : [BookSchema] // Books the user is borrowing
});
mongoose.model('User', UserSchema);
I guess this would be simplier to set this up in a relational DB where I could easily use many to many relation ships with foreign keys but I wanted to give a try to MongoDB.
Do you think this solution could work ? Also, if I delete a Book object, it seems I will have to remove it manually from the array of the user who borrowed it, it that right ?
In general mongodb will be good replacement of relational database for above task.
So some basics:
1.Once some one take a book you just need to copy book into the nested collection of user and user to the Book.
2.Once user has updated his profile you need aslo update information about user within Book.
3.Once book data was changed you also need update info about book within user.
4.If you trying to delete some book and current borrower exists you should say that book was borrowed by 'User' and not delete it.
I just suggest to add into your schema instead of current_borrower_email entire User object -> current_borrower: UserSchema.
So with such denormalized schema you will able easy show(within one request to mongodb):
the list of books currently borrowed by a user.
the current borrower of a given book
It is an old question but it came first in google so...
It's not too complicated but it is too long to summarize.
Read this:
http://blog.mongodb.org/post/87200945828/6-rules-of-thumb-for-mongodb-schema-design-part-1