Mongoose multi ref specifying the model - node.js

I have the following scheme:
var user = Schema({
id: Number,
name: String,
surname: String,
role: { type: Schema.Types.ObjectId, ref: "" }//member or crew
property: Number
});
var member = Schema({
cod_id: Number,
aa: String,
bb: String,
});
var crew = Schema({
cod_id: Number,
cc: String,
dd: String,
});
Member and crew, they are both users but they have different attributes.
The only attributes that are equal are: name, surname, role and property.
What I would like to understand if it were possible to do such a thing, specifying in user the role attribute that can be either member or crew, refer to the specific model in question.
Everything stems from the need to have the property attribute in a single model and not having to put this attribute in either member or crew, otherwise when I have to do a search I have to do two, one in the model member and one in the crew, waiting for don't have duplicate problems.
Can you give me some advice?

Related

Mongoose:create model using "1" as value for Schema.Types.ObjectId

What im tryng to do is the following, i have my models defined as:
const channelSchema= new Schema({
name:{type: String},
country_id:{type: Schema.Types.ObjectId, ref: 'country'}
})
and
const countrySchema = new Schema({
_id{ type : Number }
name: { type: String },
gmt: { type: String }
})
now, when i create a new countrySchema, i use a personalized "_id", such as 1 or 2, always a number and so on, this is created with 0 errors.
My problem is when i try to create a new channel schema, using
country_id = "1"
or
country_id = 1
i get the error:
'Cast to ObjectID failed for value "1" at path "country_id"'
what i've read from mongoose documentation, ObjectId is created (at default) with 12bytes.
My question is: Is there a way to evade having to use 12bytes keys, and to use "1" as ObjectId so i can populate channels with countries?
IMPORTANT: im using Node.Js, mongoose and express
If you want to reference a relationship, you need to set the same type to both sides of a relation. In this case _id of country is declared as Number, so country_id should be Number as well
country_id: { type: Number, ref: 'country' }
You could use "id" instead of "_id".

Mongoose Many to many relations

I'm new to mongoDB and Mongoose, and I have some problems with relations.
My schema has 3 tables (User / Person / Family), you can see it below.
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var userSchema = Schema({
_id : Schema.Types.ObjectId,
email : String,
person : [{ type: Schema.Types.ObjectId, ref: 'Person' }] // A user is linked to 1 person
});
var personSchema = Schema({
_id : Schema.Types.ObjectId,
name : String,
user : [{ type: Schema.Types.ObjectId, ref: 'User' }] // A person is linked to 1 user
families : [{ type: Schema.Types.ObjectId, ref: 'Family' }] // A person have 1,n families
});
var familySchema = Schema({
_id : Schema.Types.ObjectId,
name : String,
persons : [{ type: Schema.Types.ObjectId, ref: 'Person' }] // A family have 0,n persons
});
var User = mongoose.model('User', userSchema);
var Person = mongoose.model('Person', personSchema);
var Family = mongoose.model('Family', familySchema);
I don't know if my schema is good, does the parameter person is require in my userSchema ? Because the informations will be duplicated, in userSchema I will have the personID and in the personSchema this wil be the userID.
If I understand it's usefull to have this duplicated values for my requests ? But if the informations is duplicated I need to execute two queries to update the two tables ?
For exemple, if I have a person with a family (families parameter in personSchema), and in the family I have this person (persons parameter in familySchema). What will be the requests to remove / update the lines in the tables ?
Thanks
IMHO, your schema seems fine if it meets your needs !! (Although, if you think your current schema fulfills your purpose without being bloated, then yeah its fine)..
"Person" seems to be the only type of a user and the entity to be connected to rest of the other entities . As long as this is the case, you can feel free to remove the person parameter from the userschema as you can access the user information from the person. But lets assume if there exists another entity "Aliens" who also has their own unique family, then it would be better to add the alien and person parameter in the "User" Schema to see the types of users.(As long as there's only one type i.e. Person, then you may not need to add it in userschema). In case, if you still like to keep it, then please make the following change too in your schema as you are passing the array although it seems to be one to one relation !
var userSchema = Schema({
_id : Schema.Types.ObjectId,
email : String,
person : { type: Schema.Types.ObjectId, ref: 'Person' } // A user is linked to 1
//person // Here I have removed the []
});
var personSchema = Schema({
_id : Schema.Types.ObjectId,
name : String,
user : { type: Schema.Types.ObjectId, ref: 'User' } // removed [] here too
families : [{ type: Schema.Types.ObjectId, ref: 'Family' }]
});
Yes, you will need to update it for both entities Person and Family if you want to maintain the uniformity. But, it could be done in one request/ mutation.
Well, you could perform the request depending upon the flow order of your business logic. Lets say if "Homer" is a Person who is a new member of the Simpson Family.
So, in that case you would add "Homer" to the Family collection(table) and then push the
ObjectId of this Simpson (Family collection) to the Person entity.
I have added the sample example of adding Homer to the Simpson family below. Hope this helps :)
addNewFamilyMember: async (_, {personID, familyID}) => {
try{
let family = await Family.findOne({_id: familyID});
let person = await Person.findOne({_id: personID}); // created to push the objectId of the family in this
if (!family){
throw new Error ('Family not found !')
} else {
let updatedFamily = await Family.findByIdAndUpdate(
{ _id: familyID },
{
"$addToSet": { // "addToSet" inserts into array only if it does not exist already
persons: personID
}
},
{ new: true }
);
person.families.push(updatedFamily); // pushing objectId of the Simpson family in Person "Homer"
person = await person.save();
updatedFamily.persons.push(person); // pushing "Homer" in the Simpson family
updatedFamily = updatedFamily.save();
return updatedFamily;
}
} catch(e){
throw e;
}
}
If you want to perform update, then it depends upon the intent of your purpose (as for example, if you just want to update the name "Homer", you would only have to update it in the Person collection, as the Family collection already has reference to the objectId of Homer, so every time you make an update to the Homer, the updated document would be referenced by Family collection ! ), and
if you want to perform deletion, then in that case too, the approach would be different based upon the scenario, as if you wish to remove a person document, or just remove the person reference from the family, or remove the family reference from the person !!
Lets say you want to delete a person then in that case, you would have to take the personId and search for that person and since you have access to the families via this person, you can access the families via person.families and remove the personId from those respective families as well ! And then you could remove the associated user too as you have the reference to the user too from the same person object.
To sum up, it depends upon your choice of action, and how much sanitization you want in your schema.. The above mentioned process would be just different in case if we take a different approach.

Mongoose join query using nodejs

I have two schemas:
Department:
{dept_id :Number,
name: String,
Member: [type: Schema.Types.ObjectId, ref :'Professor']
Professor:
{
deptid :{type:Number, ref:Department},
name: String,
age:Number}
I need to find a joined table consisting of employee details along with Department details.
How can i do that?

mongoose-paginate sorting by referenced document

I would like to sort my Group-Model by the name of the departure_country with mongoose-paginate and NodeJS.
My Group-Schema:
var GroupSchema = new Schema({
name: String,
flight_date: Date,
....
departure_country: {type: Schema.ObjectId, ref: 'Country'},
....
});
The Country-Schema:
var CountrySchema = new Schema({
name: String,
code: String,
});
Using the sortBy-option of mongoose-paginate sorts by the country's _ids and I don't know how to tell it to sort by the name.
how about making the country code the _id of Country-Schema?
then departure_country: {type: String, ref: 'Country'}
That might actually be a better design. Country-Schemas _id would be small, and country codes shouldn't have collisions. Also in GroupSchema you'd use less space to store the "foreign key" departure_country.
then you'd be able to sort by departure_country and there's a chance the sort would also be valid for country name.

Mongoose/ MongoDB Database Design

I always have a certain fixed structure in my model (GroupName) and a dynamic part of 1-x (Members).
Group1
GroupName
Member 1
Member 2
Group2
GroupName
Member 1
Group3
GroupName
Member 1
Member 2
Member 3
Is it better to use two tables and connect them later via ids like this:
Groups:
Group1
GroupName
GroupId
Group2
GroupName
GroupId
Members:
Member 1
GroupId
Member 2
GroupId
or to use Schema.Types.Mixed(or anything else)? And how to do it in the second way?
I will always use them in combination later. From a gut feeling I would choose the first method:
http://blog.mongolab.com/2013/04/thinking-about-arrays-in-mongodb/
EDIT:
But even on the second method I have the issue, that one member can belong to multiple groups and I don't want to store him twice. The groups are unique and do only exist once.
But I'm new to MongoDb so I want to learn what's the best option and why.
EDIT II:
I have choosen two divide it into two docs. Is this implementation of the Schemas than correct like this:
var mongoose = require('mongoose');
// define the schema for group model
var groupSchema = mongoose.Schema({
href: {
type: String,
required: true,
unique: true
},
title: String,
members: [id: Schema.Types.ObjectId, name: String]
});
// create the model for users and expose it to our app
module.exports = mongoose.model('group', groupSchema);
&&
var mongoose = require('mongoose');
// define the schema for member model
var memberSchema = mongoose.Schema({
id: {
type:Schema.Types.ObjectId,
required: true,
unique: true
},
amount: String,
name: String
});
// create the model for users and expose it to our app
module.exports = mongoose.model('member', memberSchema);
There is an excellent post on the MongoDB blog which tells us about the various ways a schema can be designed based on the model relationships.
I believe the best schema for you would be to make use of embedded arrays with the member IDs.
//Group
{
_id: '1234',
name: 'some group',
members : [
'abcd',
'efgh'
]
}
EDIT
There is a correction needed in the schema:
// define the schema for group model
var groupSchema = mongoose.Schema({
href: {
type: String,
required: true,
unique: true
},
title: String,
members: [{id: Schema.Types.ObjectId, name: String}] //Needs to be enclosed with braces
});
// create the model for users and expose it to our app
module.exports = mongoose.model('group', groupSchema);
I don't know what your documents contains and if members are a growing array - for example Group1 can have 1-n members in any given moment . if this is the case you should go with option 2: try something like:
{gId: 1, mId: 5}
That is a design best suited for Social graph. Your Group documents will have a fixed size which is good for memory and you can easily get all the members of a group (just don't forget to index gId and mId)
If for each group there is a fixed number of members (or not growing and shrinking to much) then go with option 1
There is a great post by mongoDb team (and also src code) that talks about design.
Socialite

Resources