Express Response: Sending an Array as JSON - arrays

I'm having an issue trying to get data from my backend express api. I'm using mongodb and mongoose too. Here's my code:
Code:
const show = (req, res) => {
const product = {}
product.array = new Array()
console.log(req.cart.product[1])
for (let i = 0; i < req.cart.product.length; i++) {
Product.find({_id: ObjectId(req.cart.product[i])},function(err,products){
if (err) {
res.sendStatus(500)
} else {
product.array.push(products)
console.log(product.array)
}
})
}
req.cart.product = product.array
res.json({
cart: req.cart.toJSON({ virtuals: true, user: req.user })
})
}
Console.logs:
[ [ { _id: 5952b57ea52d092b8d34c6b0,
name: 'test00000',
price: 0,
description: 'test',
__v: 0 } ] ]
[ [ { _id: 5952b57ea52d092b8d34c6b0,
name: 'test00000',
price: 0,
description: 'test',
__v: 0 } ],
[ { _id: 5952b57ea52d092b8d34c6b0,
name: 'test00000',
price: 0,
description: 'test',
__v: 0 } ] ]
URL Response:
{
"cart": {
"_id": "5953b153d2108941d15a7fe9",
"updatedAt": "2017-06-28T13:38:27.406Z",
"createdAt": "2017-06-28T13:38:27.406Z",
"owner": "595153ad6f18427ef38c416b",
"__v": 0,
"product": [],
"id": "5953b153d2108941d15a7fe9",
"editable": false
}
}
Everything in the console logs is what I want to return in the products array for my response but it won't populate the array when I push it. Any thoughts?

You are trying to call asynchronous code, (e.g. Db query) inside the synchronous code (e.g. for-loop). That's why it returns data to client once it gets the data for the first time. You can async or promise.all to solve the problem.
var async = require('async')
const show = (req, res) => {
const product = {}
product.array = new Array()
console.log(req.cart.product[1])
async.each(req.cart.product, function(id, cb){
Product.find({_id: ObjectId(id)},function(err,products){
if (err) {
cb(err)
} else {
product.array.push(products)
console.log(product.array)
cb()
}
})
}, function(err){
if (err) {
return res.sendStatus(500)
} else {
req.cart.product = product.array
return res.json({
cart: req.cart.toJSON({ virtuals: true, user: req.user })
})
}
})
}
Promise based solution:
const show = (req, res) => {
const product = {}
product.array = new Array()
console.log(req.cart.product[1])
const promises = []
req.cart.product.forEach(function(id){
promises.push(Product.find({_id: ObjectId(req.cart.product[i])}))
})
Promise.all(req.cart.product.map(function(id) {
return Product.find({_id: ObjectId(id)})
})).then(function(products){
req.cart.product = product.array
return res.json({
cart: req.cart.toJSON({ virtuals: true, user: req.user })
})
}).catch(function(err){
return res.sendStatus(500)
})
}

Related

Sort data in Axios response and set as useReducer payload

I'm calling data from an api into my react app using axios, like so:
const adapter = axios.create({
baseURL: "http://localhost:4000",
});
const getData = async () => {
const response = await adapter.get("/test-api");
return response.data;
};
This runs in a context, and I have a basic reducer function that I pass to the context:
const initialState = {
loading: true,
error: false,
data: [],
errorMessage: "",
};
const reducer = (state, action) => {
switch (action.type) {
case ACTIONS.FETCH_SUCCESS:
return {
...state,
loading: false,
data: action.payload,
};
case ACTIONS.FETCH_ERROR:
return {
...state,
error: true,
errorMessage: "Error loading data",
};
default:
return state;
}
};
The data I'm returning from my api is shaped like this:
{
"data": [
{
"id": 1,
"name": "Name 1",
"items": [
{
"id": "klqo1gnh",
"name": "Item 1",
"date": "2019-05-12"
}
]
},
{
"id": 2,
"name": "Name 2",
"items": [
{
"id": "klqo2fho",
"name": "Item 1",
"date": "2021-05-05"
},
{
"id": "klro8wip",
"name": "Item 2",
"date": "2012-05-05"
}
]
}
]
}
And I've written a simple function that finds the item whose nested array, items here, has the earliest date, using moment:
const sortDataByDate = (items) => {
return items.sort((first, second) => {
if (moment(first.items.date).isSame(second.items.date)) {
return -1;
} else if (moment(first.items.date).isBefore(second.items.date)) {
return -1;
} else {
return 1;
}
});
};
I then fetch everything in this function:
const fetchData = useCallback(async () => {
try {
await getData().then((response) => {
dispatch({
type: ACTIONS.FETCH_SUCCESS,
payload: response,
});
});
} catch (error) {
dispatch({ type: ACTIONS.FETCH_ERROR });
}
}, []);
I then run fetchData() inside a useEffect within my context:
useEffect(() => {
fetchData();
}, [fetchData]);
All this to say, here's the problem. My sortDataByDate function works sporadically; sometimes the data is ordered correctly, other times it's not. What I'd like to do is fetch my data, sort it with sortDataByDate, and then set the payload with that sorted data, so it's sorted globally rather than on a component level. Inside my App it seems to work consistently, so I think that I have missed something on a context level. Any suggestions?
You need to sort inner items first and get the earliest date:
const sortDataByDate = (items) => {
return items.sort((first, second) => {
if (moment(first.items[0].date).isSame(second.items[0].date)) {
return -1;
} else if (moment(first.items[0].date).isBefore(second.items[0].date)) {
return -1;
} else {
return 1;
}
});
};

How to update object inside array inside object?

I have the following Mongooose schema:
{
_id: {
type: String,
required: true
},
semesters: [
{
_id: {
type: String,
required: true
},
grades: [
{
subject: String,
literalGrade: String,
grade: Number,
credits: Number
}
]
}
]
}
I want to be able to update one grade that is inside semester's grades object using its id. I tried using MongoDb new multiple positional operator to no avail.
This is my current snippet:
User.findOneAndUpdate(
{
_id: req.params.user_id,
"semesters._id": req.params.semester_id
},
{
$set: {
"semesters.$[x].grades.$[y].subject": req.body.grades.subject,
"semesters.$[x].grades.$[y].literalGrade": req.body.grades.literalGrade,
"semesters.$[x].grades.$[y].grade": req.body.grades.grade,
"semesters.$[x].grades.$[y].credits": req.body.grades.credits
}
},
{
arrayFilters: [
{ "x._id": req.params.semester_id },
{ "y._id": req.params.grade_id }
]
},
(err, user) => {
if (err) return res.json(err);
res.send({
message: "Updated grade",
data: user
});
}
);
Couldn't get any results with the MongoDb positional operator, but found a way to programmatically do it with the help of Naing Lin Aung's answer. Here is the solution:
User.findOne(
{
_id: req.params.user_id,
"semesters.grades._id": req.params.grade_id
},
{ "semesters.$.grades": 1 },
(err, user) => {
if (err) return res.json(err);
let grades = user.semesters[0].grades;
let index = null;
for (let t in grades) {
if (grades[t]._id == req.params.grade_id) {
index = t;
break;
}
}
let grade = grades[index];
grade.subject = req.body.grades.subject;
grade.literalGrade = req.body.grades.literalGrade;
grade.grade = req.body.grades.grade;
grade.credits = req.body.grades.credits;
user.save(function(err) {
if (err) return res.json(err);
res.json({
message: "Updated grade",
data: user
});
});
}
);
You can take advantage of $[] syntax with arrayFilters
refer this link : https://docs.mongodb.com/manual/reference/operator/update/positional-all
db.col.update(
{ _id: req.params.user_id },
{ $set: { "semesters.$[cond1].grades.$[cond2].subject": req.body.grades.subject } },
{ arrayFilters: [ { "cond1._id": req.params.semester_id }, { "cond2._id": req.params.grade_id } ] })
You can similarly update other fields in $set

Mongoose does not update my array in the database

I'm trying to update my collection with mongoose.
This is my Schema:
var FamilySchema = mongoose.Schema({
construction: {
type: Array,
default: [
["foundry", 0],
["farm", 0],
["sawmill", 0]
]
}
});
And this is my code:
app.put('/construction/updateConstruction/:id', (req, res) => {
let id = req.params.id;
Family.findById(id, (err, familiaDB) => {
if (err) {
return res.status(500).json({
ok: false,
err
});
}
if (!familiaDB) {
return res.status(400).json({
ok: false,
err
});
}
// I want to update the value of the posicion 0 in the array.
familiaDB.construction[0][1] = 1;
familiaDB.save();
console.log(familiaDB);
});
});
Result in console.log after making the request:
Escuchando puerto: 3000
Base de datos ONLINE
{ state: true,
construction:
[ [ 'foundry', 1 ],
[ 'farm', 0 ],
[ 'sawmill', 0 ],
_id: 5bb8d69c604625211c572ada,
__v: 0 }
In console.log everything is fine and updated, but in my db it is not updated. I have checked it many times in robomongo and never updates it.
The quickest way to do this would be with findOneAndUpdate:
Family.findOneAndUpdate(
{ _id: mongoose.Types.ObjectId("YOURID") },
{ $set: { 'construction.0.1': 'YourValue' }}
)
This will do it in one statement.
Now instead of doing this by index you can do it with $elemMatch and update the correct one via:
Family.findOneAndUpdate(
{_id: ObjectId("YOURID"), 'cons': { $elemMatch: { '0': 'foundry' }}},
{ $set: { 'cons.$.1': 'YourValue' } }
)

Mongoose Update array in a document does not work as expected

I'm scratching my head since a couple day on how to update the content of an array with Mongoose.
Here is my schema to begin with:
const playedGameSchema = new Schema ({
created: Date,
updated: Date,
game: {
type: Schema.Types.ObjectId,
ref: 'game'
},
creator: {
id: {
type: Schema.Types.ObjectId,
ref: 'user'
},
score: Number
},
partners: [{
id: {
type: Schema.Types.ObjectId,
ref: 'user'
},
score: Number
}]
});
module.exports = mongoose.model('PlayedGame', playedGameSchema);
Basically, what I want to achieve is to, at the same time:
- Update the creator.score (successful with dot notation).
- Update the score key for each partner (unsuccessful).
Here is the result of a document created:
{
"creator": {
"id": "5b8544fa11235d9f02a9b4f1",
"score": 0
},
"_id": "5bb6375f5f68cc5c52bc93ae",
"game": "5b45080bb1806be939bfde03",
"partners": [
{
"_id": "5bb637605f68cc5cafbc93b0",
"id": "5b85497111235d677ba9b4f2",
"score": 0
},
{
"_id": "5bb637605f68ccc70ebc93af",
"id": "5b85497111235d677ba9b4f2",
"score": 0
}
],
"created": "2018-10-04T15:53:03.386Z",
"updated": "2018-10-04T15:53:03.386Z",
"__v": 0
}
As I said, I was able to change the score of the score creator by passing something like { "creator.score": 500 } as a second parameter, then I switch to trying to update the array.
Here is my lambda function to update the score for each partner:
export const update: Handler = (event: APIGatewayEvent, context: Context, cb: Callback) => {
context.callbackWaitsForEmptyEventLoop = false;
const body = JSON.parse(event.body);
let partnersScore: object = {};
if(body.update.partners) {
body.update.partners.forEach((score, index) => {
const key = `partners.${index}.$.score`;
partnersScore = Object.assign(partnersScore, { [key]: score});
console.log(partnersScore);
});
}
connectToDatabase().then(() => {
console.log('connected', partnersScore)
PlayedGame.findByIdAndUpdate(body.id, { $set: { partners: partnersScore } },{ new: true})
.then(game => cb(null, {
statusCode: 200,
headers: defaultResponseHeader,
body: JSON.stringify(game)
}))
.catch(err => {
cb(null, {
statusCode: err.statusCode || 500,
headers: { 'Content-Type': 'text/plain' },
body: err
})});
});
}
Which passes a nice { 'partners.0.$.score': 500, 'partners.1.$.score': 1000 } to the $set.
Unfortunately, the result to my request is a partners array that contains only one empty object.
{
"creator": {
"id": "5b8544fa11235d9f02a9b4f1",
"score": 0
},
"_id": "5bb6375f5f68cc5c52bc93ae",
"game": "5b45080bb1806be939bfde03",
"partners": [
{
"_id": "5bb63775f6d99b7b76443741"
}
],
"created": "2018-10-04T15:53:03.386Z",
"updated": "2018-10-04T15:53:03.386Z",
"__v": 0
}
Can anyone guide me into updating the creator score and all partners score at the same time?
My thoughs about findOneAndUpdate method on a model is that it's better because it doesn't require the data to be changed outside of the BDD, but wanting to update array keys and another key seems very difficult.
Instead, I relied on a set/save logic, like this:
PlayedGame.findById(body.id)
.then(game => {
game.set('creator.score', update.creatorScore);
update.partners.forEach((score, index) => game.set(`partners.${index}.score`, score));
game.save()
.then(result => {
cb(null, {
statusCode: 200,
headers: defaultResponseHeader,
body: JSON.stringify(result)
})
})
.catch(err => {
cb(null, {
statusCode: err.statusCode || 500,
headers: { 'Content-Type': 'text/plain' },
body: JSON.stringify({ 'Update failed: ': err })
})});
})

How to remove Object from array using mongoose

I'm trying to remove an object from an array in a document using mongoose.
The Schema is the following:
var diveSchema = new Schema({
//irrelevant fields
divers: [{
user: { type: Schema.Types.ObjectId, ref: 'User', required: true },
meetingLocation: { type: String, enum: ['carpool', 'onSite'], required: true },
dives: Number,
exercise: { type: Schema.Types.ObjectId, ref: 'Exercise' },
}]
});
a possible entry can be
{
//irrelevant fields
"divers": [
{
"_id": "012345678",
"user": "123456789",
"meetingLocation": "carpool",
"exercise": "34567890",
},
{
"_id": "012345679",
"user": "123456780",
"meetingLocation": "onSite",
"exercise": "34567890",
}
]
}
Say I want to remove the entry where user is 123456789 (note I do not know the _id at this point).
How do I do this correctly?
I tried the following:
var diveId = "myDiveId";
var userIdToRemove = "123456789"
Dive.findOne({ _id: diveId }).then(function(dive) {
dive.divers.pull({ user: userIdToRemove });
dive.save().then(function(dive) {
//do something smart
});
});
This yieled no change in the document.
I also tried
Dive.update({ _id: diveId }, { "$pull": { "divers": { "diver._id": new ObjectId(userIdToRemove) } } }, { safe: true }, function(err, obj) {
//do something smart
});
With this I got as result that the entire divers array was emptied for the given dive.
What about this?
Dive.update({ _id: diveId }, { "$pull": { "divers": { "user": userIdToRemove } }}, { safe: true, multi:true }, function(err, obj) {
//do something smart
});
I solve this problem using this code-
await Album.findOneAndUpdate(
{ _id: albumId },
{ $pull: { images: { _id: imageId } } },
{ safe: true, multi: false }
);
return res.status(200).json({ message: "Album Deleted Successfully" });
Try this
Dive.update({ _id: diveId },{"$pull": { "drivers": {"user": "123456789"}}})
Try this async code
var diveId = "myDiveId";
var userIdToRemove = "123456789"
const dive=await Dive.findOne({ _id: diveId })
await dive.divers.pull({ user: userIdToRemove });
await dive.save();
Use this with try/catch:
await Group.updateOne(
{ _id: groupId },
{ $pull: { members: {id: memberId }}}
);

Resources