mongoose populate field without ref option - node.js

I have 3 schema:
var User1Schema = new Schema({
name: {type:String , trim: true, required: true },
status: {type:Number, required: true, default:1}
},{collection:"user_1"});
,
var User2Schema = new Schema({
name: {type:String , trim: true, required: true },
email: {type: String, required: true, index: {unique: true} },
status: {type:Number, required: true, default:1}
},{collection:"user_2"});
and
var ConversationSchema = new Schema( {
participants: [{user:{type: ObjectId, required: true}],
creator: {type: ObjectId, required: true},
status: { type:Number, required: true, default:1 }
}, { collection:"conversation" } );
In ConversationSchema I have creator field whic has now ref option, because it can be type of User1Schema or User2Schema.
How can I populate creator field using mongoose

Conversation.find().populate('creator', null, 'User2').exec(callback)
The docs aren't clear. Will update soon.
Also, the syntax for this will be simpler in v3.6.

As of 27th March 2019 the way to do so is:
1) Setting the field you want to populate as a ObjectId without providing a ref.
var eventSchema = new Schema({
name: String,
// The id of the corresponding conversation
// Notice there's no ref here!
conversation: ObjectId
});
2) While making the query pass the model for the schema along with the query:
Event.
find().
populate({ path: 'conversation', model: Conversation }).
exec(function(error, docs) { /* ... */ });
REF: https://mongoosejs.com/docs/populate.html#cross-db-populate

Related

Mongoose schema with field of an array of other schemas

first of all, I am a beginner and currently, I am working on a social media blog type.
Now, I have my userSchema and postSchema models:
USER MODEL
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please insert your name'],
},
email: {
type: String,
required: [true, 'Please insert your email'],
unique: true,
lowercase: true, //transform into lowercase / not validator
validate: [validator.isEmail, 'Please provide a valid email'],
},
avatar: {
type: String,
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
password: {
type: String,
required: [true, 'Please provide a password'],
minLength: 8,
select: false,
},
passwordConfirm: {
type: String,
required: [true, 'Please confirm your password'],
validate: {
validator: function (el) {
return el === this.password;
},
message: 'Passwords are not the same',
},
},
passwordChangedAt: Date,
posts: [] // ???????????????
});
POST MODEL
const postSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'A post must have a title'],
},
author: {
type: String,
required: [true, 'A post must have a title'],
},
likes: {
type: Number,
default: 10,
},
comments: {
type: [String],
},
image: {
type: String,
},
postBody: {
type: String,
required: [true, 'A post must contain a body'],
},
createdAt: {
type: Date,
default: Date.now(),
select: false,
},
});
Now I don't know if it's the best approach but I was thinking of having a field in userSchema with the type of an array of postSchema so I will have for each user their own posts created. Can I do that? If not how I can achieve that?
Should I use search params fields to filter posts by the author? I am really confused how I should approach this situation. Thank you guys
Check out this example
const UserSchema = new mongoose.Schema({
username: String,
posts: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}]
})
const PostSchema = new mongoose.Schema({
content: String,
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
})
const Post = mongoose.model('Post', PostSchema, 'posts');
const User = mongoose.model('User', UserSchema, 'users');
module.exports = { User, Post };
Credit: https://medium.com/#nicknauert/mongooses-model-populate-b844ae6d1ee7

How to update a nested array in mongoose?

I am new to the backend and trying to learn by building some stuff but unfortunately, I got stuck.
I want to know if I can update a nested array of objects in Users Schema using Mongoose in an efficient and elegant way.
Users Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: {
type: String,
required: true
},
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true
},
gender: {
type: String,
required: true
},
password: {
type: String,
required: true
},
friends: [{}],
notifications: []
}, {timestamps: true});
module.exports = User = mongoose.model('user', UserSchema);
In the friends' field, I stored friend request with the status of pending
I want if the user whose the request was sent to, hits an endpoint, to accept the request
by changing the status from pending to success.
This is how a friend request was stored:
friendRequest = {
_id: req.user.id,
status: 'pending',
sentByMe: false,
new: true,
inbox: []
}
Thanks as you help me out!!! 🙏🙏🙏
You should first create an additional friendRequest and inbox schemas like this:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const InboxSchema = new Schema({
user_id: {
type: String,
required: true
},
from_id: {
type: String,
required: true
},
message: {
type: String,
required: true
},
the_date_time: {
type: Date,
required: true
}
});
mongoose.model('Inbox', InboxSchema);
const FriendRequestSchema = new Schema({
user_id: {
type: String,
required: true
},
status: {
type: String,
required: true
},
sentByMe: {
type: Boolean,
required: true,
unique: true
},
inbox: [InboxSchema]
})
mongoose.model('FriendRequests', FriendRequestSchema);
and update your Users schema:
const UserSchema = new Schema({
name: {
type: String,
required: true
},
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true
},
gender: {
type: String,
required: true
},
password: {
type: String,
required: true
},
friends: [FriendSchema],
notifications: [FriendRequestSchema]
}, {timestamps: true});
And then use the friendRequest object
friendRequest = {
_id: req.user.id,
status: 'pending',
sentByMe: false,
new: true,
inbox: []
}
to update the Users collection
Users.update({ _id: user_id }, { $push: { notifications: friendRequest } });
Whenever you have arrays of objects within collections, its best to define additional schemas. You should also consider adding indexes to your collection schemas.
Update:
A FriendSchema would look like this:
const FriendsSchema = new Schema({
friend_id: {
type: String,
required: true
},
friend_name: {
type: String,
required: true
},
friendship_made: {
type: Date,
required: true
}
// you have to define FriendSchema before you define Users since you
// now reference [FriendSchema] in UserSchema
mongoose.model('Friends', FriendSchema);
And so is personA friends with personB?
Users.findOne({ "_id": personA.id, "friends.friend_id": personB.id});

mongoose index creation issue after drop collection

I had created one index on the collection and it was created. But after the drop that collection and when I restarted node server then collection are created again but the index isn't can anybody suggest what is the cause?
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const validator = require('validator')
const FeatureSchema = new Schema({
name:{
type: String,
trim: true,
required: "Please enter a feature name!"
},
slug: String,
description: {
type: String,
trim: true
},
author: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: 'You must supply an author'
},
productID: {
type: mongoose.Schema.ObjectId,
ref: 'Product',
required: 'You must supply an product'
}
},{
timestamps: {
createdAt: 'createdDate',
updatedAt: 'updatedDate'
}
});
// Define our indexes
FeatureSchema.index({
name: 'text'
});
module.exports = mongoose.model('Feature', FeatureSchema);
try to stop mongodb service and restart it again.

MongoDB reference using Mongoose

I'm trying to reference one model to other with Mongoose doing this:
//product model
var productSchema = new mongoose.Schema({
id: {type: Number, unique: true, required: 'product_id'},
sku: {type: String, unique: true, required: 'product_sku'},
name: {type: String, required: 'product_name'},
short_description: {type: String},
details: [{
type: String
}],
categories: [{
type: String
}, {min: 1, max: 5}],
});
module.exports = mongoose.model('products', productSchema);
//order model
var orderSchema = new mongoose.Schema({
id: {type: String, unique: true},
date: {type: Date, default: Date.now},
products_sold: {type: mongoose.Schema.Types.ObjectId, ref: 'products'},
});
module.exports = mongoose.model('orders', orderSchema);
I've already created a product with id: 1:
"_id": ObjectId('55a1ce54b058d142051ca61d'),
"id": 1,
"sku": "p0001",
"name": "test1",
"short_description": "test",
//etc
If I do:
var newOrder = new Order({
id: 'order001',
products_sold: 1,
//etc
});
newOrder.save(function(err, data) {
if(err){
console.log(err);
}else{
console.log(data);
}
});
It fails with error:
message: 'orders validation failed',
name: 'ValidationError',
errors:
{ products_sold:
{ [CastError: Cast to ObjectID failed for value "1" at path "products_sold"]
How can I have products_sold in my order filled with product data that has id 1?
Thanks in advance
You are specifying that the products_sold field is of type ObjectId, but you are trying to set a Number as its value. In order to store references to documents in other collections, you must store the _id of the referenced document, not an arbitrary id field that you have in the Schema. You will either need to change the type for _id in your products database to use integers (and then manually specify what that _id field's value is when creating a new product), or you need to store the product's _id value in the order document's product_sold field.
Response to comment:
Here is updated code for specifying your own unique _id field:
Product Model
var productSchema = new mongoose.Schema({
_id: {type: Number, unique: true, required: 'product_id'},
sku: {type: String, unique: true, required: 'product_sku'},
name: {type: String, required: 'product_name'},
short_description: {type: String},
details: [String],
categories: [{
type: String
min: 1,
max: 5
}],
}, {_id: false});
module.exports = mongoose.model('products', productSchema);
In the productSchema, you will see that there is an _id specified, and it is set to required, and has no default value. This should make it so that Mongo will use whatever Number you specify as it's _id.
You need to pass objectId of the product in product_sold , rather than number.

Is it possible to have an array of alternative subdocument schemas in mongoose

Is it possible to have an array of alternative subdocument schemas in mongoose?
For instance, take this made up scenario: if i have a main schema for a person and i have a field called vehicles of type array, i want to be able to define sub document schemas, but for each vehicle type (car, motorbike, bike, bus etc etc ) :
var car = new mongoose.Schema({
make: { type: String, required: true, default: 'Ford', trim: true },
model: { type: String, trim: true },
wheels: Number,
});
var motorbike = new mongoose.Schema({
make: { type: String, required: true, default: 'Honda', trim: true },
model: { type: String, trim: true },
seats: Number,
});
var bike = new mongoose.Schema({
make: { type: String, required: true, default: 'MuddyFox', trim: true },
model: { type: String, trim: true },
lights: Number,
});
var bus = new mongoose.Schema({
make: { type: String, required: true, default: 'MAN', trim: true },
model: { type: String, trim: true },
dents: Number,
});
/* person */
var person = new mongoose.Schema({
name: { type: String, required: true, default: 'Joe Bloggs', trim: true },
timeCreated: { type: Date, required: true, default: Date.now },
vehicles: [car, motorbike, bike, bus] /* this is what i want to do */
});
db.model('person', person);
Note the person schema.
So that person can have any of those vehicles, but the data for each vehicle is still validated against it's own schema.
Thanks in advance
There is a mongoose plugin called mongoose-schema-extend, that will allow you to enable Schema Inheritance for Mongoose, and basically I think, you are looking for this functionality.
Please check the github project:
https://github.com/briankircho/mongoose-schema-extend
You can create a Vehicle schema, add it as an array to the Person schema, and extend the Vehicle for your different type of vehicles.
I couldn't find any examples of multiple embedded documents being passed as an array.
Perhaps you could rejig things like this:
var car = new mongoose.Schema({
make: { type: String, required: true, default: 'Ford', trim: true },
model: { type: String, trim: true },
wheels: Number,
});
var motorbike = new mongoose.Schema({
make: { type: String, required: true, default: 'Honda', trim: true },
model: { type: String, trim: true },
seats: Number,
});
var bike = new mongoose.Schema({
make: { type: String, required: true, default: 'MuddyFox', trim: true },
model: { type: String, trim: true },
lights: Number,
});
var bus = new mongoose.Schema({
make: { type: String, required: true, default: 'MAN', trim: true },
model: { type: String, trim: true },
dents: Number,
});
var transport = new mongoose.Schema({
car: [car],
motorbike: [motorbike],
bike: [bike],
bus: [bus]
});
/* person */
var person = new mongoose.Schema({
name: { type: String, required: true, default: 'Joe Bloggs', trim: true },
timeCreated: { type: Date, required: true, default: Date.now },
vehicles: [transport]
});
db.model('person', person);
If that doesn't work, then as described in this SO question you can use mongoose populate. This method is now more effective following the addition of deep population was added in Mongoose Deep 3.6.
So you could do soemthing like:
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);
There's also a mongoose plugin to aid with this functionality called mongoose-deep-populate which is very up-to-date.

Resources