like/dislike mongodb using bulk - mongodb

let bulk = Card.collection.initializeOrderedBulkOp();
// if user doesn't exist in array
bulk.find({
'_id': mongoose.Types.ObjectId(card_id),
'likedBy': { '$ne': mongoose.Types.ObjectId(user_id) }
}).updateOne({
'$inc': { 'likes': 1 },
'$push': { 'likedBy': mongoose.Types.ObjectId(user_id) }
});
// if user exists in array
bulk.find({
"_id": mongoose.Types.ObjectId(card_id),
"likedBy": mongoose.Types.ObjectId(user_id)
}).updateOne({
"$inc": { "likes": -1 },
"$pull": { "likedBy": mongoose.Types.ObjectId(user_id) }
});
bulk.execute(function(response) {
console.log(response);
return res.json({
'state': true,
'msg': 'Successful',
})
});
The above is supposed to behave by incrementing or decrementing the likes field if the user id exists in the likedBy array.
However, both functions run, thus the last of the bulk gets to be the last action done. In the above, the end result is always zero.
I suspect the query matches a document always, thus the .updateOne() parts run on all.
Here's the schema:
var CardSchema = new Schema({
slug: {
type: String,
lowercase: true,
index: true
},
title: String,
content: String,
createdAt: {
type: Date,
default: Date.now,
},
updatedAt: {
type: Date,
},
options: [],
likedBy: [],
likes: Number,
createdBy: String,
featured: Boolean,
});
Is there a better mongo way to do the like/dislike thing?

Going with this for now. Too verbose, but works. I've created a like and dislike button separately in the UI, which calls two independent functions, but to same endpoint, with endpoint rigged like this:
let like = req.body.like;
// if request is a like
if (like) {
Card.update({
'_id': mongoose.Types.ObjectId(card_id),
'likedBy': { '$ne': mongoose.Types.ObjectId(user_id) }
}, {
'$inc': { 'likes': 1 },
'$push': { 'likedBy': mongoose.Types.ObjectId(user_id) }
}, function(err) {
if (err) {
console.log(err);
return res.json({
'state': false,
'msg': err
})
}
return res.json({
'state': true,
'msg': 'Liked',
})
})
} else if (!like) { // if request is dislike
Card.update({
'_id': mongoose.Types.ObjectId(card_id),
'likedBy': mongoose.Types.ObjectId(user_id)
}, {
'$inc': { 'likes': -1 },
'$pull': { 'likedBy': mongoose.Types.ObjectId(user_id) }
}, function(err,) {
if (err) {
console.log(err);
return res.json({
'state': false,
'msg': err
})
}
return res.json({
'state': true,
'msg': 'Disliked',
})
})
}
Then something like this makes the request,
likeCard(card_id: string, like: boolean) {
let param = {
card_id: card_id,
like: like
};
return this.http.post(AppSettings.API_ENDPOINT + '/card/like', JSON.stringify(param), { headers: this.headers })
.map((res) => {
return res
})
}

Related

Aggregate method for each document resulting from find method in mongodb

I want to execute this aggregate query:
db.collection('mycoll').aggregate([
{
$search: {
index: 'default',
text: {
query: 'night',
path: {
wildcard: '*',
},
},
},
},
])
})
for each document resulting from my find method:
here is my find method:
app.get('/', (req, res) => {
db.collection('subs').find(
{ name: { $regex: 'dexter', $options: '$i' } },
{ projection: { _id: 0, content: 0 } }
)
.toArray((err, result) => {
if (err) {
throw new err();
}
res.json({
length: result.length,
body: { result },
});
});
});
I know I probably have to use forEach and create a function, but I couldn't find out what to put in this function, I assume ## Heading ##it should be something like that:
.find(
{ name: { $regex: 'dexter', $options: '$i' } },
{ projection: { _id: 0, content: 0 } }
).forEach(()=>{})
.toArray((err, result) => {
if (err) {
throw new err();
}
res.json({
length: result.length,
body: { result },
});
});
});
You can achieve this in several different ways, here is the simplest code sample I could produce:
app.get('/', async (req, res) => {
const result = await db.collection('subs').find(
{name: {$regex: 'dexter', $options: '$i'}},
{projection: {_id: 0, content: 0}}
).toArray();
const finalResults = await Promise.all(result.map(async (each) => {
each.textSearchResults = await db.collection('mycoll').aggregate([
{
$search: {
index: 'default',
text: { // decide what your query is based on each document
query: each.name,
path: {
wildcard: '*',
},
},
},
},
])
return each
}))
res.json({
length: result.length,
body: {result},
});
});

Updating array of objects in Mongoose

I can't handle updating array of objects in my database, tried many options but nothing worked. Im pretty sure that the answer is obvious, but I couldn't manage it since wednesday.
Here is my kitSchema:
const kitSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
kit: {
type: Array,
required: true,
},
profiles: {
type: Array,
required: true,
},
});
module.exports = mongoose.model("Kit", kitSchema);
All users have their own document, and there are also profiles in it. I want to update single profile by passing the id of user and id of profile.
Example of data:
_id: 1,
email: "abc#mail",
password: "abc",
profiles: [
{
id: 1,
name: John
},
]
And here's my latest solution which doesn't work:
router.put("/profile/:id", async (req, res) => {
let kit = await Kit.findById(req.params.id, (error, data) => {
if (error) {
console.log(error);
} else {
console.log(data);
}
});
try {
await kit.profiles.findOneAndUpdate(
{ id: req.body.id },
{ name: req.body.name },
{ new: true },
(error, data) => {
if (error) {
console.log(error);
} else {
console.log(data);
}
}
);
try {
res.status(202).json({ message: "Changed" });
} catch (err) {
res.status(400).json({ message: err });
}
} catch (err) {
res.status(400).json({ message: err });
}
});
Could you give me a hand with this?
As always, after days of trying I've got answer 10 minutes after asking question. Here's what I came up with:
router.put("/profile/:id", async (req, res) => {
await Kit.findOneAndUpdate(
{ _id: req.params.id, profiles: { $elemMatch: { id: req.body.id } } },
{
$set: {
"profiles.$.name": req.body.name,
"profiles.$.profilePicture": req.body.profilePicture,
},
},
{ new: true, safe: true, upsert: true },
(error, data) => {
if (error) {
console.log(error);
} else {
console.log(data);
}
}
);
try {
res.status(202).json({ message: "Changed" });
} catch (err) {
res.status(400).json({ message: err });
}
});

Mongoose update only fields available in request body

I am trying to update one document using findOneAndUpdate and $set but I clearly missing something very crucial here because the new request is overwriting old values.
My Device schema looks like this:
{
deviceId: {
type: String,
immutable: true,
required: true,
},
version: {
type: String,
required: true,
},
deviceStatus: {
sensors: [
{
sensorId: {
type: String,
enum: ['value1', 'value2', 'value3'],
},
status: { type: Number, min: -1, max: 2 },
},
],
},
}
And I am trying to update the document using this piece of code:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ deviceId },
{ $set: req.body },
{},
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);
And when I try to send a request from the postman with the body that contains one or multiple sensors, only the last request is saved in the database.
{
"deviceStatus": {
"sensors": [
{
"sensorId": "test",
"status": 1
}
]
}
}
I would like to be able to update values that are already in the database based on req.body or add new ones if needed. Any help will be appreciated.
The documentation said:
The $set operator replaces the value of a field with the specified
value.
You need the $push operator, it appends a specified value to an array.
Having this documents:
[
{
_id: 1,
"array": [
2,
4,
6
]
},
{
_id: 2,
"array": [
1,
3,
5
]
}
]
Using $set operator:
db.collection.update({
_id: 1
},
{
$set: {
array: 10
}
})
Result:
{
"_id": 1,
"array": 10
}
Using $push operator:
db.collection.update({
_id: 1
},
{
$push: {
array: 10
}
})
Result:
{
"_id": 1,
"array": [
2,
4,
6,
10
]
}
you want to using $push and $set in one findOneAndUpdate, that's impossible, I prefer use findById() and process and save() ,so just try
let result = await Device.findById(deviceId )
//implementation business logic on result
await result.save()
If you want to push new sensors every time you make request then update your code as shown below:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ deviceId },
{
$push: {
"deviceStatus.sensors": { $each: req.body.sensors }
}
},
{},
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);
Update to the old answer:
If you want to update sensors every time you make request then update your code as shown below:
const deviceId = req.params.deviceId;
Device.findOneAndUpdate(
{ "deviceId": deviceId },
{ "deviceStatus": req.body.sensors },
{ upsert: true },
(err, docs) => {
if (err) {
res.send(err);
} else {
res.send({ success: true });
}
}
);

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!

How can I remove an object from an array?

I want to remove an object from an array. Here is the schema I'm working with:
event: {
invitees: {
users : [{
user: {
type: String,
ref: 'User'
},
}],
}
}
The query I'm using is listed below, but it isn't working. Basically, nothing happens when I run this script.
Event.update(
{"_id": req.params.event_id},
{"$pull": {"invitees.users.user": req.params.user_id}},
{safe: true, upsert: true},
function (err, data) {
if (err) {
console.log(err);
}
return res.json({ success: true });
}
);
What am I doing wrong?
The field of the $pull operator identifies the array to pull the elements from that match its query.
So your update should look like this instead:
Event.update(
{"_id": req.params.event_id},
// { $pull: { <array field>: <query> } }
{"$pull": {"invitees.users": {"user": req.params.user_id}}},
{safe: true, upsert: true},
function (err, data) {
if (err) {
console.log(err);
}
return res.json({ success: true });
}
);