add a array of keys in mongodb doesn't work - mongodb

I have a schema like this
{
'cr':[
{ key: 'key1' },
{ key: 'key2' }
]
}
function addCriteriaKey(id, key, callback) {
var options = {new: false, select: '_id'};
if (typeof key === 'string') {
Model.update({'uid': id}, {'$addToSet': {'cr': {'key': key}}}, options, function (err, data) {
if (err) callback(err, null);
else callback(null, data);
})
} else if (typeof key == 'object' && (key instanceof Array)) {
Model.update({'uid': id}, {'$addToSet': {'cr': {'key': {'$each': key}}}}, options, function (err, data) {
if (err) callback(err, null);
else callback(null, data);
})
}
}
what this method does is to add key into the 'cr' filed, if the key is a string, add it directly, if the key is a array of string, then add them all by suing the '$each' command
Adding a string works pretty well, the problem rises when adding to a array of string, I expect it could add one by one, but the result is different
For example:
addCriteriaKey('id',['111','222','333'],function(err,data){})
My expected result :
{
'cr':[
{ key: '111' }, { key: '222' }, { key: '333' }
]
}
However the result is :
{
'cr':[
{ key: {'$each':{'111','222','333'}} }
]
}
I wondered what is the problem for this?

either we can do in this way,
Model.addNotiCriteriaKey( 5, [{'key':'111'},{'key':'222'},{'key':'333'}], function(err,data){})
or using underscore to sort out array into key-value arrays
Model.addNotiCriteriaKey( 5, ['111','222','333'], function(err,data){})
function(id,key,callback){
_.map(key, function(element){ return {'key':element}; });
UserModel.update({'uid':id},{'$pull':{'cr':{ '$each': key}}},options,function(err,data){
if(err) callback(err,null);
else callback(null,data);
})
}

Related

Push data in mongodb use value from another field

I use mongoose for push data(use a value from rank_num field) to a field like this:
ListM.findOneAndUpdate({userId: req.body.userId},
[{
$push: {
listData: {
...req.body.musicToAdd,
pos: "$rank_num"
}
}
}])
And not work..., i dont know how to add value of another field in $push. Help me! thank you
If you want to add one value into your array field, try this. (rank_num is field name and req.body.musicToAdd is a value to be pushed.)
ListM.findOneAndUpdate(
{ userId: req.body.userId },
{ $push: { "rank_num": req.body.musicToAdd} },
{ safe: true, new: true }
).then((res) => {
console.log("updated result is", res);
}).catch((error) => {
console.log("error is", error);
});
If you want to push array to array field, try this. (listData is field name and req.body.musicToAdd is array to pushed.
ListM.findById(req.body.userId).then((res) => {
if (!res) return;
res.listData.push(...req.body.musicToAdd);
res.save();
return true;
})
catch((error) => {
console.log("error is", error);
});

Using mongoose lean after saving

So I am trying to add a key to a returned post. But I can't seem to get lean() to work. How can I manipulate the returned post after save?
I was thinking maybe I need to add lean to my findById like this Post.findById(req.params.id).lean().then(). But that didn't work, plus that only makes the first initial post mutable. It will say
post.save is not a function
if I do it like Post.findById(req.params.id).lean().then() as well
I want to only return the object about to be sent back to the client, I do not want they key saved in the actual document.
Post.findById(req.params.id)
.then(post => {
if (
post.likes.filter(like => like.user.toString() === req.user.id)
.length === 0
) {
return res
.status(400)
.json({ notliked: "You have not yet liked this post" });
}
// Get remove index
const removeIndex = post.likes
.map(item => item.user.toString())
.indexOf(req.user.id);
// Splice out of array
post.likes.splice(removeIndex, 1);
// Save
post.save().then(post => {
post["liked"] = false; <-------
res.json(post);
});
})
edit
Post.findById(req.params.id)
.lean()
.then(post => {
if (
post.likes.filter(like => like.user.toString() === req.user.id)
.length === 0
) {
return res
.status(400)
.json({ notliked: "You have not yet liked this post" });
}
// Get remove index
const removeIndex = post.likes
.map(item => item.user.toString())
.indexOf(req.user.id);
// Splice out of array
post.likes.splice(removeIndex, 1);
post["liked"] = false;
res.json(post);
// Save
post.save();
})
gives error
post.save is not a function
You can simply do this by searching for the req.user.id inside the indexOf likes array
Post.findOne({ _id: req.params.id }).lean().then((post) => {
if (post.likes.indexOf(req.user.id) !== -1) {
post.isLiked = true
}
post.isLiked = false
res.json(post)
})
Far better with the aggregation
Post.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(req.user.id) }},
{ "$addFields": {
"isLiked": { "$in": [mongoose.Types.ObjectId(req.user.id), "$likes"] }
}}
])
EDIT :- If you want to update document then use update query
Post.findOneAndUpdate(
{ _id: req.params.id },
{ $pull: { likes: { user: req.user.id } }},
{ new: true }
).then((post) => {
res.json(post)
})
Post Schema for likes
...
likes: [
{
user: {
type: Schema.Types.ObjectId,
ref: "users"
}
}
]
...

Loopback: remote method - multiple update

So, I want to have a function, that would reorder my records with orderId
I am passing an array like this:
[
{id: "5b1007aeb39c961a40540db9", orderId: 0},
{id: "5b150352184eb8471c34cf7c", orderId: 1}
]
and what I want is to multiple update all records with that ids with the orderId
so how can I do that?
I am trying something like this.... but it's not working, I imagine it is not chaining the promises at all ...
'use strict';
module.exports = function(Matchtimelineevents) {
Matchtimelineevents.reorder = function(items, cb) {
let count = 0;
if (typeof items !== 'undefined' && items.constructor === Array) {
items.forEach(item => {
Matchtimelineevents.update({'id': item.id, 'orderId': item.orderId}, function() {
count++;
console.log('UPDATING: ' + item.id, item.orderId, count);
});
});
// Matchtimelineevents.bulkUpdate(items, null, function() {
// console.log(items);
// });
}
cb(null, count);
};
Matchtimelineevents.remoteMethod('reorder', {
accepts: {
arg: 'items',
type: 'array',
},
returns: {
arg: 'count',
type: 'number',
},
http: {'verb': 'patch', 'path': '/reorder'},
description: 'Reorder the items by orderId',
});
};
What is the best way to do that?
Try to use updateAll with where like this:
const updateAllToPromise = item => new Promise((resolve, reject) => {
Matchtimelineevents.updateAll({
where: { id: item.id },
}, {
orderId: item.orderId,
}, function (err) {
if (err) resolve(false);
else resolve(true);
});
});
Matchtimelineevents.reorder = (items, cb) => {
if (!Array.isArray(items)) cb(new Error('Items not is a Array object'));
else {
Promise.all(items.map(item => updateAllToPromise(item)))
.then(items => cb(null, items.filter(item => item).length))
.catch(cb)
}
};

Mongoose update only the values that have changed

I have a PUT route to update value. I am hitting this route from two places. One is sending information about details and one about completed. The problem is that mongoose is updating booth even though it gets value from only one.
So if I send information about completed that it is true and latter I hit this route with new details (that dont have completed value) it will update completed also to false. How do I update just the value that was changed?
router.put('/:id', (req, res) => {
Todo.findOne({_id:req.body.id}, (err, foundObject) => {
foundObject.details = req.body.details
foundObject.completed = req.body.completed
foundObject.save((e, updatedTodo) => {
if(err) {
res.status(400).send(e)
} else {
res.send(updatedTodo)
}
})
})
})
EDIT:
Thanks to Jackson hint I was managed to do it like this.
router.put('/:id', (req, res) => {
Todo.findOne({_id:req.body.id}, (err, foundObject) => {
if(req.body.details !== undefined) {
foundObject.details = req.body.details
}
if(req.body.completed !== undefined) {
foundObject.completed = req.body.completed
}
foundObject.save((e, updatedTodo) => {
if(err) {
res.status(400).send(e)
} else {
res.send(updatedTodo)
}
})
})
})
const updateQuery = {};
if (req.body.details) {
updateQuery.details = req.body.details
}
if (req.body.completed) {
updateQuery.completed = req.body.completed
}
//or
Todo.findOneAndUpdate({id: req.body.id}, updateQuery, {new: true}, (err, res) => {
if (err) {
} else {
}
})
//or
Todo.findOneAndUpdate({id: req.body.id}, {$set: updateQuery}, {new: true}, (err, res) => {
if (err) {
} else {
}
})
Had a function similar to this my approach was this
const _ = require('lodash');
router.put('/update/:id',(req,res, next)=>{
todo.findById({
_id: req.params.id
}).then(user => {
const obj = {
new: true
}
user = _.extend(user, obj);
user.save((error, result) => {
if (error) {
console.log("Status not Changed")
} else {
res.redirect('/')
}
})
}).catch(error => {
res.status(500);
})
};
Taking new : true as the value you updating
It gets kinda ugly as the fields to be updated get increased. Say 100 fields.
I would suggest using the following approach:
try {
const schemaProperties = Object.keys(Todo.schema.paths)
const requestKeys = Object.keys(req.body)
const requestValues = Object.values(req.body)
const updateQuery = {}
// constructing dynamic query
for (let i = 0; i < requestKeys.length; i++) {
// Only update valid fields according to Todo Schema
if ( schemaProperties.includes(requestKeys[i]) ){
updateQuery[requestKeys[i]] = requestValues[i]
}
}
const updatedObject = await TOdo.updateOne(
{ _id:req.params.idd},
{ $set: updateQuery }
);
res.json(updatedObject)
} catch (error) {
res.status(400).send({ message: error });
}

How to print custom message in GraphQL

Hi I am working with GraphQl with the combination of es6.
While removing a particular record from graphql, I am getting details(values) of the deleted record, I want to print some custom message like "Record deleted". Please help me accordingly.
Here is my graphQL code:
removeUser:{
type: UserType,
args: {
_id: {
description: 'The _id of the user',
type: GraphQLString,
},
},
resolve: (obj, {_id}) =>{
return new Promise((resolve, reject) => {
User.findOne({_id:_id},(err,res)=> {
if(err || res == null) {
reject('User was not found')
}
else {
User.remove({_id: _id},(err,result)=>{
err ? reject(err) : reject('User removed successfully')
});
}
})
})
}
}
You declare UserType as the type of the removeUser field. Obviously, the string 'User removed successfully' is not a UserType; it's a String type.
Also, if the delete operation is successful, you should call resolve in the Promise, not reject.
I think something like this should work:
removeUser:{
type: GraphQLString,
args: {
_id: {
description: 'The _id of the user',
type: GraphQLString,
},
},
resolve: (obj, {_id}) =>{
return new Promise((resolve, reject) => {
User.findOne({_id:_id},(err,res)=> {
if(err || res == null) {
reject('User was not found')
}
else {
User.remove({_id: _id},(err,result)=>{
err ? reject(err) : resolve('User removed successfully')
});
}
})
})
}
}