How can I make a subarray unique - mongodb

I am having an issue with the following
{
artist:"Macy Gray"
song:"I Try'"
station:"PERTHRadio"
timeplay:2020-07-17T10:39:00.000+00:00
__v:0
history:Array
0:"7320564F-76B2-40D0-A0E8-E3917148F567"
1:"7320564F-76B2-40D0-A0E8-E3917148F567"
}
Basically it's adding the same UUID twice in history.
I am using a findOneAndUpdate with $push.
The code I am using
const nowplayingData = {
"station": req.params.stationname,
"song": data[1],
"artist": data[0],
"timeplay":npdate
};
LNowPlaying.findOneAndUpdate(
nowplayingData,
{ $push: { history: [uuid] } },
{ upsert: true },
function(err) {
if (err) {
console.log('ERROR when submitting round');
console.log(err);
}
}
);

Usually when people experience an issue like this It's because the function / route the code is in is being run twice. (Again -usually- this is due to debugging where the debugger is firing an extra call or something of the sort).
Regardless if this happens to you while debugging or in production you can just start using $addToSet instead of push, this will guarantee duplicate values will not be pushed.
LNowPlaying.findOneAndUpdate(
nowplayingData,
{ $addToSet: { history: [uuid] } },
{ upsert: true },
function(err) {
if (err) {
console.log('ERROR when submitting round');
console.log(err);
}
}
);

Related

Mongo and mongoose $match _id in array

I have a frontend in React and a backend in express and node.
From FE i am calling an API on the server:
const { data: autotaskItems } = useApiCall({
url: `api/endpoint`,
method: 'post',
payload: {
filter: {
_id: {
$in: ["id1","id2"],
},
},
},
});
on the server:
router.post('/config-items/find', async (req, res) => {
const { filter } = req.body
// ConfigItem.find({ ...filter })
// .then(result => {
// res.status(200).json({ success: true, data: result });
// })
ConfigItem.aggregate([
{ $match: { ...filter }
}])
.then(result => {
res.status(200).json({ success: true, data: result });
})
But this doesn't work. I have found that aggregate doesn't "support" automatic conversion of ObjectId to string. If I have used find() and spread filter like above this will work just fine. However, I do need to use aggregate as I have a couple of lookups there too.
Anyone can help, please?
Also, if possible i would like to keep structure with spreading the filter object for match
Thank you
As per #Martinez's answer, this was resolved by the following:
Nice and simple :-)
ConfigItem.aggregate([{
"$addFields": {
"_id": {
"$toString": "$_id"
}
}
},
//rest of the query

Getting a $set error even though i am setting it?

chats.post('/approveUser', (req, res) => {
// let regex = new RegExp(req.body.requestId)
Chat.updateOne(
{ 'requests._id': req.body.requestId },
{ $set: {'approved': true} },
{ upsert: true }
)
.then(res => {
console.log('hi')
if (!res) {
res.status(404).send()
} else {
res.status(200).send(res)
}
})
.catch(err => {
console.log(err)
res.status(500).send(err)
})
})
Does anyone see why i'm getting an error? It says:
errmsg: '\'$set\' is empty. You must specify a field like so: {$set: {<field>: ...}}'
req.body.requestId is not undefined. This is the layout of each mongoDB document:
{
"_id":"5cd1f4aceb05c12298779345",
"title":"test 3",
"participants":[
{
"_id":"5cd1f4aceb05c12298779347",
"userEmail":"test#gmail.com"
},
{
"_id":"5cd1f4aceb05c12298779346",
"userEmail":"adam2.cole#northumbria.ac.uk"
}],
"chatType":"publicGroup",
"messages":[
{
"_id":"5cd254b591a0eb1de4a0863c",
"text":"hi",
"from":"test#gmail.com",
"dateTimeSent":"2019-05-08T04:01:57.732Z"
},
{
"_id":"5cd254b591a0eb1de4a0863b",
"text":"hi",
"from":"test#gmail.com",
"dateTimeSent":"2019-05-08T04:01:57.764Z"
}
],
"__v":0,
"requests":[
{
"approved":false,
"_id":"5cd2467891a0eb1de4a08631",
"userEmail":"test2#gmail.com"
}
]
}
The intention is to turn approved of any request to true. I'm failing to see why it won't work. Does anyone else see why?
Thanks.

MongoDB: aggregate error when use Match and Group

I've model
var LogSchema = mongoose.Schema({
userId: String,
pageId: String,
tagId: String
}, {
timestamps: true
});
In code,
Log.aggregate([
{
$match: {
createdAt: {
$gte: new Date(strFrom),
$lte: new Date(strTo),
}
},
//$group: { _id: "$userId" },
}
], function (err, logs) {
if (err) {
res.status(500).send({ message: "error retrieving logs." });
} else {
res.send(logs);
}
});
When I execute code use $match, that's ok. Then, I add $group, I receive error
Error: Arguments must be aggregate pipeline operators
So, I remove $match, only use $grooup, code run ok. So, when I use both $match and $group, receive errors.
Please, give me ideas
Thank so much
You have a missing brace.
Log.aggregate([{
$match: {
createdAt: {
$gte: new Date(strFrom),
$lte: new Date(strTo),
}
},
{ <-- missing brace around your group
$group: { _id: "$userId" },
}],
function (err, logs) {
if (err) {
res.status(500).send({ message: "error retrieving logs." });
} else {
res.send(logs);
}
});

How to update a field using its previous value in MongoDB/Mongoose [duplicate]

This question already has answers here:
Update MongoDB field using value of another field
(12 answers)
Closed 5 years ago.
I know I can do an update using $set:
Contact.update({
_id: request.id
}, {
$set: { name: newNameValue }
}, {
upsert: false
}, function(err) { ... });
But in this case, instead of passing newNameValue, I'd like to use the previous name value to compute the new one. Let's say I want to capitalize the old name, something like:
Contact.update({
_id: request.id
}, {
$set: { name: $old.name.toUpperCase() }
}, {
upsert: false
}, function(err) { ... });
I think this has already been answered here: How to add new data to current string in MongoDB?, so please, check that for a more detailed answer, but anyway, in short, you can't do that with a single query.
The way to do it using Mongoose, as shown in this official Mongoose example:
Contact.findById(request.id, (err, contract) => {
if (err) return handleError(err);
contract.name = contract.name.toUpperCase();
contract.save((err, contractContract) => {
if (err) return handleError(err);
...
});
});
as far as I know it's not possible. You would need to find and then update
Contact
.find({
_id: request.id
})
.exec(function(err, data) {
if (err) {
return ...;
}
Contact.findByIdAndUpdate(request.id, {
$set: {
name: data.name.toUpperCase()
}
}, {
new: true
}, function(err, doc) {
if (err) return ...;
console.log(doc)
});
}

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 });
}
);