I need to perform some modification on an API that returns something like this:
[
{
"_id": "0000000000000000001",
"name": "Random name",
"categories": [
"5eb8548330550e017f8902e6",
"5eb2548630550e117f8909eb",
"5eb6548930550e017f8909e9"
]
},
{...},
]
each results' categories are returned as ids of their respective documents. I need to manipulate the result so each object's categories field has an array of objects and each object has the id and the name of its category. I make an example of what the outcome should be:
[
{
"_id": "0000000000000000001",
"name": "Random name",
"categories": [
{"_id": "5eb8548330550e017f8902e6",
"name": "Category 1"},
{"_id": "5eb2548630550e117f8909eb",
"name": "Category 2"},
{"_id": "5eb6548930550e017f8909e9",
"name": "Category 3"},
]
},
{...},
]
I need to do this using plain JS and for so far this is what I have done but it returns an array of undefined:
const resultArray = await Promise.all(
searchResults.map(async (item) => {
item.categories.map(async (categoryId) => {
return await CategoryType.find({ _id: categoryId});
});
})
);
For the moment I am trying to get each category document for each id in the categories field. I bet the reason why I am getting an array of undefined is that I am handling async in the wrong way but cannot figure out how.
To strictly answer your question: you are missing synchronization (because Array.prototype.map
'ignores' async):
const resultArray = await Promise.all(
searchResults.map(async (item) => {
const promises = item.categories.map(async (categoryId) => {
// you dont want find, but findOne btw
return await CategoryType.findOne({ _id: categoryId});
});
const categories = await Promise.all(promises)
item.categories = categories
return item
})
);
This can be simplified down as
const resultArray = await Promise.all(
searchResults.map(async item => {
item.categories = await Promise.all(item.categories.map(categoryId => {
return CategoryType.findOne({ _id: categoryId})
}))
return item
})
);
But the proper way of doing it is likely to use populate
const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost:27017/dummy')
const Category = mongoose.model('Category', {
name:String,
}, 'categories');
const X = mongoose.model('X', {
name:String,
categories: [{type: mongoose.Types.ObjectId, ref: 'Category'}]
}, 'xs');
;(async()=>{
try {
mongoose.set('debug', true)
const xs = await X.find().populate('categories')
console.log('xs : ', xs[0])
} finally {
mongoose.disconnect()
}
})()
You will notice by the way, that mongoose, under the hood uses find({ _id: {$in:[]}})
which makes only one request (so better) than doing multiple findOne
(as you do)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.