Mongoose one-to-many - not quite sure how to implement it - node.js

I'm relatively new to Mongoose (2 days at it) and I want to make a one-to-many relationship, as in one person can come from one country, one country has many people.
So, this is what I've got:
var userSchema = new Schema({
name: String,
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
country: {
type: Schema.Types.ObjectId,
ref: 'Country'
}
});
var User = mongoose.model('Person', userSchema);
var countrySchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
created_at: Date,
updated_at: Date,
people: [{
type: Number,
ref: 'User'
}]
});
var Country = mongoose.model('Country', countrySchema);
var UK = new Country({
name: 'UK'
});
usa.save(function(err) {
var user = new User({
username: 'James',
password: 'Bond',
country: UK._id
});
user.save(function(err) {
});
});
Now I have two questions: 1) I've seen that ref can sometimes be an ObjectId or just a number - what's the differences? 2) when saving the data, in my case, I saved country to a person (by _id), how do I save a person to a country? Should I update the instance of the model?
Thanks
UPDATE:
since this question has been marked as a duplicate, let me rephrase the question: consider the official example in this link: http://mongoosejs.com/docs/populate.html
The idea is that one person has many stories, and one story has one author (person). So, the saving would be as follows:
var aaron = new Person({ _id: 0, name: 'Aaron', age: 100 });
aaron.save(function (err) {
if (err) return handleError(err);
var story1 = new Story({
title: "Once upon a timex.",
_creator: aaron._id // assign the _id from the person
});
story1.save(function (err) {
if (err) return handleError(err);
// thats it!
});
});
That's from the official documentation - my question is, where or how do we save story1 to the Author? Author is created before the Story, so, shouldn't the Author be updated with story1._id???
UPDATE 2:
I figured out that if I use only type: Schema.Types.ObjectId and never type: Number, that I can do just this:
var aaron = new Person({ _id: 0, name: 'Aaron', age: 100 });
var story1 = new Story({
title: "Once upon a timex.",
_creator: aaron._id // assign the _id from the person
});
aaron.stories.push(story1._id);
aaron.save(function (err) {
if (err) return handleError(err);
});
story1.save(function (err) {
if (err) return handleError(err);
// thats it!
});
This actually works in a dummy example... are there any problems if there were too many posts in a request that IDs could have get lost/duplicated? What is the shortcoming of this approach?

1) I've seen that ref can sometimes be an ObjectId or just a number - what's the differences?
Please refer to this question Why do they use an ObjectId and a Number in the Mongoose Population example?
where or how do we save story1 to the Author
aaron.save(function (err) {
if (err) return handleError(err);
var story1 = new Story({
title: "Once upon a timex.",
_creator: aaron._id // assign the _id from the person
});
story1.save(function (err) {
if (err) return handleError(err);
// save id of story1 into person here, also you should use `update` operation with `$push` operator.
aaron.stories.push(story1._id);
aaron.save(function(err){
if (err)
handleError(err);
else
console.log('save person successfully...');
})
});
});
The results
> db.stories.find()
{ "_id" : ObjectId("56f72f633cf1e6f00159d5e7"), "title" : "Once upon a timex.", "_creator" : 0, "fans" : [ ], "__v" : 0 }
> db.people.find()
{ "_id" : 0, "name" : "Aaron", "age" : 100, "stories" : [ ObjectId("56f72f633cf1e6f00159d5e7") ], "__v" : 1 }

Related

Mongoose Model ObjectId References Not Working

I'm working with Mongoose models and references. I've been using the code from mongoose's website where it talks about the populate method and references. I am trying to have it save the respective "referenced" ids in both models. It is only saving the reference ids in the story model. Here is the code:
Update: Added schemas at the top to help:
var personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
author: { type: Schema.Types.ObjectId, ref: 'Person' },
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
(end of schemas)
var author = new Person({
_id: new mongoose.Types.ObjectId(),
name: 'Ian Fleming',
age: 50
});
author.save(function (err) {
if (err) return handleError(err);
var story1 = new Story({
title: 'Casino Royale',
author: author._id // assign the _id from the person
});
story1.save(function (err) {
if (err) return handleError(err);
// thats it!
});
});
When you run this code, it generates this in mongo:
db.people.find()
{ "_id" : ObjectId("5be0a37f1dd61a343115e2c8"), "stories" : [ ], "name" : "Ian Fleming", "age" : 50, "__v" : 0 }
db.stories.find()
{ "_id" : ObjectId("5be0a37f1dd61a343115e2c9"), "title" : "Casino Royale", "author" : ObjectId("5be0a37f1dd61a343115e2c8"), "__v" : 0 }
It appears to not be storing any ids in the people collection within "stories." Wouldn't you want to save the stories ids in the people collection as well?
I tried to modify the code to make it work with (moved the author save function, until after the story id is set):
var author = new Person({
_id: new mongoose.Types.ObjectId(),
name: 'Ian Fleming',
age: 50
});
var story1 = new Story({
_id: new mongoose.Types.ObjectId(),
title: 'Casino Royale',
author: author._id // assign the _id from the person
});
author.stories = story1._id;
author.save(function (err) {
if (err) return handleError(err);
story1.save(function (err) {
if (err) return handleError(err);
// thats it!
});
This gives me an author undefined.
Mongo wouldn't automatically add to the Person "stories" field just because you added a Story object.
You don't really need to store the story ids in Person objects anyway, as you can always get a list of stories by an author with
db.stories.find({author: <id>})
Storing in both places would create redundant information and you'd have to pick one to be the truth in the case of a mismatch. Better to not duplicate, methinks.
UPDATE:
References appear to help you populate referenced fields in queries automatically. According to this post you can retrieve an author and their stories like this:
db.persons.find({_id: <id>}).populate('stories')
Haven't personally used this but it looks pretty handy.
Mongoose docs for populate: https://mongoosejs.com/docs/populate.html

Mongoose Subdocument array pushing

My scenario is if person1 accepting person2 deal means..the person1_id will save inside that person2 particular deal field accepted,i have tried the code it was working perfectly if a accepted user(person2) has one deal but in case of more than one deal it was updating but deleting other deals (i.e,the suppose the person2 having 3 deals means if person1 accepting 3rd deal the accepted user id was updating in 3rd deal and the 1st and 2nd deal was deleted).Anyone please help me how to save only the updated deal array
var incomingUser = req.user;//accepting user accesstoken in header(person1)
if(req.params.id){
var id = req.params.id;//deal id
console.log("DealId:"+id + "Acceptinguser:"+incomingUser.name);
User.findOne(
{
"deals": {
$elemMatch: {
_id: id
}
}
},
function(err, data){
console.log("Dealer:" +data.name);
console.log("deal:"+ data.deals);
if(err){
console.log("User not found");
res.send(new restify.ResourceNotFoundError('failed','Deal not found'));
return next();
}
var dealObj = _.filter(data.deals, { id: id })[0];
console.log("Deal Obj" + dealObj);
var acceptingUser = incomingUser;
console.log("accepting user:" +acceptingUser._id);
dealObj.accepted = acceptingUser._id;
console.log("accept id: "+ dealObj.accepted);
data.deals = dealObj;
console.log("data:"+ data.deals);
data.save(function (err, result){
console.log("Result:" + result);
if(err){
console.log("Internal error");
res.send(new restifyc.InternalError('failed','Error accepting'));
return next();
}
console.log("saved");
res.send(200,{user: result});
return next();
});
});
}
}
And my schema is
var dealSchema = new mongoose.Schema({
shopName: {type: String,required: true},
deal: {type: String,required: true},
price:{type: Number,required: true},
start:{type: Date,default: Date.now},
end:{type: Date},
expiry:{type: Date},
comments:{type: String},
accepted: {type:mongoose.Schema.Types.ObjectId, ref:'user'},//person1 _id
rejected: {type:mongoose.Schema.Types.ObjectId, ref: 'user'}
});
var userSchema = new mongoose.Schema({
name: { type: String,required: true},
phone: { type: Number, required: true,unique: true},
email:{type: String},
password: {type: String},
deals:[dealSchema]
}, {collection: 'user'});
mongoose.model('Deal', dealSchema);
mongoose.model('user', userSchema);
Yep in order to update specifically what you need you can use the <array>.$ for the specified position of the element:
User.update(
"deals": {
$elemMatch: {
_id: id
}
}, {
"$set": {
"deals.$" : {/*your deal data*/}
}
}, function(err, doc) {
});
More details on how to use the $ wildcard https://docs.mongodb.org/manual/reference/operator/update/positional/

Unable to insert populated document w/ mongoose?

I'm trying to populate a document field with referenced document with the right data and saving it to the database. However, I'm unable to insert a document correctly in the database, even though I had successfully saved the document.
**getting-started.js**
...
var personSchema = mongoose.Schema({
_id : Number,
name : String,
age : Number,
stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = mongoose.Schema({
_creator : { type: Number, ref: 'Person' },
title : String,
fans : [{ type: Number, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
var Johns = new Person({ _id: 15, name: 'Johns', age: 154});
Johns.save(function (err) {
if (err) {
console.log('Person Save error! %s', err);
}
var story1 = new Story({
title: "Once upon a timex??",
_creator: Johns._id // assign the _id from the person
});
story1.save(function (err) {
if (err) return handleError(err);
console.log('successful story save! %d', story1._creator);
});
});
Story
.find({ title: 'Once upon a timex######' })
.populate('_creator')
.exec(function (err, story) {
if (err) return handleError(err);
console.log("callback story: ", story);
story.save(function (err, story) {
if (err) return console.error(err);
console.log('successful save new story w/ populated _creator');
console.log('The story: creators name: %s', story._creator.name);
console.log('The story: creator field: %s', story._creator);
})
});
First time executing:
>>node getting-started.js
Following output:
>>opened conn to db
successful story save! 15
opened conn to db
{ fans: [],
__v: 0,
_creator: { stories: [], __v: 0, age: 154, name: 'Johns', _id: 15 },
title: 'Once upon a timex######',
_id: 56244cf796f73380353e803a }
successful save new story w/ populated _creator
The story: creators name: Johns
The story: creator field: { stories: [], __v: 0, age: 154, name: 'Johns', _id: 15 }
However, after commenting out the following code:
var Johns = new Person({ _id: 15, name: 'Johns', age: 154});
Johns.save(function (err) {
if (err) {
console.log('Person Save error! %s', err);
}
var story1 = new Story({
title: "Once upon a timex??",
_creator: Johns._id // assign the _id from the person
});
story1.save(function (err) {
if (err) return handleError(err);
console.log('successful story save! %d', story1._creator);
});
});
the next time I'm executing
Story
.find({ title: 'Once upon a timex######' })
.exec(function (err, story) {
if (err) return handleError(err);
console.log("callback story: ", story);
});
I'm unable to see my saved Story with the populated field of _creator, it still only has the person _id and not the person object:
{ fans: [],
__v: 0,
_creator: 15,
title: 'Once upon a timex######'
_id: 56244cf796f73380353e803a }

Mongoose - Better solution with appending additional information

I have two Schemas:
var ProgramSchema = new Schema({
active: Boolean,
name: String,
...
});
var UserSchema = new Schema({
username: String,
email: { type: String, lowercase: true },
...
partnerships: [{
program: { type: Schema.Types.ObjectId, ref: 'Program' },
status: { type: Number, default: 0 },
log: [{
status: { type: Number },
time: { type: Date, default: Date.now() },
comment: { type: String },
user: { type: Schema.Types.ObjectId, ref: 'User' }
}]
}]
});
Now I want to get all Program docs, but also append 'status' to each doc, to return if the program is already in a partnership with the logged in user.
My solution looks like this:
Program.find({active: true}, 'name owner image user.payments', function (err, p) {
if(err) { return handleError(res, err); }
})
.sort({_id: -1})
.exec(function(err, programs){
if(err) { return handleError(res, err); }
programs = _.map(programs, function(program){
var partner = _.find(req.user.partnerships, { program: program._id });
var status = 0;
if(partner){
status = partner.status;
}
program['partnership'] = status;
return program;
});
res.json(200, programs);
});
The req.user object contains all information about the logged in user, including the partnerships array.
To get this solution to work, I have to append
partnership: Schema.Types.Mixed
to the ProgramSchema.
This looks a bit messy and thats why I am asking for help. What do you think?
When you want to freely modify the result of a Mongoose query, add lean() to the query chain so that the docs (programs in this case) are plain JavaScript objects instead of Mongoose doc instances.
Program.find({active: true}, 'name owner image user.payments')
.lean() // <= Here
.sort({_id: -1})
.exec(function(err, programs){ ...
Then you can remove partnership from your schema definition. Your query will also execute faster.

Mongoose population ref

Hye!
This is an example of http://mongoosejs.com/docs
var personSchema = Schema({
_id : Number,
name : String,
age : Number,
stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
var storySchema = Schema({
_creator : { type: Number, ref: 'Person' },
title : String,
fans : [{ type: Number, ref: 'Person' }]
});
var Story = mongoose.model('Story', storySchema);
var Person = mongoose.model('Person', personSchema);
Story
.findOne({ title: 'Once upon a timex.' })
.populate('_creator')
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The creator is %s', story._creator.name);
// prints "The creator is Aaron"
})
And that is my codeMy '_creator' is not an array but an array of objects :
var storySchema = Schema({
_creator : [{
_id : { type: Number, ref: 'Person' },
quantity : Number
}],
...
});
And my request is :
Story
.findOne({ title: 'Once upon a timex.' })
.populate('_creator._id')
.exec(function (err, story) {
if (err) return handleError(err);
console.log('The creator is %s', story._creator.name);
// prints "The creator is Aaron"
})
But this solution 'populate('_creator._id')' doesn't work.
Have you got an idea?
Thank you!

Resources