How do I update an array within an array in mongodb - node.js

In the function below, if the user, topic and subtopic do not exist, a new testRecord is inserted into the testRecords array of the User collection. This works fine. On the other hand, if the combination of the user, topic and subtopic exist, only the scores array of testRecords is to be populated with the new scores. This is not working. The code segment that begins: if(existingTopicSubtopic) is the part that is not working.
exports.updateUserTestRecord = async (req, res) => {
console.log('req.body', req.body)
const {topicName, subtopicName, rightAnswers, wrongAnswers, date} = req.body;
const {
userId
} = jwt.verify(req.headers.authorization, process.env.JWT_SECRET);
const existingTopicSubtopic = await User.findOne(
{
_id: userId,
'testRecords.topic': topicName,
'testRecords.subtopic': subtopicName
},
)
let user;
if(existingTopicSubtopic) {
console.log('EXISTING USER')
user = await User.findOneAndUpdate(
{
_id: userId,
'testRecords.topic': topicName,
'testRecords.subtopic': subtopicName
},
{
$addToSet : {
testRecords : [
{
'scores.$.rightAnswers': rightAnswers,
'scores.$.wrongAnswers': wrongAnswers,
'scores.$.date': date,
}
]
}
},
)
} else {
console.log('NOT EXISTING USER')
user = await User.findOneAndUpdate(
{
_id: userId,
},
{
$addToSet : {
testRecords : [
{
topic: topicName,
subtopic: subtopicName,
scores: [
{
rightAnswers,
wrongAnswers,
date,
}
]
}
]
}
},
)
}
}

For inner array operation, we need to use $[identifier] like $[element]. In your case:
if (existingTopicSubtopic) {
await User.findOneAndUpdate(
{
_id: userId,
"testRecords.topic": topicName,
"testRecords.subtopic": subtopicName,
},
{
$addToSet: {
"testRecords.$[element].scores": {
rightAnswers: rightAnswers,
wrongAnswers: wrongAnswers,
date,
},
},
},
{
arrayFilters: [
{
"element.topic": topicName,
"element.subtopic": subtopicName,
},
],
}
);
} else {
await User.findOneAndUpdate(
{
_id: userId,
},
{
$addToSet: {
testRecords: {
topic: topicName,
subtopic: subtopicName,
scores: [
{
rightAnswers,
wrongAnswers,
date,
},
],
},
},
}
);
}

Related

MongoDb aggregation for daily number of views and downloads

const fetchSummary = expressAsyncHandler(async (req, res) => {
//GET DAILY SUMMARY
const dailySummary = await Post.aggregate([
{
$group: {
_id: { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" } },
downloads: { $sum: "$downloadCount" },
totalViews: { $sum: "$numViews" },
},
},
{ $sort: { _id: -1 } },
]);
res.send({ dailySummary });
});
Can someone please help me out here, I'm trying to fetch sum total for daily views and downloads for a post
And here is my result
"dailySummary": [
{
"_id": "2023-01-07",
"downloads": 49,
"totalViews": 227
},
{
"_id": "2023-01-06",
"downloads": 41,
"totalViews": 605
},
{
"_id": "2023-01-05",
"downloads": 0,
"totalViews": 0
}
],
And this result is a wrong
number of views for today is not even up 40,and downloads 10
//============
// Fetch single Post
//============
const fetchPostCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
validateMongodbId(id);
try {
const post = await Post.findById(id).populate("user comments");
//Updating Number of views
await Post.findByIdAndUpdate(
id,
{
$inc: { numViews: 1 },
},
{ new: true }
);
res.json(post);
} catch (error) {
res.json(error);
}
});
This is how I'm fetching post details

Remove one field from mongoDB collection

I have a mongoDB collection which I use with a mongoose Schema :
const balanceSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId, ref: 'user'
},
incomes: { Number },
fees: { Number },
},
{ strict: false })
I use the strict mode to false, so I can push any 'key' I want with its value.
I would like to delete just one of the "incomes" category, but I can't specify the line because there is no 'defined key'.
Here is an exemple of the data inside :
{
"_id": {
"$oid": "60c763df3d260204865d2069"
},
"incomes": {
"income1": 1300,
"anyKeyNameIWant": 400
},
"fees": {
"charge1": 29,
"charge2": 29,
"chargetest": 29,
"charge7": 29
},
"__v": 0,
}
I tried this, but no success :
module.exports.deleteOneBalance = (req, res) => {
let data = req.body
if (!ObjectID.isValid(req.params.id))
return res.status(400).send('ID unknown : ' + req.params.id);
BalanceModel.update(
{ _id: req.params.id },
{
$unset: { "incomes.salairetest": "400" }
}), (err, docs) => {
if (!err) res.send('Deleted. ' + data)
else console.log('Error : ' + err)
}
}
Any idea ?
There are several ways to delete fields with dynamic field names.
One solution is this one:
var unset = {};
unset["incomes." + "anyKeyNameIWant"] = null;
db.balanceModel.updateOne({ _id: req.params.id }, { $unset: unset })
Or you can use an aggregation pipelinie like this:
db.balanceModel.updateOne(
{ _id: req.params.id },
[
{ $set: { incomes: { $objectToArray: "$incomes" } } },
{ $set: { incomes: { $filter: { input: "$incomes", cond: { $ne: ["$$this.k", "anyKeyNameIWant"] } } } } },
{ $set: { incomes: { $arrayToObject: "$incomes" } } }
]
)
If you want to remove/unset specific value/(s) from the documents then you have to provide the complete path of that key.
Let's take an example if you want to remove anyKeyNameIWant then your path will be incomes.anyKeyNameIWant and the update query will be like this
db.sample.update(
{
_id: ObjectId("60c763df3d260204865d2069")},
{
$unset: {"incomes.anyKeyNameIWant":""}
})
In your code, you are passing an object having the key incomes in $unset which will remove the complete incomes key from the document
Here is the link to the official document in case you want more details $unset

what will be mangoose equivalent of this oracle query

I am trying to convert this oracle query to Mongoose equivalent. I am trying to do it using findOneAndUpdate, but don`t know how to convert this NVL thing.
here is my query
Update Authentication
Set Datetime = Sysdate,
Isactive = 'Y',
Version = '1.0.0',
Firstlogin = Nvl(Firstlogin, Sysdate),
Lastlogin = Sysdate
This is what i have tried. I have used $cond and inside it ifNull, but its not working, Also used $cond and then and if condition to check for null, bot no luck at all.
function createOrUpdate() {
let filter = { count: { $lt: 4 } };
let options = { upsert: true, returnOriginal: false, multi: true };
let userID = "asdfasdf"
let testUser = {
userId: userID,
userNick: "user.nickName",
reqTime: 1,
}
let update = {
$inc: { count: 1 }, $addToSet: {
"users": {
$each: [testUser],
}
},
$cond: [
{
$ifNull: ['$roomName', true], $set: {
roomName: "1112"
},
$set: {
roomName: "1112"
}
},
]
};
// { },
// let update = {
// $cond: {
// if: {
// $or:[{$eq: ["roomName",null]},{$eq: ["roomName",""]}]
// },
// then: {
// $set: {
// roomName: "varxy"
// }
// },
// else: {
// $set: {
// roomName: "roomName"
// }
// }
// },
// $inc: { count: 1 },
// "$set": { "users":{ "userId": userID, "user": testUser } }
// }
ModelRooms.findOneAndUpdate(
filter,
update,
options, function (err, doc) {
if (err) {
logger.error(`Error:: ${err}`);
}
else {
logger.info(`User added to room`);
}
});
}

mongodb insert data into the array of objects and update it

I need to make a vote, it looks like an array of objects, look like the user’s ID and the value that he set.
If the user has already voted, but changed his value, you need to change the value of the rate in the array of objects for this user.
I need to make an array of objects into which data will be inserted like this {rate: 3, user: "asdr2r24f2f42f24"} and if the user has already voted in this array, then you need to change the value rate of the given user
I already tried to do something, but it seems to me you can write something better, can you help?
JSON https://jsoneditoronline.org/?id=442f1dae0b2d4997ac69d44614e55aa6
router.post('/rating', (req, res) => {
console.log(req.body)
// { id: 'f58482b1-ae3a-4d8a-b53b-ede80fe1e225',
// rating: 5,
// user: '5e094d988ddbe02020e13879' }
Habalka.find({
_id: req.body.id
})
.then(habalka => {
// here I need to check whether the user has already voted or not, and from this whether to add an object with it or update the number
Habalka.updateOne(
{_id: req.body.id},
{$push: {rating: {rate: req.body.rating, user: req.body.user}}}
)
.then(e => {
console.log(e)
})
});
});
Schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const HabalkaSchema = new Schema({
_id: {
type: String
},
bio: {
firstname: String,
lastname: String,
middlename: String,
company: String
},
rating: [
],
files: [
{
_id: {
type: String
},
destination: {
type: String
},
filename: {
type: String
},
path: {
type: String
},
folder: {
type: String
},
info: {
size: {
type: Number
},
mimetype: {
type: String
},
encoding: {
type: String
},
originalname: {
type: String
},
fieldname: {
type: String
},
},
date: {
type: Date,
default: Date.now
},
bio: {
type: Object
},
userId: String,
guessId: {},
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Habalka = mongoose.model('habalka', HabalkaSchema);
This is an aggregation query which inserts a new user or updates the rating of existing user in the rating array:
The req.body.id, req.body.user and req.body.rating are set as follows for the example code:
var ID = 1, INPUT_USER = "new user", INPUT_RATE = 5;
const matchStage = { $match: { _id: ID } };
const facetStage = {
$facet: {
new_user: [
{ $match: { "rating.user": { $not: { $eq: INPUT_USER } } } },
{ $addFields: { rating: { $concatArrays: [ "$rating", [ { user: "new user", rate: INPUT_RATE } ] ] } } },
],
user: [
{ $match: { "rating.user": INPUT_USER } },
{ $addFields: {
rating: {
$map: {
input: "$rating",
as: "r",
in: {
$cond: [ { $eq: [ "$$r.user", INPUT_USER ] },
{ user: "$$r.user", rate: { $add: [ "$$r.rate", INPUT_RATE ] } },
"$$r"
]
}
}
}
} }
]
}
};
const projectStage = {
$project: {
result: { $arrayElemAt: [ { $concatArrays: [ "$user", "$new_user" ] }, 0 ] }
}
};
const queryPipeline = [
matchStage,
facetStage,
projectStage
];
// Run the aggregation query and get the modified document
// after applying the user and rate data in the rating array.
// The result of the aggregation is used to update the collection.
col.aggregate(queryPipeline).toArray( ( err, docs ) => {
console.log("Aggregation output:");
console.log( JSON.stringify( docs[0] ) );
// Update the aggregate result to the collection.
col.updateOne( { _id: docs[0].result._id },
{ $set: { rating: docs[0].result.rating } },
( err, updateResult ) => {
console.log( 'Updated count: ', updateResult.matchedCount );
}
);
callback(docs);
} );
Example collection document:
{ "_id" : 1, "rating" : [ { "user" : "user1", "rate" : 2 } ] }
If the input is var ID = 1, INPUT_USER = "new user", INPUT_RATE = 5; the updated document will be:
{ "_id" : 1, "rating" : [ { "user" : "user1", "rate" : 2 }, { "user" : "new user", "rate" : 5 } ] }
If the input is var ID = 1, INPUT_USER = "user1", INPUT_RATE = 5; the updated document will be:
{ "_id" : 1, "rating" : [ { "user" : "user1", "rate" : 7 } ] }

Remove _Id from mongoose Aggregate response

I'm trying to remove the _Id from the returned documents, this is my code:
module.exports = function(app) {
// Module dependencies.
var mongoose = require('mongoose'),
Contacts = mongoose.models.Contacts,
api = {},
limit = 10;
api.contacts = function(req, res) {
Contacts.aggregate([{
$group: {
"_id": {
name: "$name",
city: "$city",
state: "$state"
}
}
}, {
$sort: {
AgencyTranslation: 1
}
}, {
$limit: req.query.limit | limit
}],
function(err, contacts) {
if (err) {
res.json(500, err);
} else {
res.json({
contacts: contacts
})
}
})
};
app.get('/api/contacts', api.contacts);
};
the current result-set looks like this:
{
"contacts":[
{"_id":{"name":"Joe","city":"ankorage","state":"AL"}},
{"_id":{"name":"Mark","city":"washington","state":"DC"}}
...
]
}
I tried to replace "_Id" with "$project", or $project, and adding "_Id": 0 to the object, as some have suggested elsewhere, but was not successful.
I also tried res.send(contacts), but that only stripped the super-object ('contacts').
Any suggestions are appreciated.
Like this
Contacts.aggregate( [
{ $group: { "_id": { name: "$name", city: "$city", state: "$state" } } },
{ $project: {_id: 0, name: '$_id.name', city: '$_id.city', state: '$_id.state'} },
{ $sort: { AgencyTranslation: 1 } },
{ $limit: req.query.limit | limit }
], function () {
});
Bunch of time but, here is the answer:
After making $group or $project, do this:
{ $unset: ["_id"] }

Resources