I want to combine values from two collections to get data - node.js

There are user collections and bulletin collections. The bulletin collection has the author's uid. I want to give data to the username matching the uid in the User collection, not the author's uid, before giving the bulletin board's information to the client.
User Schema
var userSchema = new Schema({
"userId": String,
"userPw": String,
"userName": String,
});
var User = mongoose.model('user', userSchema);
Board Schema
var baseBoardSchema = new Schema({
title: String,
authorUid: String,
});
var freeBoardSchema = new Schema({
baseBoard : { type: baseBoardSchema, default: baseBoardSchema}
});
var freeBoard = mongoose.model('freeBoard', freeBoardSchema);
So I did below. But it generate error. How I do?
FreeBoard.aggregate()
.lookup({
from: User,
localField: 'baseBoard.authorUid',
foreignField: '_id',
as : 'users'
})
.exec(function(error, freeBoards) {
if(error) {
//error
}
});

Related

I can't populate data using MongoDB and Node

these are the two models that I have.
user.js
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
email: {
type: String,
},
passwordHashed: {
type: String,
},
role: {
type: String,
},
student: [{ type: mongoose.Schema.Types.ObjectId, ref: "Student" }],
});
const Users = mongoose.model("User", userSchema);
module.exports = Users;
student.js
const mongoose = require("mongoose");
const studentSchema = new mongoose.Schema({
email: {
type: String,
},
role: {
type: String,
},
});
const Students = mongoose.model("Student", studentSchema);
module.exports = Students;
My problem is I want to populate the data to user.js but when I am running this code:
const user = await Users.find().populate('student')
console.log(user)
It just returns me an empty array of the student. How can I fix this problem? Thank you in advance.
For any type population/join I all time use mongodb $lookup.
It's much powerful, flexible and covenant than using population.
According your userSchema, most probably you have store the _id of student document on userSchema's student array.
If I'm correct then you can follow this way:
await Users.aggregate([
{
"$lookup": {
"from": "students",
"localField": "student",
"foreignField": "_id",
"as": "studentList"
}
}
])
Though your student field of userSchema is objectId, then you don't further need to do anything, else you have to convert your localField as mongoose.Types.ObjectId(student)
here is an example: https://mongoplayground.net/p/fuw9Drld9M-

How to get user name from user id in React.js

I have created this schema with mongoose
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const QuestionSchema = new mongoose.Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
question:{
type:String
},
name:{
type:String,
},
answerd:[
{
user:{
type:mongoose.Schema.Types.ObjectId,
ref:'users'
}
}
]
})
module.exports = Question = mongoose.model('question',QuestionSchema);
In my global state (REDUX) I have the state
const initialState = {
questions:[],
question:null,
loading:true,
error:{}
};
The element questions store question object which contains the name of the user who made the question, the question itself, and people who have answered.
Some where in a .js file I can get the id of users who have answered by simply
question.buzzed.map(user=> <h1> {user._id} </h1>),
but how is possible to get this user name, I also have a schema for user which have attributes such as name, id, ... etc
You should use the populate method. It is like join in SQL for mongoose, because it connects your answer to the user collection. Based on your code, it could look like this :
Question.find({}).populate("answerd")
or something like this:
Question.find().populate({ path: 'answerd', select: 'username' });
For more information please read the populate documentation
U can use $lookup.
var aggregate = [
{
$unwind: "$answerd"
},
{
$lookup: {
from: "users",
localField: "user",
foreignField: "_id",
as: "user"
}
}
];
Questions.aggregate(aggregate, function(err, users) {
})

How to add schema to another schema array?

I have address schema and customer schema. I have a field address array inside my customer schema. I will be sending an adress model as my req body and customer id as req param. How can I save that adress to adresses array which is declared inside customer schema?
This is my Customer Schema
const customerSchema = mongoose.Schema ({
_id: mongoose.Schema.Types.ObjectId,
name: String,
phone_number: String,
password: String,
type:{type:String,enum:['admin','user']},
adresses:['Adress'],
orders:[{type: mongoose.Schema.Types.ObjectId, ref: 'Order'}]
});
This is my Adress Schema
const addressSchema= mongoose.Schema({
_id:mongoose.Types.ObjectId,
postalCode:Number,
city:String,
district:String,
neighborhood:String,
streetNumber:String,
no:Number,
buildingName:String,
apartmentNumber:Number,
floor:Number,
coordinates:{
latitude:Number,
longitude:Number
},
addressName:String,
customerId: {type: mongoose.Schema.Types.ObjectId,ref:'Customer'}
});
I could not figure out how am ı going to do this. I am finding the customer which I will going to push my address to like this.
This is how I get the specific customer
Customer.find({_id:req.params.customerId},(err,data)=>{
if(err) return next(err);
else{
//What I am going to do here?
}
});
First what type should I put inside the addresses array which is inside Customer Schema?
Second after finding the customer which I am going to add address to, what should I do? Mongoose 5.4.11 documentation was not enough for me. This link seemed what I needed but I did not figure out how to accomplish this problem.
https://mongoosejs.com/docs/subdocs.html
OK, so basically what You look for is: association. You need to establish a connection between User and Customer model.
We will say that Address belongs to the User and User reference Address object by for example id.
Consider an example:
const personSchema = Schema({
_id: Schema.Types.ObjectId,
name: String,
age: Number,
stories: [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});
const storySchema = Schema({
author: { type: Schema.Types.ObjectId, ref: 'Person' },
title: String,
fans: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
});
const Story = mongoose.model('Story', storySchema);
const Person = mongoose.model('Person', personSchema);
Now let's try to assign an author to particular created story:
const author = new Person({
_id: new mongoose.Types.ObjectId(),
name: 'Ian Fleming',
age: 50
});
author.save(function (err) {
if (err) return handleError(err);
const 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 define relation between Story and Person, it is easy to manipulate references between them.
In your case you should define a reference in models and then you are able to manipulate the fields:
Customer.findOne({_id:req.params.customerId}, function(error, customer) {
if (error) {
return handleError(error);
}
customer.address = newAddress; // set customer's address to the desired address
// console.log(customer.address.city);
});
Check doc for more.

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 - 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
}
}
])

Resources