How can I update an array of two Date() objects in mongodb? - javascript

I'm sending an array of dates parsed to the popular ISO format (8601,) to my server as strings.
In my mongoose model I have an array specified in my schema for 2 date objects:
availability: [
{
from: {
type: Date,
},
to: {
type: Date,
},
},
],
I am trying to populate the array from my controller, like so:
const updateAvailability = async (req, res) => {
if (!req.user.admin) {
return res
.status(401)
.json({ message: "Not authorized." });
}
try {
const admin = await User.findOne({ admin: true });
const d1 = new Date(req.body[0]);
const d2 = new Date(req.body[1]);
admin.availability = [d1, d2];
console.log(admin.availability);
admin.save();
return res
.status(200)
.json({ message: "Availability updated." });
} catch (err) {
console.error(err);
}
};
The output I'm getting from the console.log is:
[0] [
[0] { _id: new ObjectId("63f14761ad66201dd83b43e3") },
[0] { _id: new ObjectId("63f14761ad66201dd83b43e4") }
[0] ]
The dates are not on here!
Can anybody tell me the correct way of doing this?
Thanks for reading.
Any help appreciated

Related

How can I update element in array in mongo db node js

How can I update element in array in one of my objects in database.
I need to update just the page value in certain title. In last viewed I need to have multiple values.
My model looks something like this:
const userSchema = new Schema({
username: {
type: String,
required: true,
unique: true
},
lastViewed: [
{
title:{
type: String,
required: true
},
page:{
type: Number
}
}
]
});
This is my node js express app
router.post("/api/update",async(req, res)=>{
let {token} = req.body;
let {itemId, page} = req.body;
if (token == null){
res.json({status: 'error', error: 'wrong token'});
}
try {
let userVerification = jwt.verify(token, JWT_SECRET);
let _id = userVerification.id;
let item = await Item.findById(itemId);
await User.updateOne({_id},{
$set: {
lastViewed: [
{
title: item.title,
page: page
}
]
}
},
{ upsert: true }).catch(err => console.log(err));
res.json({status: 'ok' });
} catch(err){
res.json({status: 'error', error: 'wrong token'});
}
});
What do you think I should do
You point out the element you want to update rather than point to the whole array. The $ operator is used to identify the array element you want to update.
Look at the documentation
You can write the update query by
User.update({
"_id": mongoose.Types.ObjectId(_id),
"lastViewed.title": "n3"
},
{
$set: {
"lastViewed.$.page": 4
}
})
Check the sample

MongoDB $set to update subarray, adding new entry instead of updating

im trying to update an oject in a sub-array and instead of replacing and updating the data. it adds a new enetry.
controller.js:
const updateSubCategory = asyncHandler(async (req, res) => {
const {
dataArray
} = req.body
const categories = await Category.find({})
if (categories) {
await Category.updateOne({
"SubCats._id": req.params.id
}, {
"$set": {
SubCats: {
name: dataArray[0],
image: dataArray[1]
}
}
}, {
"multi": true
})
res.json({
message: 'sub-category updated'
})
} else {
res.status(404)
throw new Error('Error')
}
})
I think you need this, but i am not sure, if you dont need this, if you can give sample data and expected output in json.
You can try an example PlayMongo
It updates the fields inside not replace all the embeded document (your query does that).
const updateSubCategory = asyncHandler(async (req, res) => {
const {
dataArray
} = req.body
const categories = await Category.find({})
if (categories) {
await Category.updateOne({
"SubCats._id": req.params.id
}, {
"$set": {
"SubCats.name" : dataArray[0],
"SubCats.image" : dataArray[1]
}
}
}, {
"multi": true
})
res.json({
message: 'sub-category updated'
})
} else {
res.status(404)
throw new Error('Error')
}
})

Express doesn't get another user with .map()

I came to a problem, where I can create conversations with multiple people 2 and so on. However, I can't understand why it doesn't store data to seperate User models.
Here is a code that you only need to know:
router.post(
"/",
auth,
[
check("conversators", "There should be at least two conversators").isLength(
{ min: 2 }
),
],
async (req, res) => {
const { conversators } = req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
let conversation = new Conversation({
user: req.user.id,
conversators: conversators,
});
await conversators.map(async (conversator) => {
let user = await User.findById(conversator);
let newData = user;
newData.conversations.push(conversation.id);
console.log('Created data', newData);
let newUser = await User.findOneAndUpdate(
{ user: conversator },
{
$set: {
newData,
},
},
{ new: true }
);
await newUser.save();
console.log(newUser);
});
await conversation.save();
res.status(200).json(conversation);
} catch (error) {
console.error(error.message);
res.status(500).send("Server error.");
}
}
);
module.exports = router;
What I can assure is that this line: console.log('Created data', newData); prints the desired data. However, the next console: console.log(newUser); prints the same User model as the previous one.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
surname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
conversations: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "conversation",
},
],
date: {
type: Date,
default: Date.now,
},
});
module.exports = User = mongoose.model("user", UserSchema);
The reason might be the difference in search methods used to get a record for newData and newUser. You have used User.findById for newData, which will obviously return different objects for different ids. But User.findOneAndUpdate uses filter criteria that may satisfy several results, but only first will be returned. So it boldly depends on what that user field is.
Here is the part that I changed and started to see the data on MongoDB:
await conversators.map(async (conversator) => {
let user = await User.findById(conversator);
let newData = user;
newData.conversations.push(conversation.id);
new Promise(async (resolve, reject) => {
user = await User.findOneAndUpdate(
{ id: conversator },
{
$set: {
newData,
},
},
{ new: true }
);
return resolve;
})
return await user.save();
});
Posted on behalf of the question asker

findOneandReplace keeps giving error: "Error: The replacement document must not contain atomic operators."?

I am currently developing a Pokemon Team Builder app with a React frontend and an Express backend with MongoDB for the database.
As far as I can tell my TeamSchema has no such atomic operators? Here is my TeamSchema:
const mongoose = require('mongoose');
const TeamSchema = new mongoose.Schema({
name: {
type: 'String',
required: true,
unique: true,
},
team: [
{
name: { type: String },
types: [{ type: String }],
sprite: { type: String },
},
],
username: {
type: String,
required: true,
},
userId: {
type: String,
required: true,
},
});
const TeamModel = mongoose.model('Team', TeamSchema);
module.exports = TeamModel;
And the error gets thrown in this method when I attempt to call the findOneAndReplace method by finding a team that has a name and userId match.
const replaceTeam = async (req, res) => {
const { teamName: name, filteredTeam: team } = req.body;
const { username, _id: userId } = req.user;
const newTeam = new Team({ name, team, username, userId });
try {
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam);
console.log(replacedTeam);
res.status(200).json({ message: 'Team was successfully overwritten!' });
} catch (err) {
console.log(err);
res.status(500).json({ message: 'An error occurred while updating the team.' });
}
};
This has been a real headscratcher here and I am not sure what is going wrong here. I have only started using mongoose a couple of weeks ago, so I wonder if it's something fundamental I am misunderstanding here.
The Mongoose function findOneAndReplace expects a document object passed in. See the below code.
details.findOneAndReplace(
{ location: "New York" },
{ name: "Sunny", age: 292, location: "Detroit" },
function(err, result) {
if (err) {
res.send(err);
} else {
res.send(result);
}
}
);
Change
const newTeam = new Team({ name, team, username, userId })
to
const newTeam = {name, team, username, userId}
Also as in the other poster's code, add the new: true option to the call as follows by changing
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam);
to
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam, { new: true });
otherwise the original document will be returned into replacedTeam
You can just use findOneAndUpdate and update all the fields with new data. You can do it like this:
const replaceTeam = async (req, res) => {
const { teamName: name, filteredTeam: team } = req.body;
const { username, _id: userId } = req.user;
try {
const replacedTeam = await Team.findOneAndUpdate({ name, userId }, { name, team, username, userId }, {new: true});
console.log(replacedTeam);
res.status(200).json({ message: 'Team was successfully overwritten!' });
} catch (err) {
console.log(err);
res.status(500).json({ message: 'An error occurred while updating the team.' });
}
};

NodeJS - controller - multiple queries from collections + forEach

Getting messy with promises and async & await. Would like to get your advice, what is the best practice for below controller case? Now it wont get user details before passing data to client.
I am doing a simply API endpoint to server side with NodeJS. Controller is making two queries to complete simply results as json object for client. Yes there is this useless run[] array but it is used when building whole event results :)
exports.runresults3 = function(req, res) {
// Route is passing ID
console.log('id', req.params.id);
// Test results object to fill
const resultsParams = {
run: [{
runid: {},
endtimeid: [],
userid: [],
endtime: [],
username: [],
}]
}
// Run objectid
resultsParams.run[0].runid = req.params.id;
// Get the endtimes by location/stage
Endtime.find({stage: req.params.id})
.then(data => {
// First loop
data.forEach(value => {
resultsParams.run[0].endtimeid.push(value._id);
resultsParams.run[0].userid.push(value.user);
resultsParams.run[0].endtime.push(value.endtime);
})
})
.then(() => {
// Second loop to get user details for results object
resultsParams.run[0].userid.forEach((userId, i) => {
TempUser.findById(userId)
.then(userdetails => {
console.log('userdetails.name', userdetails.name);
resultsParams.run[0].username.push(userdetails.name);
});
})
})
.then(() => {
res.json(resultsParams);
});
}
//////////// client side will get json as follows
{
"run": [
{
"runid": "5ae850d51717862590dc30d4",
"endtimeid": [
"5aec482d98555332145eccd3",
"5aec48c098555332145eccd6",
"5aec4a2c98555332145eccda",
"5aec4ab398555332145eccdd",
"5aec4bb998555332145ecce1",
"5aec4e42c3bcbb196c8474fc",
"5aec4e44c3bcbb196c8474fe",
"5aec4e45c3bcbb196c847500"
],
"userid": [
"5aec13b098555332145eccbe",
"5ae869c797e54a37f498c98f",
"5aec4a1298555332145eccd7",
"5aec4a1298555332145eccd7",
"5aec4ba698555332145eccde",
"5aec13a598555332145eccbc",
"5ae869c797e54a37f498c98f",
"5aec13b098555332145eccbe"
],
"endtime": [
24424,
3280,
11858,
38874,
5738,
40384,
50906,
36717
],
"username": []
}
]
}
exports.runresults3 = function(req, res) {
// Route is passing ID
console.log('id', req.params.id);
// Test results object to fill
const resultsParams = {
run: [{
runid: {},
endtimeid: [],
userid: [],
endtime: [],
username: [],
}]
}
// Run objectid
resultsParams.run[0].runid = req.params.id;
Endtime.find({stage: req.params.id})
.then(data => {
return Promise.all(data.map(record => {
TempUser.findById(record.user)
.then(userdetails => {
return {
endtimeid: record._id,
userid: record.user,
endtime: record.endtime,
username: userdetails.name
};
})
}))
.then(detailRecords => {
return detailRecords.reduce((acc, curr) => {
acc.endtimeid.push(curr.endtimeid);
acc.userid.push(curr.userid);
acc.endtime.push(curr.endtime);
acc.username.push(curr.username);
return acc;
},resultsParams.run[0]);
})
.then(() => {
res.json(resultsParams);
});
});
}
If you have a requirement to accumulate things into the "associative array" style of things in this result object, this is probably the way I would do it.
With that said, I would most likely try to return things as an array of run results - something more like the following...
{
"run": [
{
"runid": "5ae850d51717862590dc30d4",
"results": [
{
"endtimeid": "5aec482d98555332145eccd3",
"userid": "5aec13b098555332145eccbe",
"endtime": 24424,
"username": "User123"
},
{
"endtimeid": "5aec48c098555332145eccd6",
"userid": "5ae869c797e54a37f498c98f",
"endtime": 3280,
"username": "User234"
}
]
}
]
}
I believe the following code should do that.
exports.runresults3 = function(req, res) {
// Route is passing ID
console.log('id', req.params.id);
// Test results object to fill
const resultsParams = {
run: [{
runid: {},
results: []
}]
}
// Run objectid
resultsParams.run[0].runid = req.params.id;
Endtime.find({stage: req.params.id})
.then(data => {
return Promise.all(data.map(record => {
TempUser.findById(record.user)
.then(userdetails => {
return {
endtimeid: record._id,
userid: record.user,
endtime: record.endtime,
username: userdetails.name
};
})
}))
})
.then(results => resultsParams.run[0].results = reuslts)
.then(() => res.json(resultsParams));
}
There could certainly be more readability refactors to be done, and the fact that the last step of populating the result doesn't use the result of the previous flow troubles me a bit - but I try to avoid side effects whenever possible, so that may just be me.
Here is the edited versions as adviced :) But still missing the user details, it looks like promise.all isnt waiting "TempUser.findById(record.user)" query.
1 I created a fresh event, run, 3 users and 3 results.
2 Then I set some logs along db calls
3 Then I did a get request with postman
Log from localhost look like this after Postman GET request:
$ node app
Server started on 5000
Mongodb connected
id 5aeebd8a1b5ddf1424c25194
Get endtimes from database
[ { created: 2018-05-06T08:32:46.359Z,
_id: 5aeebdae1b5ddf1424c25199,
endtime: 23204,
user: 5aeebd751b5ddf1424c25191,
stage: 5aeebd8a1b5ddf1424c25194,
__v: 0 },
{ created: 2018-05-06T08:32:49.414Z,
_id: 5aeebdb11b5ddf1424c2519b,
endtime: 17149,
user: 5aeebd7b1b5ddf1424c25192,
stage: 5aeebd8a1b5ddf1424c25194,
__v: 0 },
{ created: 2018-05-06T08:32:51.769Z,
_id: 5aeebdb31b5ddf1424c2519d,
endtime: 10840,
user: 5aeebd7f1b5ddf1424c25193,
stage: 5aeebd8a1b5ddf1424c25194,
__v: 0 } ]
Set results to object
[ undefined, undefined, undefined ]
This should be the last one at chain?
Get user details from database
{ date: 2018-05-06T08:31:49.673Z,
_id: 5aeebd751b5ddf1424c25191,
name: 'Firstame Lastname1',
__v: 0 }
2. Get user details from database { date: 2018-05-06T08:31:55.562Z,
_id: 5aeebd7b1b5ddf1424c25192,
name: 'Firstame Lastname2',
__v: 0 }
2. Get user details from database { date: 2018-05-06T08:31:59.906Z,
_id: 5aeebd7f1b5ddf1424c25193,
name: 'Firstame Lastname3',
__v: 0 }
That is made by following code:
exports.runresults3 = function(req, res) {
// Route is passing ID
console.log('id', req.params.id);
// Test results object to fill
const resultsParams = {
run: [{
runid: {},
results: []
}]
}
// Each run objectid
resultsParams.run[0].runid = req.params.id;
Endtime.find({stage: req.params.id})
.then(data => {
console.log('1. Get endtimes from database', data);
return Promise.all(data.map(record => {
TempUser.findById(record.user)
.then(userdetails => {
console.log('2. Get user details from database', userdetails);
return {
endtimeid: record._id,
userid: record.user,
endtime: record.endtime,
username: userdetails.name
};
})
}))
})
.then(results => {
console.log('3. Set results to object', results);
console.log('This should be the last one at chain?');
resultsParams.run[0].results = results;
})
.then(() => res.json(resultsParams));
}

Categories