make a path that increments the count - mongodb

I'm trying to make a post request that will increment my schema using express and mongoose,
which is :
const ItemSchema = new Schema({
formName: String,
inputs: [
{
inputLabel: {
type: String,
required: true
},
inputType: {
type: String,
required: true,
enum: ['text', 'color', 'date', 'email', 'tel', 'number']
},
inputValue: {
type: String,
required: true
}
}
],
numOfSubs: { type: Number, default: 0 }
});
for my code purposes I want to make a route that will increase by 1 the numOfSubs everytime I use it,since there are a few listings, I have the ID so I need to search it, and I'm not sure how to write the path
router.post('/increase', (req, res) => {
"find and increase by 1 "
});
and I will use the fetch like so:
fetch('/api/items/increase', {
method: 'POST',
body: JSON.stringify({ _id }),//the ID I of the collection I want to increment
headers: {
'content-type': 'application/json'
}
});

try this using mongo $inc operator
router.post('/increase', (req, res, next) => {
const _id = req.body._id;
MyModel.findByIdAndUpdate(_id , { $inc: {numOfSubs: 1} }, { new: true }, (err,updateRes)=>{
if(err) return next(err);
return res.json({sucess: true});
});
});

Related

Mongoose model unique

I´m rather new to this..
If I dont want the user to be able to add duplicated countries to visitedCountry, shoulden unique true work?
Or are there any easy way to block that in the patch?
const User = mongoose.model('User', {
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
accessToken: {
type: String,
default: () => crypto.randomBytes(128).toString('hex')
},
visitedCountries:[ {
country: {
type: Object,
ref: "Country",
unique: true
},
comments: String
}]
})
app.patch('/countries', authenticateUser)
app.patch('/countries', async (req, res) => {
const { username, visitedCountry } = req.body
try {
const countryByAlphaCode = await Country.findOne({ alphaCode: visitedCountry }).lean()
const updatedUser = await User.findOneAndUpdate({ username: username, }, {
$push: {
visitedCountries: { country: countryByAlphaCode, comments: "no comments yet"}
},
}, { new: true })
res.json({ success: true, updatedUser })
} catch (error) {
res.status(400).json({ success: false, message: "Invalid request", error })
}
})
The options unique works for all documents. It prevents two (or more) documents from having the same value for your indexed field. It's often used for the email or username.
For your case, I recommend you to perform a check on the user data before you call findOneAndUpdate.

Mongoose - Update/Find Specific Object in an Array Not Working As Expected

I am following the docs without luck and am at a standstill while trying to update an object in an object in an array using MongoDB and Mongoose.
Here is my document:
{
fields: [
{ id: 603d63086db2db00ab09f50f, data: [Object] },
{ id: 603d63086db2db00ab09f510, data: [Object] },
{ id: 603d63086db2db00ab09f511, data: [Object] },
{ id: 603d63086db2db00ab09f512, data: [Object] },
{ id: 603d63086db2db00ab09f513, data: [Object] },
{ id: 603d63086db2db00ab09f514, data: [Object] },
{ id: 603d63086db2db00ab09f515, data: [Object] }
],
layouts: [],
_id: 603d631a6db2db00ab09f517,
bandId: '603d63146db2db00ab09f516',
eventType: 'private',
ownerId: '6039354906410800c14934c1',
__v: 0
}
I am trying to updateOne of the fields.data in the fields array. fields.data is an object as well.
I call my Express/Node Backend to this route.
//Update
router.put("/:id", async (req, res) => {
try {
let updating = await QuoteGenerator.updateOne(
{ _id: req.params.id, "fields.id": req.body.id },
{
"$set": {
"fields.$.data": req.body.data,
},
}
);
let item = await QuoteGenerator.findOne({ _id: req.params.id });
res.json({ success: "Item Updated.", item });
} catch (err) {
console.log(err);
res.json({ error: "Something went wrong when updating this item." });
}
});
Where req.body is:
{ id: '603d63086db2db00ab09f50f', data: { type: 1, rate: '200.30' } }
**Just in case it's helpful, here is what one of the fields objects looks like in the document,
{"id":"603d63086db2db00ab09f50f","data":{"type":1,"rate":300}}
I have even tried changing my route to find this document - which I have confirmed exists - Truly at a loss why it won't find the document.
Here is how I changed the above route to find the document.
//Update
router.put("/:id", async (req, res) => {
try {
let updating = await QuoteGenerator.find(
{ _id: req.params.id, "fields.id": req.body.id },
);
console.log(updating) //returns []
let item = await QuoteGenerator.findOne({ _id: req.params.id });
res.json({ success: "Item Updated.", item });
} catch (err) {
console.log(err);
res.json({ error: "Something went wrong when updating this item." });
}
});
The Model
//Create Schema - QG
const QuoteGeneratorSchema = new Schema({
bandId: {
type: String,
required: true,
},
ownerId: {
type: String,
required: true,
},
fields: {
type: Array,
default: defaultFields,
required: true,
},
eventType: {
type: String,
required: false,
},
layouts: {
type: Array,
required: false,
},
});
let QuoteGenerator = mongoose.model("QuoteGenerator", QuoteGeneratorSchema);
module.exports = QuoteGenerator;
Any nudge in the right direction to replacing that data object with a new data object would be extremely helpful! Thanks!

Mongoose text index search returns empty array

I'm trying to query indexes, but I receive an empty array. I can't find what's wrong with my code. I used two methods to create the index: 1) VideoSchema.index() and 2) in the schema itself, both of them don't work. I checked the mongodb and it seems that indexes are created correctly, so I don't know what I do wrong.
const mongoose = require("mongoose");
const VideoSchema = mongoose.Schema(
{
user: {
type: mongoose.ObjectId,
required: true,
ref: "user",
},
title: {
type: String,
maxLength: 100,
text: true,
},
description: {
type: String,
text: true,
},
publishDate: {
type: Date,
},
views: {
type: Number,
default: 0,
},
likes: {
type: Number,
default: 0,
},
dislikes: {
type: Number,
default: 0,
},
comments: [
{
type: mongoose.ObjectId,
ref: "comment",
},
],
urls: {
video_url: {
type: String,
required: true,
},
thumbnail_url: {
type: String,
},
preview_url: {
type: String,
required: true,
},
},
private: {
type: Boolean,
default: 0,
},
category: {
type: String,
default: "",
},
duration: {
type: Number,
required: true,
},
},
{ timestamps: true }
);
// VideoSchema.index({ title: "text", description: "text" });
// export model user with UserSchema
module.exports = mongoose.model("video", VideoSchema);
The query:
const express = require("express");
const router = express.Router();
const Video = require("../model/Video");
router.post("/", (req, res) => {
const query = req.body.query;
Video.find({ $text: { $search: query } }, { score: { $meta: "textScore" } })
.sort({ score: { $meta: "textScore" } })
.exec(function (error, results) {
if (error) return res.status(400).send(error);
res.status(200).json({ results });
});
});
module.exports = router;
As you are fetching data from your Database it´s a good practice and makes the code clearer if you use the 'GET' method. If you do so, there is no need to add the score option to the query since V.4.4
const express = require("express");
const router = express.Router();
const Video = require("../model/Video");
router.get("/", (req, res) => {
const query = req.query.YOUR_QUERY_PARAMETER;
Video.find({ $text: { $search: query }})
.sort({ score: { $meta: "textScore" } })
.exec(function (error, results) {
if (error) return res.status(400).send(error);
res.status(200).json({ results });
});
});
module.exports = router;
If the problem persists:
Try to add the wild card text indexing to see if the problem is within it as follows:
VideoSchema.index({'$**': 'text'});
If so, then drop the collection for a fresh start on the indexing and then append your text indexes like this:
VideoSchema.index({ title: "text", description: "text" });
Create new dummy items and then check again.
Make sure you read the exceptions shown in the MongoDB documentation:
https://docs.mongodb.com/manual/reference/operator/query/text/
It seems that I resolved the problem. I noticed that in the express js the 'query' keyword is used for 'get' request params, so I decided to change this variable to 'search', so now it is like underneath and it is working!
router.get("/", (req, res) => {
const { search } = req.query;
Video.find(
{ $text: { $search: search } },
{ score: { $meta: "textScore" } }
)
.sort({ score: { $meta: "textScore" } })
.exec(function (error, results) {
if (error) return res.status(400).send(error);
res.status(200).json({ results });
});
});
But I've noticed that I'm getting only one video instead of two that contains the 'obs' in the title, so now I will need to deal with that.
Thank you so much for your time and effort!

Mongoose.populate() producing no change in the model

Listing Schema:
const mongoose = require('mongoose');
const listingSchema = new mongoose.Schema({
title: String,
name: String,
tel: String,
service: String,
description: String,
location: Object,
isAvailible: Boolean,
canTravel: Boolean,
distance: Number,
isPublic: { type: Boolean, default: true},
pro: { type: mongoose.Types.ObjectId, ref: 'User' }
}, { timestamps: true });
const Listing = mongoose.model('Listing', listingSchema);
module.exports = Listing;
Request to DB:
Listing.find({ 'title': { '$regex' : service, '$options' : 'i' } , isPublic: { $gte: true }}, async (err, listings) => {
if (err) { return next(err); }
await listings[0].populate('pro');
console.log(listings[0].pro);
res.render('search', {
title: 'Search',
listings: listings,
search: {
service: service,
zip: zip
}
});
});
Screenshot of console
I'm also curious what is the best way to populate an array of models, however, I can't even get it to populate one. Any thoughts?
can you please tye execPopulate() method
try below code
Listing.find({ 'title': { '$regex' : service, '$options' : 'i' } , isPublic: { $gte: true }}, async (err, listings) => {
if (err) { return next(err); }
const listing=await listings[0].populate('pro').execPopulate();
console.log(listing.pro);
res.render('search', {
title: 'Search',
listings: listing,
search: {
service: service,
zip: zip
}
});
});
assignment to constant variable before the populate may work like so:
Listing.find({ 'title': { '$regex' : service, '$options' : 'i' } , isPublic: { $gte: true }}, async (err, listings) => {
if (err) { return next(err); }
const listings = await listings[0].populate('pro');
console.log(listings.pro);
res.render('search', {
title: 'Search',
listings: listings,
search: {
service: service,
zip: zip
}
});
});

How can I refer to the schema I was trying to save in nestjs/mongoose?

I am trying to encrypt some passwords and get its salt before saving my model to mongoose in Nestjs, but simply using this to refer to the schema itself doesn't yield any results as it refers to the UserSchemaProvider object itself, instead of the current model I'm trying to save.
My schema provider:
export const UserSchemaProvider = {
name: 'User',
useFactory: (): mongoose.Model<User> => {
const UserSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
birthday: { type: Date, required: true },
celphoneNumber: String,
whatsapp: Boolean,
promo: Object,
status: String
});
UserSchema.pre<User>('save', async (next) => {
const user = this;
console.log(user);
if (user.password) {
const salt = await bcrypt.genSalt();
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) return next(err);
user.password = hash;
next();
});
}
});
return UserSchema;
},
};
and my user Module comes below:
#Module({
imports: [
MongooseModule.forFeatureAsync([
UserSchemaProvider]),
HttpModule
],
controllers: [UsersController],
providers: [UsersService, Validator, ValidationPipe, IsEmailInUseConstraint, GoogleRecaptchaV3Constraint],
})
:Nest Platform Information:
platform-express version: 6.10.14
mongoose version: 6.3.1
common version: 6.10.14
core version: 6.10.14
Your pre hook handler shouldn't be an arrow function () => {}. mongoose handler will need to have the execution context to point to a current document being saved. When using arrow function, your execution context of the pre hook is no longer the document, hence, this inside of the handler isn't the document itself anymore.
export const UserSchemaProvider = {
name: 'User',
useFactory: (): mongoose.Model<User> => {
const UserSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
birthday: { type: Date, required: true },
celphoneNumber: String,
whatsapp: Boolean,
promo: Object,
status: String
});
UserSchema.pre<User>('save', async function(next) { // <-- change to a function instead
const user = this;
console.log(user);
if (user.password) {
const salt = await bcrypt.genSalt();
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) return next(err);
user.password = hash;
next();
});
}
});
return UserSchema;
},
};