How to use $addToSet in Mongoose with array of ObjectId? - node.js

In MongoDB collection, I need to add unique ids only for a array field of a document. I am using findByIdAndUpdate of Mongoose. Can I pass the value as id or I need to pass the value as ObjectId() in the update body.
Should it be 1 or 2?
1.
await User.findByIdAndUpdate(userId, { $addToSet: { roleIds: roleId } })
await User.findByIdAndUpdate(userId, { $addToSet: { roleIds: mongoose.Types.ObjectId(roleId) } })
The schema is as follows:
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
roleIds: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Role",
},
],
});
module.exports = mongoose.model("User", UserSchema);

whats your schema , if in your schema you defined roleIds element you must use 1

Related

Table Reference In Mongodb

I have two models 1 : UserSchema & 2 :todo
const userSchema = new mongoose.Schema {
name: String,
email: String,
todo: { desc }
}
const todoSchema = new mongoose.Schema {
id: id,
desc : String
}
So my query is I want to get the todo of user based on id from on "todo" collection based on user id.
The my final result will be :
{
_id: new ObjectId("63a73aded28fd96ab66a9f30"),
name: 'world',
email: 'world#gmail.com',
todo: [],
__v: 0,
}
How to get the todo data using mongodb?
MongoDB, or document databases in general, are different from relational database (like MySQL, PostgreSQL, etc.) as MongoDB doesn't support relations natively as relational databases do. Yet, mongoose (the ODM for MongoDB in node.js) and its models has a way to represent relations using the model reference. So, your models are going to be:
const userSchema = new mongoose.Schema{ name: String, email: String, todo: { type: Schema.Types.ObjectId, ref: 'Todo' } }
const todoSchema = new mongoose.Schema{ desc : String }
const User = mongoose.model('User', userSchema);
const Todo = mongoose.model('Todo', todoSchema);
And to get the user and populate their todo, you can do the following query in mongoose
const user = await User.findById(id).populate('todo')
I also suggest you go through this page on mongoose documentation on populate function https://mongoosejs.com/docs/populate.html
To do the query directly on MongoDB without mongoose, you can do the following query using aggregates.
db.users.aggregate([
{
"$match": {
"_id": 1
}
},
{
"$lookup": {
"from": "todos",
"localField": "todo",
"foreignField": "_id",
"as": "todo"
}
}])

Saving ObjectId as reference dose not work in NodeJs MongoDB

I am going to store a category id to an item.
I do the below .save() function to add a new row.
const myNewItem = {
categoryId = ObjectId("5fc0a6e58dc3892120595384"),
title = "Apple"
}
var myNewItemSave = await new Item(myNewItem).save();
However, when I check my database record
_id: ObjectId("..."),
categoryId: "5fc0a6e58dc3892120595384",
title: "Apple"
The categoryId is not saved as ObjectId.
I want to save it as ObjectId is because I am going to query some lookup aggregation like this:
https://mongoplayground.net/p/50y2zWj-bQ6
so I have to make localField and foreignField are the same type as ObjectId.
It happen becuse your schema don't allow category id as mongoose objectId it is string
const schema = new Mongoose.Schema(
{
title: { type: String },
categoryId: { type: Mongoose.Schema.ObjectId, ref: 'categories' }
},
{ timestamps: true, versionKey: false }
)
export default Mongoose.model('Items', schema)
Import Item model and save will convert string into mongoose objectId
const myNewItem = {
categoryId: "5fc0a6e58dc3892120595384",
title: "Apple"
}
const myNewItemSave = await Item.create(myNewItem);
#Ashok defines a type in the model, if you dont want to modify the model you can "convert" the id with mongoose.Types.ObjectId, eg:
const mongoose = require('mongoose')
const myNewItem = {
categoryId = mongoose.Types.ObjectId("5fc0a6e58dc3892120595384"),
title = "Apple"
}

Join two collection in mongoose using nodejs

I want to join two collection in MongoDB. I have two collections 1. student 2. course.
student collection:
course collection
I tried some code but that is not working.
This is my code
student.js
router.get('/student/:id', function(req, res){
Student.find({_id:req.params.id}, function(err, user){
res.send(user);
})
})
This is Schema:
student.js
const mongoose = require('mongoose');
let StudentSchema = mongoose.Schema({
name:{
type: String
},
email:{
type: String
},
phone:{
type: String
},
password:{
type: String
},
course :[{
type: mongoose.Schema.Types.ObjectId,
ref: 'Course'
}]
}, { collection: 'student' });
const Student = module.exports = mongoose.model('Student', StudentSchema);
course.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let CourseSchema = mongoose.Schema({
student_id:{
type: String
},
course_name:{
type: String
},
course_cost:{
type: String
}
}, { collection: 'course' });
const Course = module.exports = mongoose.model('Course', CourseSchema);
Result:
you need to query like this :
findOne : to find single document (return object {} )
find : to find multiple documents (return array [])
Student.findOne({_id:req.params.id}, function(err, student){
if(!err && student){
Courses.find({student_id:req.params.id},function(error,courses){
if(!error && courses){
student.courses=courses;
}else{
student.courses=[];
}
res.send(student);
});
}
});
currently you are getting course :[] , because there is no field found in students collection , as you can see in photo-1
you need to set course :["coures_id1","coures_id2"] while inserting a document in student collection.
then you can use mongoose populate to populate course into students
Student.findOne({_id:req.params.id}).populate('course').exec(function(err,student){
res.send(student);
});
so after than no need to store , student_id field in courses collection , as you are getting from students collection.

Mongoose - query to get data from multiple collections

I want to get query of the mongoose in nodejs application as describe below out put.
user.js, comment.js and post.js are the model files I used.
user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var userSchema = new Schema({
nick_name:{type:String},
email: {
type: String,
trim: true,
required: '{PATH} is required!',
index: true,
},
},{ collection: 'user'});
var User = mongoose.model('User', userSchema);
module.exports = User;
comment.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var commentSchema = new Schema({
comment: type:String,
user_id:{
type:Schema.Types.ObjectId, ref:'User'
},
is_active :1
},{ collection: 'comment'});
post.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var postSchema = new Schema({
post: type:String,
user_id:{
type:Schema.Types.ObjectId, ref:'User'
},
is_active :1
},{ collection: 'post'});
wants to get out put as follows:
{
"nick_name":"prakash",
"email":"prakash#mailinator.com",
"comments":[
{
"comment":"this is a comment text1",
"is_active":1,
},
{
"comment":"this is a comment text2",
"is_active":1,
}
],
"posts":[
{
"post":"this is a post text1",
"is_active":1,
},
{
"post":"this is a post text2",
"is_active":1,
},
{
"post":"this is a post text3",
"is_active":1,
},
]
}
dependencies
"express" => "version": "4.7.4",
"mongoose" => "version": "4.4.5",
"mongodb" => "version": "2.4.9",
"OS" => "ubuntu 14.04 lts 32bit",
if query is not possible ,please suggests me a proper mongoose plugn.
but I don't want to any changes in user.js file and its userSchema object.
There are no 'joins' in Mongo. But what you would do is change your User Schema to store the ObjectId's of the Comment and Post documents in an array of your User. Then use 'populate' when you need the data with the user.
const userSchema = new Schema({
nick_name:{type:String},
email: {
type: String,
trim: true,
required: '{PATH} is required!',
index: true,
},
comments: [{ type: Schema.Types.ObjectId, ref:'Comment' }],
posts: [{ type: Schema.Types.ObjectId, ref:'Post' }]
}, {timestamps: true});
mongoose.model('User', userSchema);
Your query would then look something like this:
User.find()
.populate('comments posts') // multiple path names in one requires mongoose >= 3.6
.exec(function(err, usersDocuments) {
// handle err
// usersDocuments formatted as desired
});
Mongoose populate docs
It is possible .you should use aggregation.
it should work.
Initiate the variable
var mongoose = require('mongoose');
var userCollection = require('./user');//import user model file
var resources = {
nick_name: "$nick_name",
email: "$email"};
userCollection.aggregate([{
$group: resources
}, {
$lookup: {
from: "Comments", // collection to join
localField: "_id",//field from the input documents
foreignField: "user_id",//field from the documents of the "from" collection
as: "comments"// output array field
}
}, {
$lookup: {
from: "Post", // from collection name
localField: "_id",
foreignField: "user_id",
as: "posts"
}
}],function (error, data) {
return res.json(data);
//handle error case also
});
Of course it is possible, you just have to use populate, let me tell you how:
Import your schemas
var mongoose = require('mongoose');
var userSch = require('userSchema');
var postSch = require('postSchema');
var commSch = require('commentSchema');
Init all the necessary vars
var userModel = mongoose.model('User', userSch);
var postModel = mongoose.model('Post', postSch);
var commModel = mongoose.model('Comment', commSch);
And now, do the query
postModel.find({}).populate('User')
.exec(function (error, result) {
return callback(null, null);
});
commModel.find({}).populate('User')
.exec(function (error, result) {
return callback(null, null);
});
This way you get the user inside of your comment and your post, to get the post and comments inside of your user, you have to do 3 queries, one for the user, one for the comments and one for the post, and mix all together
you can consider using populate virtual with both comments and posts like docs (https://mongoosejs.com/docs/populate.html#populate-virtuals) in model User. And using like this:
User.find({filter}).populates('virtualComments').populate('virtualPosts')
Adding on to the answers above: What if we only want a few specific fields returned for the populated documents? This can be accomplished by passing the usual field name syntax as the second argument to the populate method:
Story.
findOne({ title: /casino royale/i }).
populate('author', 'name'). // only return the Persons name
exec(function (err, story) {
if (err) return handleError(err);
console.log('The author is %s', story.author.name);
// prints "The author is Ian Fleming"
console.log('The authors age is %s', story.author.age);
// prints "The authors age is null"
});
Reference: https://mongoosejs.com/docs/populate.html#field-selection
Use aggregate to do your work in a single query which is almost like a join.
var mongoose = require('mongoose');
var userModel = require('./user');//import user model file
let result = await userModel.aggregate([
{
$match: {
user: mongoose.Types.ObjectId(req.body.user_id),//pass the user id
}
},
{
$lookup: {
from: "comments",//your schema name from mongoDB
localField: "_id", //user_id from user(main) model
foreignField: "user_id",//user_id from user(sub) model
pipeline: [
{
$project:{ //use to select the fileds you want to select
comment:1, //:1 will select the field
is_active :1,
_id:0,//:0 will not select the field
}
}
],
as: "comments",//result var name
}
},
{
$lookup: {
from: "post",//your schema name from mongoDB
localField: "_id", //user_id from user(main) model
foreignField: "user_id",//user_id from user(sub) model
pipeline: [
{
$project:{//use to select the fileds you want to select
post:1,//:1 will select the field
is_active :1,
_id:0,//:0 will not select the field
}
}
],
as: "posts",//result var name
}
},
{
$project:{//use to select the fileds you want to select
nick_name:1,//:1 will select the field
email:1,
_id:0,//:0 will not select the field
comments:1,
posts:1
}
}
])

Mongoose $push into array adding _id [duplicate]

If you have subdocument arrays, Mongoose automatically creates ids for each one. Example:
{
_id: "mainId"
subDocArray: [
{
_id: "unwantedId",
field: "value"
},
{
_id: "unwantedId",
field: "value"
}
]
}
Is there a way to tell Mongoose to not create ids for objects within an array?
It's simple, you can define this in the subschema :
var mongoose = require("mongoose");
var subSchema = mongoose.Schema({
// your subschema content
}, { _id : false });
var schema = mongoose.Schema({
// schema content
subSchemaCollection : [subSchema]
});
var model = mongoose.model('tablename', schema);
You can create sub-documents without schema and avoid _id. Just add _id: false to your subdocument declaration.
var schema = new mongoose.Schema({
field1: {
type: String
},
subdocArray: [{
_id: false,
field: { type: String }
}]
});
This will prevent the creation of an _id field in your subdoc.
Tested in Mongoose v5.9.10
Additionally, if you use an object literal syntax for specifying a sub-schema, you may also just add _id: false to supress it.
{
sub: {
property1: String,
property2: String,
_id: false
}
}
I'm using mongoose 4.6.3 and all I had to do was add _id: false in the schema, no need to make a subschema.
{
_id: ObjectId
subDocArray: [
{
_id: false,
field: "String"
}
]
}
You can use either of the one
var subSchema = mongoose.Schema({
//subschema fields
},{ _id : false });
or
var subSchema = mongoose.Schema({
//subschema content
_id : false
});
Check your mongoose version before using the second option
If you want to use a predefined schema (with _id) as subdocument (without _id), you can do as follow in theory :
const sourceSchema = mongoose.Schema({
key : value
})
const subSourceSchema = sourceSchema.clone().set('_id',false);
But that didn't work for me. So I added that :
delete subSourceSchema.paths._id;
Now I can include subSourceSchema in my parent document without _id.
I'm not sure this is the clean way to do it, but it work.
NestJS example for anyone looking for a solution with decorators
#Schema({_id: false})
export class MySubDocument {
#Prop()
id: string;
}
Below is some additional information from the Mongoose Schema Type definitions for id and _id:
/**
* Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field
* cast to a string, or in the case of ObjectIds, its hexString.
*/
id?: boolean;
/**
* Mongoose assigns each of your schemas an _id field by default if one is not passed into the Schema
* constructor. The type assigned is an ObjectId to coincide with MongoDB's default behavior. If you
* don't want an _id added to your schema at all, you may disable it using this option.
*/
_id?: boolean;

Resources