how to use Mongoose to (add to , update,delete) Nested documents - node.js

I am a fresh mongoose user and I have a small exercise I have this schema
`var BusinessSchema = mongoose.Schema({
personal_email: { type: String, required: true, unique: true },
business_name: { type: String, required: true, unique: true },
business_emails: [{ email: String, Description: String }],
business_logo: { data: Buffer, contentType: String },
//Business Services
services: [{
service_name: { type:String,required:true},
service_price: Number,
promotion_offer : Number,
service_rating : [{Clinet_username:String ,rating : Number}],
service_reviews : [{Clinet_username:String ,review : String}],
type_flag : Boolean,
available_flag : Boolean
}]
});`
what I want to do is to update or add new service or delete rating using mongoose
business.update({// something here to update service_rating },function(err,found_business)
{
}); business.update({// something here to add new service_rating },function(err,found_business)
{
}); business.update({// something here to delete service_rating },function(err,found_business)
{
});

var where_clause = { /* your where clause */ };
var service_rating = {"username", 5};
to add :
business.update(where_clause, {
'$addToSet' : {
services.service_rating : service_rating
}
}, callback);
to delete :
business.update(where_clause, {
'$pull' : {
services.service_rating : service_rating
}
}, callback);
to update :
var other_where = {services.service_rating : {"user", 5}}; // your where clause
business.update(other_where, {
'$set': {
'services.service_rating.Clinet_username' : 'newUser',
'services.service_rating.rating' : 10
}
}, callback);

Related

How to return data but filter by is active mongoose (CLOSED)

i have two models relationship with mongoose.
my first model looks like this:
const gradeSchema = new Schema(
{
grade_name: {
type: String,
required: true,
uppercase: true
},
major: {
type: Schema.Types.ObjectId,
ref: "Major"
},
active: {
type: Boolean,
default: true
}
},
{
timestamps: true
}
)
and my second models like this:
const majorSchema = new Schema(
{
major_name: {
type: String,
required: true,
},
major_type: {
type: String,
required: true,
minLength: 2,
unique: true,
uppercase: true
},
active: {
type: Boolean,
default: true
}
},
{
timestamps: true
}
)
i want to get all grades by major but i want majors to be filtered by active.
i try using populate.match but i got the majors response null:
const grade = await Grade.find()
.populate({
path: "major",
match: {
active: true
},
select: "-_id major_name major_type"
})
.where("active")
.equals(true)
.exec()
You can make use of aggregation concept in mongo.
const data = await Grade.aggregate([
{
$match : {}
},
{
$lookup : {
"from" : "majors",
"pipeline" : [
{
$match : {
"active" : true
}
}
],
"as" : "majorInfo"
}
}
]).exec()

How to Delete Comment from Post on Node, express and Mongoose and Ajax

Please i need help on how to delete a single comment from a Post. when i click delete it return 500 error. some of the things that are confusing me here is, if i pass both post and comment ids on the link, how will i tell Ajax that this one is for post and this one is for comment.
Here is my posts schema
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
from: {
type: String,
required: true
},
createdAt: {
required: true,
default: Date.now
},
postImage: {
type: String,
require: true
},
comments: [{ type: Schema.Types.ObjectId, ref: 'Comment' }]
})
here is my ajax from main.js file.
/deleting comment with Ajax
$(document).ready(function() {
$('.delete-comment').on('click', function(e) {
const $target = $(e.target);
const id = $target.attr('data-id');
$.ajax({
type: 'DELETE',
url: '/posts/comments/'+id,
success: function(response) {
window.location.href='/posts';
},
error: function(err){
console.log(err);
}
});
});
});
my routes/comments
//Delete comment inside a post
router.delete( '/comments/:id', function( req, res ){
const post = Post.findOne({_id: req.params.postId});
const ObjectId = mongoose.Types.ObjectId;
let query = {_id:new ObjectId(req.params.id)}
console.log(query)
post.comments.delete(query, function(err) {
if(err){
console.log(err);
}
res.send('Success');
});
})
my views/index.ejs
//Comments and link
<% const counter = post.comments.length >= 2 ? "Comments" : "Comment"; %>
<button class="show-comments"><%= post.comments.length + " " + counter + "" %></button>
<% %>
<div class="postcomments" ><% post.comments.forEach(item => { %>
<ul >
<li><%= item.description %></li>
<a class="delete-comment" href="#" data-id="<%=item._id%>">Delete</a>
</ul>
<% });%>
my app.js
//use route from app.js
var commentRouter = require('./routes/comments');
app.use('/posts', commentRouter);
here is what is being returned on my terminal
DELETE /posts/comments/5e8ad7121277855e656b3379 500 5.395 ms - 3698
You need to know both the postId and the commentId to be able to delete the comment from posts collection. Also it would be good to delete the comment inside the comments collection.
So change your delete route to include postId and commentId as req.params.
You can delete a comment from posts using the findByIdAndUpdate method and $pull operator.
router.delete("/comments/:postId/:commentId", async function (req, res) {
try {
const post = await Post.findByIdAndUpdate(
req.params.postId,
{
$pull: { comments: req.params.commentId },
},
{ new: true }
);
if (!post) {
return res.status(400).send("Post not found");
}
await Comment.findByIdAndDelete(req.params.commentId);
res.send("Success");
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
TEST
Let's say we have this post document with 3 comments.
Posts:
{
"_id" : ObjectId("5e8b10c49ae619486094ed10"),
"comments" : [
ObjectId("5e8b104f9ae619486094ed0d"),
ObjectId("5e8b10599ae619486094ed0e"),
ObjectId("5e8b105e9ae619486094ed0f")
],
"title" : "Title",
"description" : "Description...",
"from" : "From",
"postImage" : "Post Image",
"createdAt" : ISODate("2020-04-06T14:21:40.884+03:00")
}
Comments:
{
"_id" : ObjectId("5e8b105e9ae619486094ed0f"),
"message" : "Comment 3"
},
{
"_id" : ObjectId("5e8b10599ae619486094ed0e"),
"message" : "Comment 2"
},
{
"_id" : ObjectId("5e8b104f9ae619486094ed0d"),
"message" : "Comment 1"
}
If we want to delete the comment with _id:5e8b10599ae619486094ed0e, we need to send a DELETE request to our route using url like this:
http://localhost:3000/posts/comments/5e8b10c49ae619486094ed10/5e8b10599ae619486094ed0e
5e8b10c49ae619486094ed10 is the postId, 5e8b10599ae619486094ed0e is the commentId.
Result will be like this:
Posts:
{
"_id" : ObjectId("5e8b10c49ae619486094ed10"),
"comments" : [
ObjectId("5e8b104f9ae619486094ed0d"),
ObjectId("5e8b105e9ae619486094ed0f")
],
"title" : "Title",
"description" : "Description...",
"from" : "From",
"postImage" : "Post Image",
"createdAt" : ISODate("2020-04-06T14:21:40.884+03:00")
}
Comments:
{
"_id" : ObjectId("5e8b105e9ae619486094ed0f"),
"message" : "Comment 3"
},
{
"_id" : ObjectId("5e8b104f9ae619486094ed0d"),
"message" : "Comment 1"
}
Your nodejs/express route contains this code. Maybe it should do more with possible errors: specifically, pass the error to the next() function that's the third parameter of any route handler.
post.comments.delete(query, function(err) {
if(err){
console.log(err)
return next(err)
}
res.send('Success')
});
Passing an error value to next() should deliver the error message to the user. And, you have the same message showing up on your server's console.log. So if the error is from there you should learn more about it.
I am facing the same problem Except that before erasing the post, I want to make sure that the user deleting the post is the creator of the same post. My data set is a little bit different.
import mongoose from 'mongoose'
const postSchema = mongoose.Schema(
{
title: {
type: String,
required: true,
},
comment: { type: String, required: true },
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
imagePost: { type: String, required: true },
},
{
timestamps: true,
}
)
const stationSchema = mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
nameUnit: {
type: String,
required: true,
},
typeOfPoint: { type: String },
image: {
type: String,
required: true,
},
lat: {
type: Number,
required: true,
},
long: {
type: Number,
required: true,
},
// Base
nameBase: { type: String, required: true },
element: {
type: String,
required: true,
},
baseCommanderInfo: { type: String },
aboutBaseInfo: { type: String },
// Unit
unitSuperviserInfo: { type: String },
unitCommanderInfo: { type: String },
unitInfo: { type: String },
taskInfo: { type: String },
benefitInfo: { type: String },
// address
country: { type: String },
province: { type: String },
town: { type: String },
adresse: { type: String },
postalCode: { type: String },
posts: [postSchema],
},
{
timestamps: true,
}
)
const Station = mongoose.model('Station', stationSchema)
export default Station
NodeControler
const removeStationPost = asyncHandler(async (req, res) => {
const stationId = req.params.id
const postId = req.params.idPost
const userId = req.user._id
console.log(stationId)
console.log(postId)
console.log(userId)
const station = await Station.findById(stationId)
if (station) {
station.posts.pull(postId)
await station.save()
res.status(201).json({ message: 'Post removed' })
} else {
res.status(404)
throw new Error('Post not found')
}
})
So now everybody can delete anybody else post with Postman

Mongoose populate() returns empty array with no errors

I've been trying to get this populate thing to work, but I'm getting issues because I am not getting the expected results, and no errors to work with. Just simply an empty array.
My models look like this. Each their own file
var mongoose = require( 'mongoose' );
var upgradeSchema = new mongoose.Schema({
type: {
type: String,
default: "Any"
},
ability: String,
ability_desc: String,
level: Number,
tag: String
});
mongoose.model('Upgrade', upgradeSchema);
and the other
var mongoose = require( 'mongoose' );
var crypto = require('crypto');
var jwt = require('jsonwebtoken');
var userSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true
},
hero: {
level: Number,
name: String,
type: {
path: String,
heroType: String
},
upgrades: [{
type: mongoose.Schema.Types.ObjectId, ref: 'Upgrade'
}],
unspent_xp: Number,
total_xp: Number,
},
armyTotal: {
type: Number,
default: 0,
max: 5000
},
army:[{
foc_slot: String,
unit_name: String,
unit_cost: Number
}],
username: {
type: String,
required: true,
unique: true,
},
faction: String,
name: {
type: String,
required: true
},
hash: String,
salt: String,
roles: {
type: String,
default: 'player' }
});
And I'm trying to do this
module.exports.profileRead = function(req, res) {
User
.findById(req.payload._id)
.populate('hero.upgrades')
.exec(function (err, user) {
if (err){
console.log(err);
} else {
res.status(200).json(user);
console.log("success");
}
});
}
};
This is an example of a user
{
"_id" : ObjectId("57b4b56ea03757e12c94826e"),
"hash" : "76",
"salt" : "2",
"hero" : {
"upgrades" : [
"57b42773f7cac42a21fb03f9"
],
"total_xp" : 0,
"unspent_xp" : 0,
"type" : {
"heroType" : "Psyker",
"path" : ""
},
"name" : "Jon Doe"
},
"username" : "michaelzmyers",
"faction" : "Grey Knights",
"email" : "email#gmail.com",
"name" : "Michael Myers",
"roles" : "player",
"army" : [],
"armyTotal" : 625,
"__v" : 3
}
Now, I've tried an array of just the strings with ObjectId's in them, similar to the eample, and I've also tried using ObjectId("STRINGHERE") and no luck. They both return just an empty array. However, if i get rid of the populate call (or change the contents inside populate from hero.upgrades to just hero, or upgrades) then it just returns an array of strings. I feel like the problem is with populate and how I'm using it. HOWEVER, when I had just a single upgrade in my databse (the test upgrade), everything worked fine. Now nothing works. Any thoughts? I'd be happy to provide more code if needed.
I found that during my little research that it will work:
User
.findById(req.payload._id)
.populate({
path: 'hero.upgrades',
model: 'Upgrade'
})
.exec(function (err, user) {
if (err){
console.log(err);
} else {
res.status(200).json(user);
console.log("success");
}
});
}
It looks like when user is giving nested object notation i.e. hero.upgrades into populate method, Mongoose got problems with detecting referring model.

How do I access `findAndModify` method from Mongo in Node (with Mongoose)?

This is /models/joke.js:
var mongoose = require ('mongoose')
, database = mongoose.connect('localhost', 'joke', { server: { poolSize: 3 } });
var jokeSchema = mongoose.Schema({
content: String,
upvotes: {
type: Number,
default: 0
},
downvotes: {
type: Number,
default: 0
},
views: {
type: Number,
default: 0
},
published: {
type: Boolean,
default: true
},
author_id: Number,
created: {
type: Date,
default: Date.now
}
});
var Joke = mongoose.model('Joke', jokeSchema);
module.exports = Joke;
And I'm doing this to check if something exist — if doesn't, then create:
var Joke = require ('./models/joke');
// ...
Joke.findAndModify({
query: {
content: content
},
update: {
$setOnInsert: {
content: "test",
}
},
new: true,
upsert: true
});
But my console shout me the following:
TypeError: Object function model(doc, fields, skipId) {
if (!(this instanceof model))
return new model(doc, fields, skipId);
Model.call(this, doc, fields, skipId);
} has no method 'findAndModify'
I can understand the reason for the error – I'm calling it through a model instead of a collection, but how do I access my Jokes' collection methods?
I mean, all of the examples were using db.collection.findAndModify, but what is this db.collection? How do I call it?
To access findAndModify update functionality from Mongoose, use findOneAndUpdate:
Joke.findOneAndUpdate(
{ content: content },
{ $setOnInsert: { content: "test" } },
{ new: true, upsert: true },
callback
);

geospatial queries on subdocuments

I have a mongoose schema with subdocuments that contain a location field (with 2dSpehre index). Like this:
var playerSchema = new mongoose.Schema({
name: { type: String, required: true },
addresses: [
{
address: {
street: String,
city: String,
zip: String,
country: String
},
loc: { type: [Number], index: '2dSphere' }
}
],
});
When I try to query for addresses via geospatial operators I get this error: planner returned error: unable to find index for $geoNear query. The query looks like this:
var query = {
'addresses.loc': {
$nearSphere: {
$geometry: { type: 'Point', coordinates: [16.3738189, 48.2081743] }
}
}
};
Player.find(query).exec();
I also checked via mongo that the index really exists:
> db.player.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "project.player"
},
{
"v" : 1,
"key" : {
"addresses.loc" : "2dsphere"
},
"name" : "addresses.loc_2dsphere",
"ns" : "project.player",
"2dsphereIndexVersion" : 2
}
]
What am I doing wrong? Thanks in advance.
Are you sure you are using the right collection? Mongoose will pluralize your collection name by default (so players instead of player).
The script below is working for me. For some reason Mongoose wasn't creating the 2dsphere index for me when it was specified in the schema so I moved that out.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var playerSchema = new mongoose.Schema({
name: { type: String, required: true },
addresses: [
{
address: {
street: String,
city: String,
zip: String,
country: String
},
loc: { 'type': { type: String }, 'coordinates': { type: [Number] } }
}
],
});
playerSchema.index({'addresses.loc': '2dsphere'});
var Player = mongoose.model('Player', playerSchema);
mongoose.connect('mongodb://localhost/test');
var query = Player.find({
'addresses.loc': {
$nearSphere: {
$geometry: { type: 'Point', coordinates: [16.3738189, 48.2081743] }
}
}
}, function (err, players) {
console.log(err)
console.log(players)
});

Resources