Update fields in object with mongoose in mongodb - javascript

I have a simple collection in mongodb.
I use mongoose.
I have users model with one field type object.
And I want change this object dynamically. But this code doesn't work, I used findByIdAndUpdate(), findById, findOne(), findOneAndUpdate().
const UsersSchema = mongoose.Schema({
likes: {}
},
{ collection: 'users' });
const Users = mongoose.model('Users', UsersSchema);
const id ="5b4c540f14f353a4b9875af4";
const thems = ['foo', 'bar'];
Users.findById(id, (err, res) => {
thems.map(item => {
if (res.like[item]) {
res.like[item] = res.like[item] + 1;
} else {
res.like[item] = 1;
}
});
res.save();
});

I believe that, for solve this problem you need to add more fields in your schema:
I created one example with this data:
const UsersSchema = new mongoose.Schema({
likes :[
{
thema:{
type: String
},
likes_amount:{
type: Number
},
_id:false
}]
});
module.exports = mongoose.model('Users', UsersSchema);
I added one user:
var newUser = new UserModel({
likes:[{
thema:'foo',
likes_amount:1
}]
});
newUser.save();
Here the code that increment the likes per thema:
const thems = ['foo', 'bar'];
const userId = "5b4d0b1a1ce6ac3153850b6a";
UserModel.findOne({_id:userId})
.then((result) => {
var userThemas = result.likes.map(item => {
return item.thema;
});
for (var i = 0; i < thems.length; i++) {
//if exists it will increment 1 like
if (userThemas.includes(thems[i])) {
UserModel.update({_id: result._id, "likes.thema" : thems[i]}, {$inc: {"likes.$.likes_amount": 1}})
.then((result) => {
console.log(result);
}).catch((err) => {
console.log(err)
});
} else {
//if doesn't exist it will create a thema with 1 like
UserModel.update({_id: result._id},
{
$addToSet: {
likes: {
$each: [{thema: thems[i], likes_amount: 1}]
}
}})
.then((result) => {
console.log(result);
}).catch((err) => {
console.log(err)
});
}
}
}).catch((err) => {
console.log(err)
});
Database result of this increment:
I hope that it can help you.

Related

Why could the findOrCreate plugin create several documents at once?

I'm currently working on a MERN application with following/follower function for the users. I decided to create separate schemas for following and follower relationships detached from user schema.
Follower schema
const mongoose = require('mongoose');
const findOrCreate = require('mongoose-findorcreate');
const ObjectId = mongoose.Schema.Types.ObjectId;
const followerSchema = mongoose.Schema({
userId: {
type: ObjectId,
ref: 'User'
},
follower: {
type: [ObjectId],
ref: 'User'
}
});
followerSchema.plugin(findOrCreate);
const Follower = mongoose.model('Follower', followerSchema);
module.exports = { followerSchema, Follower };
Following schema
const mongoose = require('mongoose');
const findOrCreate = require('mongoose-findorcreate');
const ObjectId = mongoose.Schema.Types.ObjectId;
const followingSchema = mongoose.Schema({
userId: {
type: ObjectId,
ref: 'User'
},
following: {
type: [ObjectId],
ref: 'User'
}
});
followingSchema.plugin(findOrCreate);
const Following = mongoose.model('Following', followingSchema);
module.exports = { followingSchema, Following };
The problem however starts in my service where followings are created as supposed to. However, for followers mongoose create 6-8 documents at once with userIds that don't even exist in my db.
Here's the code of the followerService (it's the first function)
const { Follower } = require('../models/followerModel');
const { Following } = require('../models/followingModel');
const { User } = require('../models/userModel');
const mongoose = require('mongoose');
exports.changeFollowStatus = async (user, userId) => {
console.log({ userId: userId, user: user._id });
const newFollowing = await Following.findOrCreate({ userId: user._id }, (err, following, created) => {
console.log({following: following});
if (!err && !isFollowing(following, userId)) {
following.following.push(mongoose.Types.ObjectId(userId));
following.save();
User.findByIdAndUpdate(mongoose.Types.ObjectId(userId), {$inc: {follower: 1}});
} else {
const followingIndex = following.following.indexOf(mongoose.Types.ObjectId(userId));
following.following.splice(followingIndex, 1);
following.save();
User.findByIdAndUpdate(mongoose.Types.ObjectId(userId), { $inc: { follower: -1 } });
}
});
const newFollower = await Follower.findOrCreate({ userId: mongoose.Types.ObjectId(userId) }, (err, follower, created) => {
console.log({follower: follower});
if (!err && !isFollower(follower, user._id)) {
follower.follower.push(user._id);
follower.save();
User.findByIdAndUpdate(user._id, { $inc: { following: 1 } });
} else {
const followerIndex = follower.follower.indexOf(user._id);
follower.follower.splice(followerIndex, 1);
follower.save();
User.findByIdAndUpdate(user._id, { $inc: { following: -1 } });
}
});
};
exports.showFollowings = async (userId) => {
const followings = await Following.findOrCreate({ userId: mongoose.Types.ObjectId(userId) });
return followings.following;
};
exports.showFollowers = async (userId) => {
const followers = await Follower.findOrCreate({ userId: mongoose.Types.ObjectId(userId) });
return followers.follower;
};
const isFollowing = (newFollowing, userId) => {
return newFollowing.following.includes(mongoose.Types.ObjectId(userId));
};
const isFollower = (newFollower, userId) => {
return newFollower.follower.includes(userId);
}
Now, my following adding code and follower adding code look almost identical, but for some reason for followers, mongoose creates many more documents. The strange thing is that there is a follower document with the correct userId, but many other with random userIds get created which doesn't happen with followings which works as supposed to.
I also checked whether I pass the correct values and everything seems to be fine. But just for reference, here's the controller function from which I pass the values.
exports.changeFollowingStatus = async (req, res, next) => {
passport.authenticate('jwt', async (err, user, info) => {
if (err) {
console.error({ authError: err });
};
if (info !== undefined) {
console.error({ authError: info.message });
res.status(403).send(info.message);
} else {
console.log({params: req.params});
const userToFollow = req.params.id;
await FollowerService.changeFollowStatus(user, userToFollow);
res.status(200).send({ message: 'success' })
}
})(req, res, next);
};
Could anyone help me with this bug or at least navigate me towards the right direction? I can't seem to find solution to my problem. Thank you all in advance!

Mongoose find by a subdocument's value

I have 2 schemas
const schema = Schema({
headLine: {
type: String,
required: false
},
availableDays: [{
type: Schema.Types.ObjectId,
ref: AvailableDay
}]
}, {collection: 'providers', timestamps: true});
module.exports = mongoose.model("Provider", schema);
const schema = Schema({
day: {
type: String,
enum: ['Mondays','Tuesdays','Wednesdays','Thursdays','Fridays','Saturdays','Sundays']
},
timeFrom: String,
timeTo: String
}, {collection: 'availableDays', timestamps: true});
module.exports = mongoose.model("AvailableDay", schema);
Then in a route I call to a repository like this
router.get('/', async (req, res) => {
const match = {};
const sort = {};
const options = {};
// Arrange sort
if(req.query.sortBy){
const sortArray = JSON.parse(req.query.sortBy);
sortArray.map(e => sort[e[0]] = e[1] && e[1] === 'desc' ? -1 : 1);
options['sort'] = sort
}
// Get the pagination: limit how many, skip where it starts
if(req.query.limit) {
options['limit'] = parseInt(req.query.limit);
}
if(req.query.skip) {
options['skip'] = parseInt(req.query.skip);
}
const docs = await ProviderRepository.findBy(match, {}, options);
res.status(200).json(docs)
});
So what I need here is to filter providers for an AvailableDay monday and return the docs and count the total docs for pagination. I'm doing something like this without success
const findBy = async (params, projection = "", options = {}, callback) => {
const data = () => {
Provider.find(params, projection, options)
.populate([{path: 'user', match: {gender: 'F'}}]).exec((error, e) => {
if (error) {
console.log('error:', error)
return {error: error}; // returns error in json
}
return e.filter(i => i.user);
});
};
const total = await Provider.countDocuments(params).exec();
return {data(), total}
}
Thanks in advance
Use mongoose-aggregate-paginate-v2 and update your schema. If you use that package then you have to convert your queries from populate to aggregate style.
STEP 1: Update schema. Sample Schema:
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-aggregate-paginate-v2');
const Schema = mongoose.Schema;
let definition = {
headLine: {
type: String,
required: false
},
availableDays: [{
type: Schema.Types.ObjectId,
ref: AvailableDay
}]
};
let options = {
collection: 'providers'
};
let providerSchema = new Schema(definition, options);
providerSchema.plugin(mongoosePaginate);
module.exports = mongoose.model('providers', providerSchema);
STEP 2: Update controller. Sample code in controller:
router.get('/', async (req, res) => {
const match = {}
const sort = {
// Fill it based on your sort logic.
}
const paginateOptions = {
page: req.query.page, // Page number like: 1, 2, 3...
limit: req.query.limit // Limit like: 10, 15, 20...
};
ProviderRepository
.findBy(match, {}, sort, paginateOptions)
.then(() => {
res.status(200).json(docs)
})
.catch(() => {
res.status(HTTP_ERROR_CODE).json({ "error": "Your error message" })
})
});
STEP 3: Update manager. Sample code in manager:
const findBy = (match, projection, sort, paginateOptions) => {
if (!paginateOptions) {
paginateOptions = {
pagination: false
};
}
let providerAggregate = providerSchema.aggregate([
{
$lookup: {
from: "availableDays",
let: { days: "$availableDays" },
pipeline: [
{
$match: {
$expr: {
$in: ["$$availableDays", "$day"]
}
}
}
],
as: "availableDays"
}
},
{
$lookup: {
from: "users", // I dont know the collection name
let: { user_id: "$user" }
pipeline: [
{
$match: {
"gender": 'F',
$expr: {
$eq: ["$_id", "$$user_id"]
}
}
}
],
as: "users"
}
}
{ $sort: sort }
]);
return providerSchema
.aggregatePaginate(providerAggregate, paginateOptions)
.then(res => {
return res;
})
.catch(err => {
throw err;
});
};

MongoDB if field does not exist insert

i have category schema and i want to insert new category if category name does not exist. I tried something but i could not any response.
category.model
var mongoose = require("mongoose");
var categorySchema = mongoose.Schema({
name: {
type: String,
require: true
},
createdAt: {
type: Date,
default: () => {
return new Date();
}
}
});
module.exports = mongoose.model("category", categorySchema);
category insert function
var Category = require("../models/category.model");
exports.create = (req, res) => {
Category.find({ name: req.body.name }, (err, cat) => {
if (cat.length > 0) {
// i want to return exists message here
} else {
// i want to insert here if not exists
var category = new Category();
category.name = req.body.name;
category.save(err => {
if (err) {
return new response(null, err).error500(res);
}
return new response(category, null).created(res);
});
}
});
};
You can do it like this code below:
exports.create = async (req, res) => {
try {
let category = await Category.find({ name: req.body.name });
if(category) {
// return or do some stuff here
}
category = new Category(req.body);
category = await category.save();
return new response(category, null).created(res);
} catch(ex) {
console.log(ex.message);
return new response(null, ex).error500(res);
}
};
I hope it can help you.

Why is this mongoose 'findOne' query always returning null?

I am trying to find a specific document with mongoose in my Cosmosdb with this query described below.
const mongoose = require('mongoose');
var ObjectID = require('mongodb').ObjectID
const keys = require('../config/keys');
const Item = mongoose.model('items');
const uploadToBlob = async (containerName, blobName, json, id) => {
console.log('id', id)
Item.findOne({ _id: id }, (foundItem) => {
console.log(foundItem)
});
console.log('here')
Item.findOneAndDelete({ name: blobName });
};
I am successfully able to find the document when querying like this below.
const scanMongo = () => {
Item.find({
_id: {
$gt: ObjectID.createFromTime(Date.now() / keys.mongoPurgeInterval)
}}, (err, foundItems) => {
if(err) {
console.log("Oops", err);
return;
}
foundItems.forEach(item => {
JSON.stringify(item)
const blobName = item.name;
json = "'"+item+"'"
const id = item._id
uploadToBlob(keys.containerName, blobName, json, id);
});
});
}
This is what the object I'm looking for looks like when pulled from the query above.
[ { _id: 5cabd5c6e16288230cba2cf6, name: 'test', value: 1, __v: 0 } ]
For kicks, here my model.
const mongoose = require('mongoose');
const { Schema } = mongoose;
const itemSchema = new Schema({
name: String,
value: Number,
});
mongoose.model('items', itemSchema);
I'm befuddled. Any help would be bawler. Thanks!!!
Yeah, the first parameter is supposed to catch the error.
Item.findOne({ _id: id }, (error, foundItem) => {
console.log(foundItem)

Mongoose Find All then find all in other schema for each

in my application i need to find Categories, then i want Books for this Categories
const CategorySchema = mongoose.Schema({
displayname: String,
category: String
});
module.exports = mongoose.model('Category', CategorySchema);
}
const BookSchema = mongoose.Schema({
name: String,
img: String,
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Category'
}
});
module.exports = mongoose.model('Books', BookSchema);
i need a response like this
categories = [
{
category: 'News',
books: [{name:'',id''},{}..]
},
{
category: 'Sports',
books: [{name:'',id''},{}..]
}
];
so my code looks like this
Router.get('/categories/', (req, res) => {
Category.find({}, (err, categories) => {
Promise.all(categories.map(category => {
return Book.find({category: category._id}).then( books => {
return {
category: category,
books: books
};
})
})).then( categories => {
res.json(categories);
})
})
});
please some one know if have better solution for this query?
thanks all!
How about this?
Router.get('/categories/', (req, res) => {
Book.find().populate('category').then( books => {
var categories = {}
for(var i = 0; i < books.length; i++){
categories[books[i].category.displayname] = {
category: books[i].category.displayname
books: categories[books[i].category.displayname].books ? categories[books[i].category.displayname].books.push(books[i]) : [books[i]]
};
}
var result = [];
for(var key in categories){
result.push(categories[key])
}
return result;
});
});
It could be prettier, but it only makes one request to your db and should work the way you want
From http://mongoosejs.com/docs/populate.html
Book.
find({}).
populate('category').
exec(function (err, story) {
if (err) return handleError(err);
// Do other things
});

Categories

Resources