restful-keystone and populate - javascript

I currently have a News and a User model in my KeystoneJS installation.The User model is the one produced by Keystone.
News.js
var keystone = require('keystone'),
Types = keystone.Field.Types;
var News = new keystone.List('News', {
autokey: { path: 'slug', from: 'title', unique: true },
map: { name: 'title'},
defaultSort: '-publishedAt'
});
News.add({
title: { type: String, required: true },
keywords: {type: String, initial:true, required: true, default:""},
description: {type: String},
state: { type: Types.Select, options: 'draft, published, archived', default: 'draft' },
author: { type: Types.Relationship, ref: 'User' },
createdAt: { type: Date, default: Date.now },
publishedAt: {type:Date,noedit:true},
content: { type: Types.Markdown}
});
News.defaultColumns = 'title, keywords, state|20%, author, publishedAt|15%'
News.schema.methods.isPublished = function() {
return this.state == 'published';
}
News.schema.pre('save', function(next) {
if (this.isModified('state') && this.isPublished() && !this.publishedAt) {
this.publishedAt = new Date();
}
next();
});
News.register();
In my index.js, as part of the expose() method, I have the author populated:
Index.js (fragment)
...
restful.expose({
News : {
populate:'author',
methods: ['retrieve','list'],
filter:{
state:'published'
},
show: ['title','author.name','keywords','publishedAt','description','content.html']
},
...
My problem is that 'author.name' prints no value (it should return an object containing first and last name), and printing the author directly, includes fields such as as the author's id and password. Is there some way I get the fields I wanted from a related model without exposing everything in the call? I notice that content.html does return the HTML section of the Markdown element.

Related

How to implement a deep populate using the Mongoose, to properly output a M:N database relation?

I am trying to create a basic social media website, with post having different comments, likes, comments also having likes. If the logged in user is the one that made the comment or post, he could be able to delete the post.
So I have to use the deep populate method of mongoose, but the issue I am having is, when the content of the comment is showing, then the name of the user that made that comment is missing.
Post Schema is as follows
content: {
type: String,
required: true
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
// include the array of ids of all comments in this post schema itself
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}
],
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Like'
}
]
},{
timestamps: true
});
Like Schema is as follows -
const likeSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.ObjectId
},
// this defines the object id of the liked object
likeable: {
type: mongoose.Schema.ObjectId,
require: true,
refPath: 'onModel'
},
// this field is used for defining the type of the liked object since this is a dynamic reference
onModel: {
type: String,
required: true,
enum: ['Post', 'Comment']
}
}, {
timestamps: true
});
User Schema is as follows -
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
name: {
type: String,
required: true
},
avatar: {
type: String
}
}, {
timestamps: true
});
Comment Schema is as follows -
const commentSchema = new mongoose.Schema({
content: {
type: String,
required: true
},
// comment belongs to a user
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
},
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Like'
}
]
},{
timestamps: true
});
And this is the populate function, on the front end I am trying to display all possible posts with all possible likes and comments, with all the comments also having likes. And obviously the name of the user that made the comment -
let posts = await Post.find({})
.sort('-createdAt')
.populate('user')
.populate({
path: 'comments',
populate: {
path: 'user'
},
populate: {
path: 'likes'
}
}).populate('comments')
.populate('likes');
But in the front end, I am not able to display the User Name that made a particular comment.
Please tell the error.

Failed to delete the a comment on post?

I'm trying to delete a comment on a post, but I'm unable to find the comment. When I'm console.log(post.comments) it shows me all the comments but still, I can't find the comment. The error was Comment not found which I wrote to find that comment is still there or not. But the comment was there I matched the id with it. Help me I'm new to NodeJs. Help me to fix this
*As a frontend I'm using react and redux I think the problem is in the backend, I've also tested with the postman. Can't delete the comment from postman.
here is the comment route and controller
router.route('/:id/comment/:comment_id').delete(protect, deleteComment);
export const deleteComment = asyncHandler(async (req, res) => {
const post = await Post.findById(req.params.id);
const comment = post.comments.find(
(comment) => comment._id === req.params.comment_id
);
if (!comment) {
res.status(404);
throw new Error('Comment not found');
}
//Check User
if (comment.user.toString() === req.user._id.toString()) {
post.comments = post.comments.filter(
({ id }) => id !== req.params.comment_id
);
await post.save();
return res.json(post.comments);
} else {
res.status(401);
throw new Error('User not authorized');
}
});
here is the post model
import mongoose from 'mongoose';
const postSchema = mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: [true, 'Please Author is required'],
},
title: {
type: String,
required: true,
},
desc: {
type: String,
required: true,
},
img: {
type: String,
},
isLiked: {
type: Boolean,
default: false,
},
isDisLiked: {
type: Boolean,
default: false,
},
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
],
disLikes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
],
comments: [
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
text: {
type: String,
required: true,
},
name: {
type: String,
},
pic: {
type: String,
},
date: {
type: Date,
default: Date.now,
},
},
],
categories: {
type: Array,
},
},
{
timestamps: { createdAt: 'created_at', updatedAt: 'modified_at' },
}
);
const Post = mongoose.model('Post', postSchema);
export default Post;
When you access to the _id you are accessing the instance of the ObjectId
You should try to compare with the id, that is a string representation of the _id
const comment = post.comments.find(
(comment) => comment.id === req.params.comment_id
);

How to use mongoose transactions with updateMany?

I am using the mongoose updateMany() method and I also want to keep it a part of transaction. The documentation shows the example of save() where I can do something like Model.save({session: mySession}) but don't really know how to use it with for example Model.updateMany()
UPDATE:
For example I have two models called SubDomain and Service and they look like this respectively:
SUB-DOMAIN
{
name: {
type: String,
required: true,
},
url: {
type: String,
required: true,
unique: true,
},
services: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Service",
},
],
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
}
SERVICE:
{
name: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
price: { type: Number },
tags: { type: Array },
packages: [
{
name: { type: String, required: true },
description: { type: String, required: true },
price: { type: Number, required: true },
},
],
map: { type: String },
isHidden: {
type: Boolean,
required: true,
default: false,
},
sortingOrder: { type: Number },
isForDomain: { type: Boolean, required: false, default: false },
isForSubDomain: { type: Boolean, required: false, default: false },
subDomains: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "SubDomain",
},
],
}
Now the main field here is the services field in SubDomain and subDomains field in Service.
The complicated part😅:
Whenever the user wants to create new service, I want to $push that service's _id into the array of services of all the subDomains inside that new service
And for that, I am using the updateMany() like this:
const sess = await mongoose.startSession();
sess.startTransaction();
const newService = new Service({
_id: mongoose.Types.ObjectId(),
subDomains: req.body.subDomains
...foo
})
await SubDomain.updateMany(
{ _id: { $in: req.body.subDomains } },
{ $push: { services: newService._id } }
);
The problem starts here, of course I can do:
newService.save({session: sess})
but how do I keep my SubDomain's updateMany in the same transaction (i.e sess)
I know my example is difficult to wrap your head around but I have tried to pick a simplest example rather than copying the exact same code which would have been a lot more difficult

How can I save this JSON to a mongoDB database serviced by MonogDB Compass?

I have some raw JSON that I have populated for testing purposes, but now I would like to put it into a mongoDB database using mongoDB Compass.
My mongoDB connection string is working and I have working mongoose code.
How do I go about doing this?
I would hope this would be an easy task as mongoDB stores it's data in the form of BSON already.
Here is a snippet of my code.
const json_string =
`[
{
"link": "https://www.youtube.com/watch?v=BMOjVYgYaG8",
"image": "https://i.imgur.com/Z0yVBpO.png",
"title": "Debunking the paelo diet with Christina Warinner",
// ... snip
},
{ // ... snip
The schema is already created:
// for relevant data from google profile
schema.Article = new Schema({
link: { type: String, required: true },
image: { type: String, required: true },
title: { type: String, required: true },
summary: { type: String, required: true },
tag: { type: String, required: true },
domain: { type: String, required: true },
date: { type: String, required: true },
timestamp: { type: Date, default: Date.now }
});
You can use this
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
mongoose.connect(process.env.MONGO_URI);
const articleSchema = new Schema({
link: { type: String, required: true },
image: { type: String, required: true },
title: { type: String, required: true },
summary: { type: String, required: true },
tag: { type: String, required: true },
domain: { type: String, required: true },
date: { type: String, required: true },
timestamp: { type: Date, default: Date.now }
});
const Article = mongoose.model("Article", articleSchema);
const json_string = `[
{
"link": "https://www.youtube.com/watch?v=BMOjVYgYaG8",
"image": "https://i.imgur.com/Z0yVBpO.png",
"title": "Debunking the paelo diet with Christina Warinner"
}
]`;
const jsonBody = JSON.parse(json_string);
for (let i = 0; i < jsonBody.length; i++) {
const data = jsonBody[i];
const article = new Article({
link: data.link,
image: data.image,
title: data.title
//.... rest
});
article.save();
}
Convert JSON string to an array
Loop through each object in the array
Create a new Article instance based on values from the object
Call the save method on the Article object

meteor: add some value to nested documents

I'm trying to add a value to a nested schema:
groups = new SimpleSchema({
title: { type: String, optional: true },
element: { type: [elements], optional: true }
});
elements = new SimpleSchema({
description:{ type: String, optional: true },
anything: { type: String, optional: true }
});
MongoDB.attachSchema(new SimpleSchema({
title: { type: String },
slug: { type: String, unique: true },
language: { type: String, defaultValue: "en" },
group: { type: [groups], optional: true },
}));
Now I want to add just a new element-description to an existing entry in the DB. I tried this, but it doesn't work.
Uncaught Error: When the modifier option is true, validation object must have at least one operator
var newElement = {
description: 'insert this as a new element description'
};
MongoDB.update({ _id: Id }, { $push: { 'group.element': newElement }}, function(error) { if(error) console.warn(error); });
Is it correct to use 'group.element' as a $push-parameter?
Update
I forgot the index of group: $push: { 'group.0.element': newElement }
Also I have to define elements before groups in the schema.

Categories