How to show list of field values of a collection of objects - node.js

I am making an API that shows a collection of ads with MongoDB and Node.js
I need to display the list of collection tags in a JSON string.
Example: 'home', 'mobile', 'motor'
This is the API initializer code:
const readline = require('readline');
const Product = require('./models/Product');
async function main() {
const advance = await question('Are you sure to continue with the deletion of the database? (yes or no) ')
if (!advance) {
process.exit();
}
const connection = require('./lib/connectMongoose')
await initProducts();
connection.close();
}
async function initProducts() {
const deleted = await Product.deleteMany();
console.log(`Remove ${deleted.deletedCount} products.`);
const inserted = await Product.insertMany([
{name: 'Table', sale: true, price: 150, photo: 'Table.png', tags: ['home']},
{name: 'Iphone 13 pro', sale: false, price: 950, photo: 'Iphone 13 pro.png', tags: ['mobile']},
{name: 'Car Mini Cooper', sale: true, price: 1000, photo: 'Car Mini Cooper.png', tags: ['motor']}
]);
console.log(`Create ${inserted.length} products.`)
}
main().catch(err => console.log('Hubo un error', err))
function question(text) {
return new Promise((resolve, reject) => {
const interface = readline.createInterface({
input: process.stdin,
output: process.stdout
});
interface.question(text, answer => {
interface.close();
if (answer.toLowerCase() === 'yes') {
resolve(true);
return;
}
resolve(false);
})
})
}
I need to find a MongoDB method that allows me to show when the API route calls the list that shows in JSON format all the tags that the collection includes

If I've understood correctly, one option is $unwind the tags array to get all tags as strings and be able to $group adding to a set to avoid duplicates.
db.collection.aggregate([
{
"$unwind": "$tags"
},
{
"$group": {
"_id": null,
"tags": {
"$addToSet": "$tags"
}
}
}
])
I think this works but $unwind and $group the entire collection is not always a good idea. It may be a slow process.
Example here

Related

Mongodb How to update documents already found

I'm building a movie rating system.
After entering the user ID, content ID, and rating registered in the rating document,
It was implemented by updating the rating in the content document.
How can I update the content document while I have already found it like the code below?
router.post('/', authenticate, async (req: IRequest<IRating>, res) => {
try {
const document = await Rating.create({
contentId: req.body.contentId,
contentType: req.body.contentType,
rating: req.body.rating,
review: req.body.review,
userId: req.user?._id,
});
const content = await Content.findOne({
type: req.body.contentType,
_id: document._id,
});
if (content) {
await content.updateOne(
{},
{
average: (content.average + document.rating) / content.count + 1,
count: content.count + 1,
},
);
}
res.json({ success: true, document, content });
} catch (err) {
res.status(404).json({ success: false, message: 'sever error' });
}
});
You can update with pipeline instead of use 2 queries, which for your current code can look like:
await Content.findOneAndUpdate(
{
type: req.body.contentType,
_id: document._id,
},
[{$set: {
average: {$divide: [{$add: ["$content.average", document.rating]},
{$add: ["$content.count", 1]}]},
count: {$add: ["$content.count", 1]}
}}])
But I don't think this is the right way to calculate an average. You should consider multiplying the existing $content.average by $content.count before adding the new document.rating

How to fix 'Updated field become undefined' issue in MongoDb query

I'm currently writing a mongoDB query to update set of queries in the db, The requirement is to fetch the db entries that has mediaOrigin = 'Kaltura' and the mediaType= 'YOUTUBE', then update the entries as mediaOrigin= 'AWS' and the mediaUrl.dataUrl and mediaUrl.downloadUrl with the value of mediaUrl.originalUrl. So I have completed a script to update the relevent queries but the value that the mediaUrl.dataUrl and mediaUrl.downloadUrl taking is undefined. So how can I solve that, I need to fill that two fields with the value of mediaUrl.originalUrl.
Here is the query I have written,
try {
db.getCollection('media').find({mediaOrigin: { $eq: 'KALTURA' }, mediaType: {$eq: 'YOUTUBE' }, delete: false
})
.forEach(function(media) {
var youtubeUrl = media.mediaUrl.originalUrl;
var Url = youtubeUrl;
db.getCollection('media').update(
{
_id: media._id
},
{
$set: {
'mediaUrl.downloadUrl': Url,
'mediaUrl.dataUrl': Url,
mediaOrigin: 'AWS'
}
}
);
});} catch (e) {
print(e);}
So how can I solve that.
Here I have attached the example entry in the db that I need to update.
You are attaching .forEach end of the .find() method get results from your collection.
You have to wait to get results before sending the result into foreach.
So use it like this:
const medias = await db.getCollection('media').find({
mediaOrigin: { $eq: 'KALTURA' },
mediaType: {$eq: 'YOUTUBE' },
delete: false
}).toArray();
medias.forEach(async(media) => {
var youtubeUrl = media.mediaUrl.originalUrl;
var Url = youtubeUrl;
await db.getCollection('media').update(
{
_id: media._id
},
{
$set: {
'mediaUrl.downloadUrl': Url,
'mediaUrl.dataUrl': Url,
mediaOrigin: 'AWS'
}
}
);
});

how can we use data received through axios put request on client side in mern stack?

I have sent category Id to the Nodejs through this code
const catHandler = async (catId) => {
const data = Axios.put('/api/products/categories/filter', {catId: catId},
{
"headers": { "content-type": "application/json", },
}
).then( categoriesProducts => {
console.log(categoriesProducts.data.products)
})
}
and this is my route for this
router.put('/categories/filter', async (req, res) => {
try {
const findCategory = await Category.find({ _id: req.body.catId });
if (findCategory) {
const productsByCategory = await Product.find(
{ category: req.body.catId }
).then(products => {
res.status(200).json({ products });
})
}
} catch (error) {
console.log('categories filter error', error)
}
})
The products of specific category are being shown in the console.log(categoriesProducts.data.products) on the react front end side like below
0: {_id: "5f7c88756746363148792982", name: "Simple Pizza", price: 5.6, image: "1601996916374.jpg", countInStock: 434, …}
1: {_id: "5f7c88976746363148792983", name: "Smoked Pizza", price: 7.8, image: "1601996951114.jpg", countInStock: 88, …}
2: {_id: "5f7c88c56746363148792984", name: "Large Cheezy Pizza", price: 9.4, image: "1601996997474.jpg", countInStock: 434, …}
But I want to display these products on the front end side. I have tried to use axios.get method but with get method how can I can send category Id to backend. So if any one has any idea how to do that Plz guide me.
you can use the query params with get method in node js
you can get query params by req.query in nodeJs
example
passing category id from front end -
api/products/categories/filter?cid=1
getting query param in the backend
const catId = req.query.cid
You can use below code to pass parameter to API get method.
fetch("/api/products/categories/filter?catId" + catId)
.then((res) => res.json())
.then((json) => {
this.setState({
Items: json,
});
});
And also you first create new state named as Items such as below.
this.state = {
Items: []
};
And finally Iterate on Items.
BR

Can mongodb send query pipelining with no loop?

I'm new to NodeJS and MongoDB.
I wanna get user's profile with one user's following list. If I use RDB, it was so simple with EQ join but I didn't have much experience of MongoDB, I don't know how.
Sample data below.
// list of users
[
{
_id: "oid_1",
nickname: "user_01",
link: "url/user_01"
},
{
_id: "oid_2",
nickname: "user_02",
link: "url/user_02"
},
{
_id: "oid_3",
nickname: "user_03",
link: "url/user_03"
}
...
]
user_01's followList
[
{
followOid: "foid_1",
userOid: "user_01"
},
{
followOid: "foid_2",
userOid: "user_02"
},
]
My solution is, get follow list, then use loop with follows.findOne() like below
const dataSet = [];
Follow.getFollowerList(userId) // for pipeline, use promise
.exec()
.then( async (result) => { // no async-await, no data output...
for (let data of result) {
let temp = await Users.getUserInfo( // send query for each data, I think it's not effective
data.userId,
{ nickname: 1, link: 1 }
);
dataSet.push(temp);
}
return dataSet;
})
.then((data) => {
res.status(200).json(data);
})
.catch( ... )
I think it's not best solution. If you are good at mongodb, plz save my life :)
thanks
One option would be to use aggregation.
const userId = 'Fill with UserId';
const pipe = [
{
'$match': {
'_id': userId
}
}, {
'$lookup': {
'from': 'followListCollectionName',
'localField': '_id',
'foreignField': 'userOid',
'as': 'followList'
}
}
];
const result = await UserModel.aggregate(pipeline);
and then you can find an array in result which contains one user with given Id ( and more if there are with same Id) and result[0].followList you can find follow objects as array
Second Option is to use virtuals
https://mongoosejs.com/docs/tutorials/virtuals.html
but for this schema of your collection needs some changes.
Good luck

How store a function in a MongoDb document, and then read it from there and call it?

I want store function in MongoDb document.
If i right , that can do like this:
collection.insertOne({ name: 'New', calculate: a => console.log(a) }, { serializeFunctions: true });
or:
const { Code } = require('mongodb');
collection.insertOne({ name: 'New', calculate: new Code(a => { console.log(a) }) });
as a result, we get a property with the datatype Code
{ name: "New", calculate: Code('a => { console.log(a); }', undefined)}
How work with it later.... how read this function from MongoDb and call it ???
i not undurstood how work with BSON
Thanks....

Resources