MongooseJS schema relation - javascript

I have MongooseJS schema as follow:
var UserSchema = new Schema({
name : String,
app_key : String,
app_secret : String,
tasks : [{ type : Schema.ObjectId,
ref : 'Task'}]
})
var ActionSchema = new Schema({
name : String,
description : String,
module : String
})
var enumTaskState = ['New', 'Indexing', 'Idle', 'In Queue', 'Working'];
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
actions : [{type : Schema.ObjectId,
ref : 'Task'}],
user : { type : Schema.ObjectId,
ref : 'User'},
status : { type : String,
enum : enumTaskState,
default : 'New'}
})
Problem is, when I set a task's user, do I manually have to go to the user and add a task there too? This seems like extra work (redundancy), is there an option in mongoosejs that will allow me to specify the relations it will handle everything by itself?
Thanks.

MongoDB is not a relational database, it is a document based based database.
You can get a user's list of tasks by querying the TaskSchema and looking for the user you want. Just make sure to add an index so it will be a fast query:
user: {type: Schema.ObjectId, ref: 'User', index: true}

To elaborate on emostar's answer:
You're trying to use MongoDB like a relational database. You're giving Users a list of Tasks, but each Task is referencing a User. In a typical MongoDB schema, you'd figure out how you want to use the models in your app and just embed the documents where it makes sense (e.g. if Users contains an array of Tasks, there's no need for a task to have a reference to it's owner--just look at the User that owns the collection).

Related

Mongoose : Cast to ObjectId failed for value "Some String" at path "_id"

New to MongoDB, Javascript stack and need help understanding cause of this error.
I have my model created :
const
Mongoose = require('mongoose');
Schema = Mongoose.Schema,
Model = Mongoose.model;
module.exports = Model('Project',
new Schema({
icon : String,
name : String,
state : String,
number : String
})
);
This is my MongoDB document :
[![MongoDB Document][1]][1]
I am attempting to receive all the documents in the collection when I call the API so therefore as per the Mongoose document I am using the find() method.
Here is my API Implementation:
const Project = require('../../models/project');
router.get('/projects/:page?/:limit?',
function(req, res, next){
const page = Math.max(req.params.page || 1, 1) - 1;
const limit = Math.max(req.params.limit || 20, 20);
//Verified : I am hitting the API
console.log("Reached API /projects");
Project.find()
.populate('icon')
.populate('name')
.populate('state')
.populate('number')
.limit(limit).skip(page * limit).exec(
function(err, project)
{
if (err) { return next(err); }
res.send(project);
}
); //End of exec()
}//End of unction
);
I am successful in making the API call using fetch() but I am receiving "Cast to ObjectId failed error" for all the String values.
I believe there is something really simple within my Mongo DB document that I might be missing. Please help me understand and solve this issue.
**EDIT ---
The error seems to point at the string values of the keys:
**
Thank you
Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s). So you're Id cast is not valid, because of string, you need to have ObjectId, some changes need to be made before it, Let's debug:
const alldata = await Project.find()
console.log(alldata) // ?
does this return something, I'm using async await here if it return data then the problem is with your populate because your Id case isn't valid as you save in schema string and you're referring here populate, example of using populate:
module.exports = Model('Project',
new Schema({
icon : [{ type: Schema.ObjectId, ref: 'your icon document' }],
name : [{ type: Schema.ObjectId, ref: 'you name document' }],
state : [{ type: Schema.ObjectId, ref: 'state document' }],
number : [{ type: Schema.ObjectId, ref: 'number document' }]
})
);
but it seems to me that you don't need to use the populate because you have simple data, name, number... so you should be good to go with the above example
Resources: mongoose fetching data, using populate, relation

Nested elements in mongoose modelling

I have to create a database in this format:
I have tried to do it like this :
var mongoose = require("mongoose")
, Schema = mongoose.Schema;
var categorySchema = new Schema({
_id: String,
subcategory: {
type: Schema.Types.ObjectId,
ref: "Subcategory"
},
id: String,
name: String,
page_description: String,
page_title: String,
parent_category: String,
c_showInMenu: Boolean
});
module.exports = mongoose.model("Category", categorySchema);
And I did the same for every subcategory, but I named them "Subcategory" and "SubsubCategory". I'm pretty much a newbie, I've read the documentation on mongoose and I've also followed an online course. I feel like I know something, but I don't understand it properly or that my logic behind it is wrong.
Also, I've managed to find this about recursive elements in Schemas, but I don't fully understand it and I don't know how to implement it to my database: Recursive elements in Schema : Mongoose modelling
I checked your code and collection image.
Here's what you are doing wrong.
You don't need to declare _id field, it's auto-generated.
Keep the column names same as they are in the given collection image.
This should be your schema declaration. Note that, this schema is for the whole collection and not just for categories array.
var mongoose = require("mongoose"),
Schema = mongoose.Schema;
var mainSchema = new Schema({
categories: [ {
id: String,
image: String,
name: String,
page_description: String,
page_title: String,
parent_category_id: String,
c_showInMenu: Boolean
}]
});
This is just an example of the schema, please add necessary changes to it.

Is it possible to populate without `_id`?

I Set manually an uid to each item in my collections...I Want To Know It's Possible that I use uid for populate?
I Dont Want Use from '_id' because Have Many collections In My DB And should change many things...somthing like this :
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var PersonSchema = new Schema({
name : String
, age : Number
, stories : [{ type: String, ref: 'Story' }]
});
var StorySchema = new Schema({
_creator : { type: String, ref: 'Person' }
, title : String
, fans : [{ type: String, ref: 'Person' }]
});
var Story = mongoose.model('Story', StorySchema);
var Person = mongoose.model('Person', PersonSchema);
No, it is not possible to have a mongodb document without '_id' , if you want to avoid duplication of ids, you can put uid in '_id' before saving the document.
If you keep uid seperately, '_id' will get autopopulated as ObjectId.
Here is a clear example of how to do it (https://mongoosejs.com/docs/populate.html):
Story.
find(...).
populate({
path: 'fans',
select: 'name -_id' // <---------------'-_id' exclude _id field.
}).
exec();

Adding a list of objectIds to an array in a collection (ref)

I have a user and doctor collection/model. here's the file it is in
module.exports = function(mongoose){
var User = mongoose.Schema({
username: {type : String, unique : true},
password : String,
nameOfOrg : String,
workers : [],
doctors : [{type : mongoose.Schema.Types.ObjectId, ref : Doctor}],
patients : [{type : mongoose.Schema.Types.ObjectId, ref : Patient}]
})
var Doctor = mongoose.Schema({
name : String,
address : String,
parentOrg :[{type : mongoose.Schema.Types.ObjectId, ref : User}],
patients :[{type : mongoose.Schema.Types.ObjectId, ref : Patient}]
})
var Patient = mongoose.Schema({
name : String,
DOS : String,
parentOrg :[{type : mongoose.Schema.Types.ObjectId, ref : User}],
parentDoctor : [{type : mongoose.Schema.Types.ObjectId, ref : User}]
})
var models = {
User : mongoose.model("user", User),
Doctor : mongoose.model("doctor", Doctor),
Patient : mongoose.model("patient", Patient)
};
return models;
}
I have a route handler like doctor/:name every time I go there I put the param in the db so doctor/doc1 adds doc1 to the name field in the doctors collection. Well every time that happens Im trying to add the objectId of this newly created document to the doctors array in the users collection. I think that is what you are supposed to do to use populate in the future to keep data separate in different collections then bring the appropriate parts together. Any ways I'm having problems adding the doctors ID to the doctors array in the users collection when I go to the route. here is the routing app.js code
mongoose.connect("mongodb://localhost/pop3");
var models = require("./db")(mongoose);
app.get("/:name", function(req, res){
var user1 = new models.User({"username": req.params.name})
user1.save(function(err, doc){
if(err){
console.log(err)
}
console.log(doc)
res.send(doc)
app.locals.user = doc
})
})
app.get("/doctor/:name", function(req, res){
var doctor = new models.Doctor({"name" : req.params.name, "parentOrg" : app.locals.user._id})
// app.locals.user.doctors.push(app.locals.user._id)
models.User.findOne({"username": app.locals.user.username}).push(doctor._id)
doctor.save(function(err, doc){
if(err){
console.log(err)
}
console.log(doc)
res.send(doc)
})
});
I tried to folow the mongoose doc on populate they show to do a similar thing I think by demoing this:
aaron.stories.push(story1);
aaron.save(callback);
I guess arron will be my user that was just saved.
stories will be the doctors field and
story1 will be the doctor.
I just couldn't do it
EDIT:: I get this error in the browser window:
TypeError: models.User.findOne(...).push is not a function

Node.js find document by an element in array

I have a user schema with the following structure:
new Schema({
email : String,
password : String,
shoppingCart : [{type : Schema.Types.ObjectId, ref : 'Product'}]
});
and I also have a product schema as follows:
new Schema({
title : String,
description : String,
vendorId : String,
stock : Number
});
How could I search for the users which have a specific product within their shopping carts?
I tried both
UserModel.find({shoppingCart : product._id})...
and
UserModel.find({'shoppingCart._id' : product._id})....
but unfortunately it does not work. Any ideas? Thanks.
Have you tried...
UserModel.find({shoppingCart : product})
If you use the actual object within the query, it will hydrate itself into ID and runs the search based on that object ID.
Since you have a Schema type as "Schema.Types.ObjectId" if you run
UserModel.find({shoppingCart : product._id})
It will search against product._id as "String" not ObjectId.

Categories