Add key on each json object in Promise - node.js

I have the following Promise and Promise.all which works and it returns a json object. However, I want to add a key for each return object.
as of now, it returns something
[value: {school object}, value:{students object}, value:{classroom object}]
desired output:
["schools": {school object }, {students object} , {classroom object} ]
Current Implementation:
new Promise((resolve, reject) => {
const school = getschool (webHost, dataSource, req);
const classRooms = getClassRooms(webHost, dataSource, req);
const students = getstudents (webHost,dataSource, req);
Promise.all([school ,classRooms,students ]).then((res) => {
resolve(res);
})
.catch((error) => {
logger.error(`${error}`);
reject(error);
});
});
classroom
{
"metadata": "metadata",
"value": [
{
"class_id": "171717",
"teacher_name": "Science"
}
]
}
School object
{
"metadata": "metadata",
"value": [
{
"id": "2345354",
"schoolName": "Memorial High School"
}
]
}
Student json
{
"metadata": "metadata",
"value": [
{
"id": "1234",
"studentName": "Beck"
},
{
"id": "5678",
"studentName": "Jeck"
}
]
}
Desired Output:
[
{
"class_id":"171717",
"teacher_name":"Science",
"id":"2345354",
"schoolName":"Memorial High School",
"Students":[
{
"id":"1234",
"studentName":"Beck"
},
{
"id":"5678",
"studentName":"Jeck"
}
]
}
]

It seems like you want to merge the objects to make one unified object of custom type, here is what you want to do:
res => resolve(
{
...res[1].value[0],
...res[0].value[0],
Students: res[2].value
}
)
The ... is called spread syntax. It "explodes" the objects and arrays. What we want here is get the internals of classRooms.value[0] merge them with school.value[0] object's internals, and then, add another attribute at the same level with key as Students which is a not-exploded array specified by student.value.
Here I have created a small TS Playground Example for you to play with the syntax and modify the output the way you may seem fit.
If you run it, it prints the desired output:
{
"class_id": "171717",
"teacher_name": "Science",
"id": "2345354",
"schoolName": "Memorial High School",
"Students": [
{
"id": "1234",
"studentName": "Beck"
},
{
"id": "5678",
"studentName": "Jeck"
}
]
}
-- ORIGINAL ANSWER --
Promise.all returns a promise of resolved objects in an array. So, the .then takes the parameter that's an array of resolved objects in the same order. That means your res parameter is an array of school object, students object, and classroom object in that order. you can do the following to "zip" them up.
new Promise((resolve, reject) => {
const school = getschool (webHost, dataSource, req);
const classRooms = getClassRooms(webHost, dataSource, req);
const students = getstudents (webHost,dataSource, req);
Promise.all([school ,classRooms,students ]).then((res) => {
resolve({"schools": res[0], "classRooms" : res[1], "students": res[2]});
})
.catch((error) => {
logger.error(`${error}`);
reject(error);
});
});
or even better,
.then(([schools, classRooms, students]) => {
resolve({schools, classRooms, students});
})

Related

How to pluck out field value from arrays of object

I am retrieving data from mongoose database using node.js and express.
Currently, I am getting a response like this
[
{
"images": [
{
"link": "https://just-incase-from-a-far/yop.png"
},
{
"link": "https://main-link-test/lol.png"
}
]
},
{
"images": [
{
"link": "https://example-link/happy.jpg"
},
{
"link": "https://example-link/angry.jpg"
},
{
"link": "https://example-link/sad.png"
}
]
}
]
But I want a response like this
[
"https://just-incase-from-a-far/yop.png",
"https://main-link-test/lol.png",
"https://example-link/happy.jpg",
"https://example-link/angry.jpg",
"https://example-link/sad.png"
]
How can I achieve my desired response.
This is my code that gives me my response of array objects
exports.getProducts = async (req,res) => {
const result = await Product
.find({isEmpty:false})
.select("-_id -createdAt -__v -isEmpty")
.exec()
if(!result) return res.status(400).json({ data: 'No product found' });
if(result.err) return res.json({ err: err });
return res.json(result);
}
Just add a outer loop extracting the links
links = [];
result.forEach((obj) => {
links = [...obj.images.map((o) => o.link), ...links];
})

How to pull out object heading from an array

I have a JSON response structure like this
{
"_id": "620e97d76ca392a43097cca6",
"user": "620295cbd67ece90802d2522",
"orderId": "EnrL7C",
"Items": [
{
"product": {
"name": "Fresh Salad",
"id": "61f2911723ff35136c98ad3e"
},
"quantity": 1,
"price": 1250,
"_id": "620e97d76ca392a43097cca7"
},
],
}
But i want the product not to be an object, so it should look like this
{
"_id": "620e97d76ca392a43097cca6",
"user": "620295cbd67ece90802d2522",
"orderId": "EnrL7C",
"Items": [
{
"name": "Fresh Salad",
"id": "61f2911723ff35136c98ad3e",
"quantity": 1,
"price": 1250,
"_id": "620e97d76ca392a43097cca7"
},
],
}
This is my code responsible for the response output
exports.getOrder = (req,res) => {
Order.findOne({orderId: 'EnrL7C'})
.populate("Items.product", "name")
.exec((error, order) => {
if(error) return res.status(400).json({ error });
if (order) {
return res.json(order);
}else{
return res.json(['No order found']);
}
});
Sometimes when I'm too lazy to look up all the mongoose documentation and figure out what version I'm on etc, I use the .lean() to just convert it to a normal JS object, which I'm way more comfortable with.
exports.getOrder = (req, res) => {
Order.findOne({ orderId: "EnrL7C" })
.lean() // add lean
.populate("Items.product", "name")
.exec((error, order) => {
if (error) return res.status(400).json({ error });
if (order) {
// fix the structure in javascript
order.Items = order.Items.map((item) => {
const flat = {
...item.product,
...item,
};
delete flat.product;
return flat;
});
return res.json(order);
} else {
return res.json(["No order found"]);
}
});
};
Let me know if that doesn't work, so I can update the answer.

elasticsearch node.js API remove an object from an array on a document using painless script results in array Index Out of Bounds

I want to remove items (an object) from an array on a document in elasticsearch, however whenever I try and run my update script using painless, I receive an Array Index Out of Bounds exception.
I'm using the javascript elasticsearch npm package to search elasticsearch for the relevant documents which then returns me data like:
"_index": "centres",
"_type": "doc",
"_id": "51bc77d1-b514-4f4e-85fa-412def6829f5",
"_score": 1,
"_source": {
"id": "cbaa7daa-f1a2-4ac3-8d7c-fc981245d21c",
"name": "Five House",
"openDays": [
{
"title": "new open Day",
"endDate": "2022-03-22T00:00:00.000Z",
"id": "82be934b-eeb1-419c-96ed-a58808b30df7"
},
{
"title": "last open Day",
"endDate": "2020-12-24T00:00:00.000Z",
"id": "8cc339b9-d2f8-4252-b68a-ed0a49cbfabd"
}
]
}
I then want to go through and remove certain items from the openDays array. I've created an array of the items I want to remove, so for the above example:
[
{
id: '51bc77d1-b514-4f4e-85fa-412def6829f5',
indexes: [
{
"title": "last open Day",
"endDate": "2020-12-24T00:00:00.000Z",
"id": "8cc339b9-d2f8-4252-b68a-ed0a49cbfabd"
}
]
}
]
I'm then trying to run an update via the elasticsearch node client like this:
for (const centre of updates) {
if (centre.indexes.length) {
await Promise.all(centre.indexes.map(async (theIndex) => {
const updated = await client.update({
index: 'centres',
type: 'doc',
id: centre.id,
body: {
script: {
lang: 'painless',
source: "ctx._source.openDays.remove(ctx._source.openDays.indexOf('openDayID'))",
params: {
"openDayID": theIndex.id
}
}
}
}).catch((err) => {throw err;});
}))
.catch((err) => {throw err;});
await client.indices.refresh({ index: 'centres' }).catch((err) => { throw err;});
}
}
When I run this though, it returns a 400 with an "array_index_out_of_bounds_exception" error:
-> POST http://localhost:9200/centres/doc/51bc77d1-b514-4f4e-85fa-412def6829f5/_update
{
"script": {
"lang": "painless",
"source": "ctx._source.openDays.remove(ctx._source.openDays.indexOf(\u0027openDayID\u0027))",
"params": {
"openDayID": "8cc339b9-d2f8-4252-b68a-ed0a49cbfabd"
}
}
}
<- 400
{
"error": {
"root_cause": [
{
"type": "remote_transport_exception",
"reason": "[oSsa7mn][172.17.0.2:9300][indices:data/write/update[s]]"
}
],
"type": "illegal_argument_exception",
"reason": "failed to execute script",
"caused_by": {
"type": "script_exception",
"reason": "runtime error",
"script_stack": [],
"script": "ctx._source.openDays.remove(ctx._source.openDays.indexOf(\u0027openDayID\u0027))",
"lang": "painless",
"caused_by": {
"type": "array_index_out_of_bounds_exception",
"reason": null
}
}
},
"status": 400
}
I'm not quite sure where I'm going wrong with this. Am I using the indexOf painless script correctly? Does indexOf allow for the searching of properties on objects in arrays?
I stumbled across this question and answer: Elasticsearch: Get object index with Painless script
The body of the update script needs changing like so:
Promise.all(...
const inline = `
def openDayID = '${theIndex.id}';
def openDays = ctx._source.openDays;
def openDayIndex = -1;
for (int i = 0; i < openDays.length; i++)
{
if (openDays[i].id == openDayID)
{
openDayIndex = i;
}
}
if (openDayIndex != -1) {
ctx._source.openDays.remove(openDayIndex);
}
`;
const updated = await client.update({
index: 'centres',
type: 'doc',
id: centre.id,
body: {
script: {
lang: 'painless',
inline: inline,
},
}
}).catch((err) => {throw err;});
await client.indices.refresh({ index: 'centres' }).catch((err) => { throw err;});
})).catch(... //end of Promise.all
I am not au fait with painless scripting, so there are most likely better ways of writing this e.g. breaking once the index of the ID is found.
I have also had to move the refresh statement into the Promise.all since if you're trying to remove more than one item from the array of objects, you'll be changing the document and changing the index. There is probably a better way of dealing with this too.
'openDayID' should be params.openDayID
And use removeIf:
"ctx._source.openDays.removeIf(el -> (el.id == params.openDayID))"

Trouble with async mongoose queries

This is the array that i get from my database, let's call it product_list
{
"seller": "5cee0e69f67e171ac8ef14c7",
"products": [...]
},
{
"seller": "5d1c36910aec8934cefdda8e",
"products": [...]
}
And i want to send the same array but transforming the seller field into the complete seller object. In the given example would be these ones.
{
"_id": "5cee0e69f67e171ac8ef14c7",
"name": "Will"
},
{
"_id": "5d1c36910aec8934cefdda8e",
"name" : "Jess"
}
So my desired output would be
{
"seller": {
"_id": "5cee0e69f67e171ac8ef14c7",
"name": "Will"
},
"products": [...]
},
{
"seller": {
"_id": "5d1c36910aec8934cefdda8e",
"name": "Jess"
},
"products": [...]
}
I tried the following
product_list.forEach(element => {
User.findById(element.seller, function(err, user){
element.seller = user;
};
});
What i'm trying is to take all the seller ids from the product_list object, convert it to the complete object with User.findById, and return the updated product_list. But the problem is that i'm not to good with asynchronous code, and since the mongoose calls are callbacks when i return the object with res.json(product_list), the mongoose query is not finished and i receive the object without the modification.
I tried promises and awaits but im not getting any result.
I hope you understand my explanation, and thank you very much.
You can try this approach, wrap User.findById and you can use the async / await pattern.
// Wrapper function for User.findById. We could use utils.promisify for this.
function getUserById(id) {
return new Promise((resolve, reject) => {
User.findById(id, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
});
}
async function getSalesDetails(productList) {
for(let product of productList) {
let userDetails = await getUserById(product.seller);
product.seller = userDetails;
}
return productList;
}
async function testGetSalesDetails() {
let productList = [{
"seller": "5cee0e69f67e171ac8ef14c7",
"products": [1,2,3]
},{
"seller": "5d1c36910aec8934cefdda8e",
"products": [4,5,6]
}];
let productListWithSeller = await getSalesDetails(productList);
console.info("Product list (with seller): ", productListWithSeller);
}
testGetSalesDetails();
You could also use utils.promisify to generate the getUserById function (it's very handy!), for example:
const getUserById = util.promisify(User.findById);

How to get count by array of values using MongoDB and Node.js

I need to get sum of count of document using MongoDB and Node.js. I am explaining my code and document below.
var finalOut=[
{
"location": "NEW DELHI",
"nos_of_fos": 15,
"login_id": [
"9619300317",
"9619300343",
"9619300338",
"9619300351",
"9619300322",
"9619300316",
"9619300323",
"9619300328",
"9619300341",
"9619300309",
"9619300310",
"9619300329",
"9619300353",
"9619300356",
"NORTH#oditeksolutions.com"
],
},
{
"location": "North West Delhi",
"nos_of_fos": 6,
"login_id": [
"9619300355"
],
}
]
The above is my input. I am explaining my code below.
finalOut.forEach(function(listItem, index){
Feedback.collection.aggregate([
{
$match: {
login_id: {
$in: listItem['login_id']
}
}
},
])
.toArray((cerr,cdocs)=>{
console.log(cdocs);
})
finalOut[index]['total_remarks']=cdocs;
})
Here I need the total count of all login_id arrays value which is present inside feedback document.
You can not use forEach for asynchronous operations:
Here I will use the async library but you can use any library that you like.
async.forEachOf(finalOut, (listItem, index, callback) => {
Feedback.collection.aggregate([
{
$match: {
login_id: {
$in: listItem['login_id']
}
}
},
{
$count: ‘theCount’
}
])
.toArray((cerr,cdocs)=> {
finalOut[index]['total_remarks']=cdocs.theCount;
callback();
})
}, err => {
// here you can access the filled `finalOut` object
});

Resources