Mongoose populate() - express

referring to Mongoose url populate example given at https://mongoosejs.com/docs/populate.html#checking-populated , there seems to be a two way relationship between both Schema. What if I only have 1 way relationship for example ( using same schema example, But Person schema does not Story ref )
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const personSchema = Schema({
name: String,
age: Number
});
const storySchema = Schema({
author: { type: Schema.Types.ObjectId, ref: 'Person' },
title: String
});
const Story = mongoose.model('Story', storySchema);
const Person = mongoose.model('Person', personSchema);
How can I return a GET Story output that looks like the following :
{
author :{
name: "Bla bla bla",
age: 30
}
title : "ABC Story"
}
I am always getting this at the moment :
{
author :34235245453
title : "ABC Story"
}

I am not sure whether this is a good practise, but you can achieve this as follows
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const personSchema = Schema({
name: String,
age: Number
});
const storySchema = Schema({
author: Schema.Types.ObjectId, //modified by just defining the type
title: String
});
const Story = mongoose.model('Story', storySchema);
const Person = mongoose.model('Person', personSchema);
when populating,
SOMETHING.find()
.populate({
'path':'author',
'model':'Person'
});

I think you are confusing populated() function and populate() function.
To be able to retrieve the author info of a story, we need to use populate like this:
router.get("/stories/:id", async (req, res) => {
const result = await Story.findById(req.params.id).populate("author");
res.send(result);
});
Let's say we have this person:
{
"_id": "5e3c63ba3a178830dc497a00",
"name": "Name1",
"age": 33,
"__v": 0
}
And this story by user:
{
"_id": "5e3c63e93a178830dc497a02",
"author": "5e3c63ba3a178830dc497a00",
"title": "Story1",
"__v": 0
}
The result will be like this when we send a get request to our route ( http://.../stories/5e3c63f33a178830dc497a02)
{
"_id": "5e3c63e93a178830dc497a02",
"author": {
"_id": "5e3c63ba3a178830dc497a00",
"name": "Name1",
"age": 33,
"__v": 0
},
"title": "Story1",
"__v": 0
}
To get all stories with author info, we can use find() method like this:
router.get("/stories", async (req, res) => {
const result = await Story.find().populate("author");
res.send(result);
});

Related

how to reference itself in mongoose schema?

I am trying to create profile of a user and in friends key I want to store array of objectId of other users. So how to reference itself
const mongoose = require("mongoose")
const userSchema = mongoose.Schema({
firstname:String,
lastname:String,
password:String,
email:{
type:String,
unique:true
},
birthday:Date,
creation_date:{
type:Date,
default:Date.now
},
gender:String,
profile_picture:{
type:String,
default:""
},
friends:[{
type:mongoose.Schema.Types.ObjectId,
ref:"userModel"
}]
})
const userModel = new mongoose.model("userModel", userSchema)
module.exports = userModel
By doing this, and sending request from postman i am getting empty array of friends
Fields
firstname
dhiran
lastname
sapkota
email
dhiransapkota9#gmail.com
password
thisisemail
birthday
2059-08-09
gender
male
friends
["6319b1bd2d4f0f0145662f3b", "6319ad712d4f0f0145662f35"]
Output I am getting is:
{
"success": true,
"msg": "user has been signed up",
"signupUser": {
"firstname": "dhiran",
"lastname": "sapkota",
"password": "$2a$10$rOaA3QlameBO4my8C9fmye73Zi9QxtPbi3X.XFpDNd7J8tBD3SbMq",
"email": "dhiransapkota999#gmail.com",
"birthday": "2059-08-09T00:00:00.000Z",
"gender": "male",
"profile_picture": "1662798667954-cv_image.png",
"friends": [],
"_id": "631c4b4c2d832a439c88720f",
"creation_date": "2022-09-10T08:31:08.072Z",
"__v": 0
}
}
I am using multipart/formdata so data is in this format.
route
router.post("/signup", upload.single("profile"), (req, res) => {
userControllerInstance.signup(req, res, profile_picture)
});
controller:
class UserController {
async signup(req, res, profile_picture) {
try {
const { firstname, lastname, email, password, birthday, gender, friends } =
req.body;
const response = await userModel.find({ email: email });
console.log(response);
if (response.length > 0) {
return res
.status(409)
.json({ success: false, msg: "email already exists" });
}
const hashedPassword = generateHash(password);
const signupUser = await userModel.create({
firstname,
lastname,
email,
password: hashedPassword,
birthday,
gender,
profile_picture,
friends
});
return res.json({
success: true,
msg: "user has been signed up",
signupUser,
});
} catch (error) {
return res.json(error);
}
}
}

How to get data from related table in 1:N relationship when login in Sequelize?

I have two tables, users and recyclings, which are 1:N relationship (one user have many recyclings, one recycling belongs to one user. When login, I also want to fetch user's recyclings, but it doesn't work when I try to use include. I get response, but without recyclings array.
Response should be like this:
{
"id": 2,
"name": "Bernt",
"surname": "Bjornson",
"vat": "78965412354",
"email": "bernt#mail.sv",
"password": "$2a$08$N02C/YMq0MO.b.eiEZVAc.7cmdb49X1yEPKrFy.8bWU9TsrGgcdfG",
"createdAt": "2022-08-22T10:04:07.454Z",
"updatedAt": "2022-08-22T10:04:07.454Z",
"recyclings": [
{
"id": 4,
"solvents": 0,
"acids": 5,
"createdAt": "2022-08-22T10:04:36.413Z",
"updatedAt": "2022-08-22T10:04:36.413Z",
"userId": 2
},
{
"id": 5,
"solvents": 0.4,
"acids": 77,
"createdAt": "2022-08-22T10:05:05.733Z",
"updatedAt": "2022-08-22T10:05:05.733Z",
"userId": 2
}
]
}
Here is login function in AuthController:
const db = require("../models");
const config = require("../config/auth.config");
const User = db.user;
const Role = db.role;
const Recycling = db.recyclings;
const Op = db.Sequelize.Op;
var jwt = require("jsonwebtoken");
var bcrypt = require("bcryptjs");
exports.signin = (req, res) => {
User.findOne({
where: {
email: req.body.email
}
}, {include: ["recyclings"]}) //HERE IS INCLUDE-doesn't work!
.then(user => {
if (!user) {
return res.status(404).send({ message: "User not found!" });
}
var passwordIsValid = bcrypt.compareSync(
req.body.password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({
accessToken: null,
message: "Password not correct!"
});
}
var token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400 // 24 hours
});
var authorities = [];
user.getRoles().then(roles => {
for (let i = 0; i < roles.length; i++) {
authorities.push("ROLE_" + roles[i].name.toUpperCase());
}
res.status(200).send({
id: user.id,
name: user.name,
surname: user.surname,
vat: user.vat,
email: user.email,
roles: authorities,
accessToken: token
});
});
})
.catch(err => {
res.status(500).send({ message: err.message });
});
};
I don't know how to incorporate getRecyclings() function with existing getRoles(). Can someone help me, please?
spelling error,
you import Recycling to your code
and use recyclings in to your find query include.
change it.and try again

Graphql can't retrieve sub type

I'm new on graphql and i tried to implement a basic API with express.
But, when i tried to request post author these return null.
My "Post" type :
const postType = `
type Post {
id: ID
title: String
content: String
author: User
}
`;
module.exports = postType
My "User" type :
const userType = `
type User {
id: ID
name: String
age: Int
}
`;
module.exports = userType
My Graphql API schema :
const schema = buildSchema(`
${userType}
${postType}
type Query {
users: [User!]!
posts: [Post!]!
user(id: Int): User!
post(id: Int): Post!
}
type Mutation {
createUser(user: createUserInput): User
}
schema {
query: Query
mutation: Mutation
}
`);
module.exports = schema
My "Post" resolver and type implementation with ES6 class :
const { getUser } = require('../actions/index').user
const { getPost, getPosts } = require('../actions/index').post
class Post {
constructor(post) {
Object.assign(this, post)
}
async author() {
const data = await getUser(this.post.author)
return data
}
}
const postResolver = {
posts: async () => {
const data = await getPosts()
return data.map(post => new Post(post))
},
post: async ({ id }) => new Post(await getPost(id))
}
module.exports = postResolver
My "User" resolver and type implementation with ES6 class :
const { getUsers, getUser, createUser } = require('../actions/index').user
class User {
constructor(user) {
Object.assign(this, user)
}
}
const userResolver = {
users: async () => {
const data = await getUsers()
return data.map(user => new User(user))
},
user: async ({ id }) => new User(await getUser(id)),
}
module.exports = userResolver
The client query and response :
query {
post(id: 1) {
id
title
author {
id
}
}
}
// response
{
"data": {
"post": {
"id": "1",
"title": "Post 1",
"author": {
"id": null
}
}
}
}
Someone can help me please ? Thanks !

How to use Mongoose Populate between 2 different Schema's

I have 2 schema's, Categories and Cards. Each Category has an array of cards, and I want to populate that array with values , but I am unsure how to go about this as the mongoose documentation is somewhat confusing to understand.
// Schemas in seperate files
// Category Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categorySchema = new Schema({
title: {
type: String,
trim: true,
max: 30,
},
cards: [{ type: Schema.Types.ObjectId, ref: "categoryCard" }],
});
module.exports = mongoose.model("category", categorySchema);
// Category Card Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categoryCardSchema = new Schema({
category: {
type: String,
trim: true,
},
name: {
type: String,
trim: true,
},
post: {
type: String,
required: true,
trim: true,
},
});
module.exports = mongoose.model("categoryCard", categoryCardSchema);
// Below is the express router file . I want users to be able to create cards for different categories , after the category is already created. It worked in postman, but it doesn't work on the front end for some reason.
router.route("/createCard").post((req, res) => {
const { title, name, post } = req.body;
newCard = new categoryCard({
category: title,
name,
post,
});
newCard.save();
category.findOne({ title }).exec((err, item) => {
if (!err) {
item.cards.push(newCard._id);
item.save();
res.send(item);
} else {
res.send(err);
}
});
});
You can Follow this code...
let categorys= await category.findOne({ title }).populate("cards")

Vuex - Normalizr doesn't work as expected

I am creating a simple chat app. I have three entities: rooms, messages and users.
I have a fake API that returns a response like this:
[{
id: 1,
name: 'room1',
avatar: 'some img url',
messages: [
{
id: 1,
text: 'some text',
user: {
id: 1,
username: 'Peter Peterson',
avatar: 'some img url'
}
]
}]
And my action looks like this:
getAllRooms({ commit }) {
commit(GET_ALL_ROOMS_REQUEST);
return FakeApi.getAllRooms()
.then(
rooms => {
const { entities } = normalize(rooms, room);
console.log(entities);
commit(GET_ALL_ROOMS_SUCCESS, {
rooms: entities.rooms, byId: rooms.map(room => room.id)
});
commit(GET_ALL_MESSAGES_SUCCESS, { messages: entities.messages });
commit(GET_ALL_USERS_SUCCESS, { users: entities.users });
},
err => commit(GET_ALL_ROOMS_ERROR)
)
}
And my mutations look like this:
[GET_ALL_ROOMS_REQUEST](state) {
state.loading = true;
},
[GET_ALL_ROOMS_SUCCESS](state, payload) {
state.rooms = payload.rooms;
state.byId = payload.byId;
state.loading = false;
},
[GET_ALL_ROOMS_ERROR]() {
state.error = true;
state.loading = false;
}
And my component calls the action like this:
{
mounted() {
this.getAllRooms();
}
}
These are my schema definitions:
const user = new schema.Entity('users');
const message = new schema.Entity('messages', {
user: user
});
const room = new schema.Entity('rooms', {
messages: [message]
})
when i check the response in then method after FakeApi.getAllRooms() every object is wrapped in some weird Observer, and I pass it like that to normalize and normalize returns some weird response.
What am I doing wrong?
The problem wasn't with vuejs, it was with the way I made the normalizr schemas. Because my response is an array at the root I should have had a new rooms array schema, like so:
const user = new schema.Entity('users');
const message = new schema.Entity('messages', {
user: user
});
const room = new schema.Entity('rooms', {
messages: [message]
});
const roomsSchema = [room];
And then use it like this: normalize(rooms, roomsSchema)