Populating related keystone list - node.js

So I have a Post model (built in of course). Each Post has an Author. Each Author has a Profile (I built this out separately deliberately as I am trying to learn. I am having real problems populating the profile
Here's what I have in my blog.js
// Load the posts
view.on('init', function(next) {
var q = keystone.list('Post').paginate({
page: req.query.page || 1,
perPage: 10,
maxPages: 10
})
.where('state', 'published')
.sort('-publishedDate')
.populate('categories')
.populate('author');
if (locals.data.category) {
q.where('categories').in([locals.data.category]);
}
q.exec(function(err, results) {
locals.data.posts = results;
async.each(results.results, function(post,_next) {
post.author.populate('profile', function(err, author) {
post.author = author;
locals.data.posts.results.push(post);
_next();
});
}, function(err) {
next();
})
})
});
Here is Profile
var keystone = require('keystone'),
Types = keystone.Field.Types;
var Profile = new keystone.List('Profile', {
map: { name: 'nickname' },
autokey: { path: 'slug', from: 'nickname', unique: true }
});
Profile.add({
nickname: { type: String, required: true, initial: true},
age: { type: Types.Number, initial: false, required: false, index: true },
location: { type: Types.Text, initial: false, required: false, index: true }
});
Profile.defaultColumns = 'nickname, age, location';
Profile.register();
Here is user
var keystone = require('keystone'),
Types = keystone.Field.Types;
/**
* User Model
* ==========
*/
var User = new keystone.List('User');
User.add({
name: { type: Types.Name, required: true, index: true },
email: { type: Types.Email, initial: true, required: true, index: true },
password: { type: Types.Password, initial: true, required: true },
profile: { type: Types.Relationship, ref: 'Profile', index: true },
}, 'Permissions', {
isAdmin: { type: Boolean, label: 'Can access Keystone', index: true }
});
// Provide access to Keystone
User.schema.virtual('canAccessKeystone').get(function() {
return this.isAdmin;
});
/**
* Relationships
*/
User.relationship({ ref: 'Post', path: 'posts', refPath: 'author' });
/**
* Registration
*/
User.defaultColumns = 'name, email, isAdmin';
User.register();
Here is Post
var keystone = require('keystone'),
Types = keystone.Field.Types;
/**
* Post Model
* ==========
*/
var Post = new keystone.List('Post', {
map: { name: 'title' },
autokey: { path: 'slug', from: 'title', unique: true }
});
Post.add({
title: { type: String, required: true },
state: { type: Types.Select, options: 'draft, published, archived', default: 'draft', index: true },
author: { type: Types.Relationship, ref: 'User', index: true },
publishedDate: { type: Types.Date, index: true, dependsOn: { state: 'published' } },
image: { type: Types.CloudinaryImage },
content: {
brief: { type: Types.Html, wysiwyg: true, height: 150 },
extended: { type: Types.Html, wysiwyg: true, height: 400 }
},
categories: { type: Types.Relationship, ref: 'PostCategory', many: true }
});
Post.schema.virtual('content.full').get(function() {
return this.content.extended || this.content.brief;
});
Post.defaultColumns = 'title, state|20%, author|20%, publishedDate|20%';
Post.register();
Thanks in advance!

Related

Mongoose Schema type Object Id Mapping

Backend Code:
This is orderModel.js
import mongoose from "mongoose";
const orderSchema = mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
orderItems: [
{
name: { type: String, required: true },
qty: { type: String, required: true },
image: { type: String, required: true },
price: { type: String, required: true },
productId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "Product",
},
},
],
deliveryAddress: {
address: { type: String, required: true },
area: { type: String, required: true },
// gender: { type: String, required: true },
postalCode: { type: String, required: true },
district: { type: String, required: true },
division: { type: String, required: true },
country: { type: String, required: true, default: "Bangladesh" },
},
paymentMethod: {
type: String,
required: true,
},
paymentStatus: {
id: { type: String },
status: { type: String },
update_time: { type: String },
email: { type: String },
},
taxPrice: {
type: Number,
required: true,
default: 0.0,
},
deliveryCost: {
type: Number,
required: true,
default: 0.0,
},
totalPrice: {
type: Number,
required: true,
default: 0.0,
},
discount: {
type: Number,
required: true,
default: 0.0,
},
isPaid: {
type: Boolean,
required: true,
default: false,
},
paidAt: {
type: Date,
},
isDelivered: {
type: Boolean,
required: true,
default: false,
},
deliveredAt: {
type: Date,
},
},
{
timeStamps: true,
}
);
const Order = mongoose.model("Order", orderSchema);
export default Order;
This is orderController.js -
import asyncHandler from "express-async-handler";
import Order from "../models/orderModel.js";
// review: desc => Create New Order
// review: route => POST api/orders
// review: access => Private
const createOrder = asyncHandler(async (req, res) => {
const {
orderItems,
deliveryAddress,
paymentMethod,
taxPrice,
deliveryCost,
discount,
totalPrice,
} = req.body;
if (orderItems && orderItems.length === 0) {
res.status(400);
throw new Error("No order items");
} else {
try {
const order = new Order({
orderItems,
user: req.user._id,
deliveryAddress,
paymentMethod,
taxPrice,
deliveryCost,
discount,
totalPrice,
});
console.log("ORDER CREATED", order);
const createdOrder = await order.save();
console.log("ORDER saved", createdOrder);
res.status(201).json(createdOrder);
} catch (error) {
res.status(400);
throw new Error("Invalid order data", error);
}
}
});
// review: desc => GET order by id
// review: route => GET api/orders/:id
// review: access => private
const getOrderById = asyncHandler(async (req, res, next) => {
const order = await Order.findById(req.params.id).populate(
"user",
"name email"
);
if (order) {
res.json(order);
} else {
res.status(404);
throw new Error("Order not found");
}
});
export { createOrder, getOrderById };
Frontend Code:
const handleSubmit = (e) => {
e.preventDefault();
dispatch(
createOrder({
orderItems: cartItems,
deliveryAddress: addressObject,
paymentMethod,
taxPrice: vat,
deliveryCost,
discount,
totalPrice,
})
);
};
Here When I am sending request to backend, It's giving me validation error. As - orderItems.0.productId: Path productId is required. Why I am getting this error ? mongoose should not map data and get productId from sent cartItems, where is a field is _id ? Your time and help is most appreciated, Thanks.

Delete the associated blog posts with user before deleting the respective user in mongodb and nodejs

When I am deleting a user, I also want to delete all the associated blog posts with that user. I have used MongoDB's pre() middleware. when it is fired it only sets the postedBy property to null in the post and then MongoDB compass the postedBy is still there along with userId
here is User schema.
const crypto = require("crypto");
const mongoose = require("mongoose");
const Post = require("./post");
const userSchema = mongoose.Schema(
{
username: {
type: String,
trim: true,
required: true,
unique: true,
index: true,
lowercase: true,
},
name: {
type: String,
index: true,
required: true,
max: 32,
},
email: {
type: String,
index: true,
required: true,
trim: true,
unique: true,
},
hashed_password: {
type: String,
required: true,
},
role: {
type: Number,
default: 0,
},
profile: {
type: String,
required: true,
},
photo: {
data: Buffer,
contentType: String,
},
salt: String,
resetPassword: {
data: String,
default: "",
},
},
{ timestamp: true }
);
userSchema
.virtual("password")
.set(function (password) {
this._password = password;
this.salt = this.makeSalt();
this.hashed_password = this.encryptPassword(password);
})
.get(function () {
return this._password;
});
userSchema.methods = {
authenticate: function (plainText) {
return this.encryptPassword(plainText) === this.hashed_password;
},
encryptPassword: function (password) {
if (!password) return "";
try {
return crypto
.createHmac("sha1", this.salt)
.update(password)
.digest("hex");
} catch (err) {
return "";
}
},
makeSalt: function () {
return Math.round(new Date().valueOf() * Math.random()) + "";
},
};
userSchema.pre("findByIdAndRemove", function (next) {
Post.deleteMany({ postedBy: this._id }, function (err, result) {
if (err) {
console.log("error");
} else {
console.log(result);
}
});
next();
});
module.exports = mongoose.model("User", userSchema);
here is Post schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const postSchema = new Schema({
postedBy: {
type: Schema.Types.ObjectId,
ref: "User",
},
title: {
type: String,
},
body: {
type: String,
},
name: {
type: String,
},
photo: {
data: Buffer,
contentType: String,
},
updated: Date,
avatar: {
type: String,
},
created: {
type: Date,
default: Date.now,
},
comments: [
{
postedBy: {
type: Schema.Types.ObjectId,
refPath: "onModel",
},
text: String,
created: {
type: Date,
default: Date.now,
},
},
],
likes: [
{
type: Schema.Types.ObjectId,
refPath: "onModel",
},
],
onModel: {
type: String,
enum: ["User", "Imam"],
},
});
module.exports = mongoose.model("Post", postSchema);
this is delete route function
exports.userdelete = (req, res) => {
User.findByIdAndRemove(req.params.id).exec((err, doc) => {
if (err) {
return res.status(400).json({
error: "Something went wrong",
});
}
return res.json({
message: "User deleted",
});
});
};
You Can follow this code
You can use cascade delete in mongoose
User.findOne({...}, function(err, customer) {
Post.remove();
});

How to delete the referenced document in one collection and its record from the referred other collection

In my NodeJS API and MongoDB, I'm trying to delete a record which is a reference to another collection.
What I would like to do is to delete the referred objectId and the records related to the other collection which is referred.
I have 2 models Profiles and Posts and I want to delete the same one post from Profile and Post collection.
I was able to delete the reference id in Profile but I don't know how to delete also the record from Posts collection.
I tried this:
async delete(req, res) {
try {
// Match with username and pull to remove
await Profile.findOneAndUpdate(
{ _id: res.id._id },
{ $pull: { posts: req.params.postId } },
err => {
if (err) {
throw new ErrorHandlers.ErrorHandler(500, err);
}
res.json({ Message: "Deleted" });
}
);
} catch (error) {
res.status(500).send(error);
}
}
And my 2 models:
// Here defining profile model
// Embedded we have the Experience as []
const { Connect } = require("../db");
const { isEmail } = require("validator");
const postSchema = {
type: Connect.Schema.Types.ObjectId,
ref: "Post"
};
const experienceSchema = {
role: {
type: String,
required: true
},
company: {
type: String,
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: false
},
description: {
type: String,
required: false
},
area: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
username: {
type: String,
required: false
},
image: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
}
};
const profileSchema = {
firstname: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
email: {
type: String,
trim: true,
lowercase: true,
unique: true,
required: [true, "Email is required"],
validate: {
validator: string => isEmail(string),
message: "Provided email is invalid"
}
},
bio: {
type: String,
required: true
},
title: {
type: String,
required: true
},
area: {
type: String,
required: true
},
imageUrl: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
},
username: {
type: String,
required: true,
unique: true
},
experience: [experienceSchema],
posts: [postSchema],
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
}
};
const collectionName = "profile";
const profileSchemaModel = Connect.Schema(profileSchema);
const Profile = Connect.model(collectionName, profileSchemaModel);
module.exports = Profile;
const { Connect } = require("../db");
const reactionSchema = {
likedBy: {
type: String,
unique: true,
sparse: true
}
};
const postSchema = {
text: {
type: String,
required: true,
unique: true,
sparse: false
},
profile: {
type: Connect.Schema.Types.ObjectId,
ref: "Profile",
},
image: {
type: String,
default: "https://via.placeholder.com/150",
required: false
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
reactions: [reactionSchema],
comments: {
type: Connect.Schema.Types.ObjectId,
ref: "Comment",
required: false
}
};
const collectionName = "post";
const postSchemaModel = Connect.Schema(postSchema);
const Post = Connect.model(collectionName, postSchemaModel);
module.exports = Post;
Just add a query to remove the post after pulling it's ID from the profile collection:
async delete(req, res) {
try {
// Match with username and pull to remove
await Profile.findOneAndUpdate(
{ _id: res.id._id },
{ $pull: { posts: req.params.postId } },
// You don't need an error callback here since you are
// using async/await. Handle the error in the catch block.
);
await Posts.remove({ _id: req.params.postId });
} catch (error) {
// This is where you handle the error
res.status(500).send(error);
}
}

Want to output ID value of user and display it on console

I am using a sailsjs framework for an app. I am building, and I am trying to extract the id value from a model I have:
const memoryCreatorId = _(Memory.creator).map('id').value();
console.log(memoryCreatorId);
const message = {
app_id: '***********************',
contents: {"en": "Yeah Buddy, Rolling Like a Big Shot!"},
filters: [{"field": "tag", "key": "userId", "relation": "=", "value": memoryCreatorId}],
ios_badgeType: 'Increase',
ios_badgeCount: 1
};
return PushNotificationService.sendNotification(message);
I'm basically trying to get what would be the Memory.creator.User.id value. So basically the userid of the person who creates a memory. I'm trying to get it from the "Memory" model "creator" attribute, which maps to the "User" model, and from the "User" model, extract the "id" attribute. Thanks for your help in advance!
Memory model below:
Memory.js
const _ = require('lodash');
module.exports = {
attributes: {
creator: {
model: 'User'
},
title: {
type: 'string'
},
description: {
type: 'text'
},
contentUrl: {
type: 'string',
url: true
},
cropRect: {
type: 'string'
},
likers: {
collection: 'User',
via: 'memoryLikes'
},
comments: {
collection: 'Comment',
via: 'memory'
},
update: {
model: 'Update',
},
cause: {
model: 'Cause',
}
}
};
User model is a follows:
User.js
'use strict';
const uuid = require('node-uuid');
const CipherService = require('../services/CipherService');
const BraintreeService = require('../services/BraintreeService');
module.exports = {
attributes: {
id: {
type: 'string',
primaryKey: true,
defaultsTo: () => uuid.v4(),
unique: true,
index: true,
uuidv4: true
},
firstName: {
type: 'string',
defaultsTo: ''
},
lastName: {
type: 'string',
defaultsTo: ''
},
email: {
type: 'string',
email: true,
required: true,
unique: true
},
password: {
type: 'string'
},
passwordResetToken: {
type: 'string'
},
passwordResetTokenExpires: {
type: 'string'
},
type: {
type: 'string',
enum: ['admin', 'member']
},
city: {
type: 'string'
},
state: {
type: 'string'
},
address: {
type: 'string'
},
institution: {
model: 'Institution'
},
major: {
type: 'string'
},
contentUrl: {
type: 'string',
url: true,
defaultsTo: AwsService.getAssetImageUrl('user-default.png')
},
cropRect: {
type: 'string'
},
graduationYear: {
type: 'integer'
},
donations: {
collection: 'Donation',
via: 'donor'
},
memories: {
collection: 'Memory',
via: 'creator'
},
causes: {
collection: 'Cause',
via: 'followers',
dominant: true
},
adminCauses: {
collection: 'Cause',
via: 'admins'
},
isLeader: {
type: 'boolean',
defaultsTo: false
},
isCurrentStudent: {
type: 'boolean',
defaultsTo: false
},
isAdmin: {
type: 'boolean',
defaultsTo: false
},
adminTitle: {
type: 'string'
},
paymentProfile: {
model: 'PaymentProfile'
},
jsonWebTokens: {
collection: 'Jwt',
via: 'owner'
},
memoryLikes: {
collection: 'Memory',
via: 'likers'
},
updateLikes: {
collection: 'Update',
via: 'likers'
},
toJSON: function() {
return User.clean(this);
}
},
beforeUpdate: (values, next) => {
CipherService.hashPassword(values).then(() => next()).catch(next);
},
beforeCreate: (values, next) => {
CipherService.hashPassword(values).then(() => next()).catch(next);
},
clean: (user) => {
//let obj = user.toObject();
delete user.password;
delete user.jsonWebTokens;
delete user.passwordResetToken;
delete user.passwordResetTokenExpires;
return user;
}
};

mongoosejs findByIdAndUpdate a best way

I'm wondering if there a best way to do this:
/**
* Article Schema
*/
var PostSchema = new Schema({
title: {
type: String,
required: true,
trim: true
},
author:{
type: String,
required: true,
default: 'whisher'
},
slug: {
type: String,
index: { unique: true }
},
body: {
type: String,
required: true,
trim: true
},
avatar:{
type: String,
required: true
},
status: {
type: String,
required: true,
trim: true
},
created: {
type: Date,
required: true,
default: Date.now
},
published: {
type: Date,
required: true
},
categories: {
type: [String]
},
tags: {
type: [String],
required: true,
index: true
},
comment: {
type: Schema.Types.ObjectId,
ref: 'CommentSchema'
},
meta: {
votes: {
type: Number,
default: 0
},
comments: {
type: Number,
default: 0
}
}
});
/**
* Comment Schema
*/
var CommentSchema = new Schema({
post_id: {
type: Schema.Types.ObjectId,
ref: 'Post',
required: true
},
author:{
type: String,
required: true
},
email:{
type: String,
required: true
},
web:{
type: String
},
body: {
type: String,
required: true,
trim: true
},
status: {
type: String,
required: true,
default: 'pending'
},
created: {
type: Date,
required: true,
default: Date.now
},
meta: {
votes: Number
}
});
/**
* Create a comment
*/
exports.create = function(req, res) {
var comment = new Comment(req.body);
comment.save(function(err) {
if (err) {
return res.jsonp(500,{ error: 'Cannot save the comment' });
}
Post.findById(comment.post_id).exec(function(err, post) {
if (err) {
return res.jsonp(404,{ error: 'Failed to load post with id ' + comment.post_id });
}
if (!post) {
return res.jsonp(404,{ error: 'Failed to load post with id ' + comment.post_id });
}
post.meta.comments = post.meta.comments++;
post.save(function(err) {
if (err) {
return res.jsonp(500,{ error: 'Cannot update the post' });
}
res.jsonp(200,comment);
});
});
});
};
Btw I just looking at http://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate
but like this:
Model.findByIdAndUpdate(comment.post_id, { post.meta.comments: post.meta.comments++ })
doesnt work
I think you need to use the $inc operator to increment the comment count like this...
Post.findByIdAndUpdate(comment.post_id, { $inc: {"meta.comments" : 1} }, callback);

Resources