MongoDB data matching and fetching issue while querying on an array - node.js

I want to fetch data from the MongoDB. Since I am new into Node.Js and MongoDB. I am not able to get the data.
I am passing primary category and secondary category to an API where I want to match the primary and secondary category with the array field "category" which is having two indexes 0 and 1 ,in the 0 index primary category is there and in the 1 index secondary categories are there separated by ~ .
Below is my code to get the data according to primary category but I want to get the data by matching the primary and secondary category from the db.
ProductModel
.find({ category : { $all : [req.body.primary] }})
.select('id link brand title description category image images in_stock price sale_price merchant_number part_number GroupID status promotion attributes tags currency updated -_id')
.then((productList) => {
response.status = 200;
response.msg = 'Success';
response.count = productList.length;
response.data = productList
res.json(response);
})
.catch(() => {
response.status = 500;
response.msg = 'connection error';
response.data = [];
res.json(response);
});
Sample Doc :
So I wanted to input something like ['Health','women'] & get docs where all of these elements in category array exists, kind of regex search on array elements.
Can Someone help me out to get the data from the db.

If you've to query with input as ['Health', 'women'] & get the documents which has both listed & also get the documents which has something like this : category : ["Health", "tes~men~women"] then you can try below query :
Query :
db.collection.find({
$and: [
{
category: {
$in: [
/\bwomen\b/
]
}
},
{
category: {
$in: [
"Health"
]
}
}
]
})
Js Code :
const secondary = req.body.secondary
const primary = req.body.primary
db.collection.find({
$and: [
{
category: {
$in: [
new RegExp(`\\b${secondary}\\b`, 'i')
]
}
},
{
category: {
$in: [
primary
]
}
}
]
})

Related

Query to find document by matching a value in an object embeded in an array field

I want to query a collection "chat" whose documents have an array field "participants". This array has always two objects with the properties id and name. I want to find the document that matches the two ids of the embeded objects. For example, I want to find the following document in which the ids are "1" and "2".
//chat
//find ids 1 and 2.
//this is the document that I am looking for and should be returned.
{
_id : ytyutu876788
participants : [ {id : 1, name : Murray}, {id : 2, name : Pier} ]
}
//In this document id 1 is present but not together with id 2.
{
_id : d6t6d6tdt6
participants : [ {id : 1, name : Murray}, {id : 3, name : Gustav} ]
}
The query that I have tried to use so far is the following which returns an empty array even though I am sure that the document that I want to find exists in the collection so I know that it is the query what I am doing wrong:
try {
const userId = req.body.userId
const friendId = req.body.friendId
console.log(userId)
console.log(friendId)
await client.connect()
const query = await client.db('instagram_clone').collection('chat').find(
{"participants.id" : {userId, friendId}}
).toArray()
console.log(query)
You may use $all operator like
const query = await client.db('instagram_clone')
.collection('chat')
.find({"participants.id": {$all:[userId, friendId]}})
.toArray();
Or you may like to use $and operator like
const query = await client.db('instagram_clone')
.collection('chat')
.find({$and :[{"participants.id": userId}, {"participants.id": friendId}]})
.toArray();
This another query that does the job, in which I used the $elemmatch operator.
const firstQuery = await client.db('instagram').collection('chat').find({
participants : {
$all: [
{
$elemMatch: {
id: friendId
}
}, {
$elemMatch: {
id: userId
}
}
]
}
}).toArray();

Updating nested array mongodb

I think there are multiple ways to do this, and that has me a little confused as to why I can't get it to work.
I have a schema and I would like to update Notes within it but I can't seem to do it. Additionally, if I want to return the notes how would I go about doing it?
schema :
{
_id : 1234
email : me#me.com
pass : password
stock : [
{
Ticker : TSLA
Market : Nasdaq
Notes : [
"Buy at 700",
"Sell at 1000"
]
},
{
Ticker : AAPL
Market : Nasdaq
Notes : [
"Buy at 110",
"Sell at 140"
]
},
]
}
Each user has a list of stocks, and each stock has a list of notes.
Here is what I have tried in order to add a note to the list.
router.post(`/notes/add/:email/:pass/:stock/:note`, (req, res) => {
var email = req.params.email
var pass = req.params.pass
var note = req.params.note
var tempStock = req.params.stock
userModel.findOne({email: email} , (err, documents)=>{
if (err){
res.send(err);
}
else if (documents === null){
res.send('user not found');
}else if (bcrypt.compareSync(pass , documents.pass)){
userModel.findOneAndUpdate({email : email , "stock.Ticker" : tempStock}, {$push : {Notes : note}} ,(documents , err)=>{
if(err){
res.send(err);
}else {
res.send(documents.stock);
}
})
}
})
})
Thanks :)
Currently, you are pushing the new note into a newly created Notes property inside the model instead of into the Notes of the concrete stock. I am not completely aware of the mongoose semantics but you need something like this:
userModel.findOneAndUpdate({ email: email, "stock.Ticker": tempStock }, { $push: { "stock.$.Notes": note } }, (documents, err) => {
$ gives you a reference to the currently matched element from the stock array.
For the second part, I am not sure what you mean by
Additionally, if I want to return the notes how would I go about doing it?
They should be returned by default if you're not doing any projection excluding them.
Also, as per the docs(and general practise), the callback for the findOneAndUpdate has a signature of
(error, doc) => { }
instead of
(documents, err) => { }
so you should handle that.

MongoDB - find one and add a new property

Background: Im developing an app that shows analytics for inventory management.
It gets an office EXCEL file uploaded, and as the file uploads the app convert it to an array of JSONs. Then, it comapers each json object with the objects in the DB, change its quantity according to the XLS file, and add a timestamp to the stamps array which contain the changes in qunatity.
For example:
{"_id":"5c3f531baf4fe3182cf4f1f2",
"sku":123456,
"product_name":"Example",
"product_cost":10,
"product_price":60,
"product_quantity":100,
"Warehouse":4,
"stamps":[]
}
after the XLS upload, lets say we sold 10 units, it should look like that:
{"_id":"5c3f531baf4fe3182cf4f1f2",
"sku":123456,
"product_name":"Example",
"product_cost":10,
"product_price":60,
"product_quantity":90,
"Warehouse":4,
"stamps":[{"1548147562": -10}]
}
Right now i cant find the right commands for mongoDB to do it, Im developing in Node.js and Angular, Would love to read some ideas.
for (let i = 0; i < products.length; i++) {
ProductsDatabase.findOneAndUpdate(
{"_id": products[i]['id']},
//CHANGE QUANTITY AND ADD A STAMP
...
}
You would need two operations here. The first will be to get an array of documents from the db that match the ones in the JSON array. From the list you compare the 'product_quantity' keys and if there is a change, create a new array of objects with the product id and change in quantity.
The second operation will be an update which uses this new array with the change in quantity for each matching product.
Armed with this new array of updated product properties, it would be ideal to use a bulk update for this as looping through the list and sending
each update request to the server can be computationally costly.
Consider using the bulkWrite method which is on the model. This accepts an array of write operations and executes each of them of which a typical update operation
for your use case would have the following structure
{ updateOne :
{
"filter" : <document>,
"update" : <document>,
"upsert" : <boolean>,
"collation": <document>,
"arrayFilters": [ <filterdocument1>, ... ]
}
}
So your operations would follow this pattern:
(async () => {
let bulkOperations = []
const ids = products.map(({ id }) => id)
const matchedProducts = await ProductDatabase.find({
'_id': { '$in': ids }
}).lean().exec()
for(let product in products) {
const [matchedProduct, ...rest] = matchedProducts.filter(p => p._id === product.id)
const { _id, product_quantity } = matchedProduct
const changeInQuantity = product.product_quantity - product_quantity
if (changeInQuantity !== 0) {
const stamps = { [(new Date()).getTime()] : changeInQuantity }
bulkOperations.push({
'updateOne': {
'filter': { _id },
'update': {
'$inc': { 'product_quantity': changeInQuantity },
'$push': { stamps }
}
}
})
}
}
const bulkResult = await ProductDatabase.bulkWrite(bulkOperations)
console.log(bulkResult)
})()
You can use mongoose's findOneAndUpdate to update the existing value of a document.
"use strict";
const ids = products.map(x => x._id);
let operations = products.map(xlProductData => {
return ProductsDatabase.find({
_id: {
$in: ids
}
}).then(products => {
return products.map(productData => {
return ProductsDatabase.findOneAndUpdate({
_id: xlProductData.id // or product._id
}, {
sku: xlProductData.sku,
product_name: xlProductData.product_name,
product_cost: xlProductData.product_cost,
product_price: xlProductData.product_price,
Warehouse: xlProductData.Warehouse,
product_quantity: productData.product_quantity - xlProductData.product_quantity,
$push: {
stamps: {
[new Date().getTime()]: -1 * xlProductData.product_quantity
}
},
updated_at: new Date()
}, {
upsert: false,
returnNewDocument: true
});
});
});
});
Promise.all(operations).then(() => {
console.log('All good');
}).catch(err => {
console.log('err ', err);
});

Mongoose - How to find by an 'array' of ObjectId?

I'm getting an array of ObjectId from a query, then I need to use them inside another query.
Here is the function:
exports.getAllByShop = wrap(async(req, res, next) => {
const products = await Product.find({ _shop: mongoose.Types.ObjectId(req.params.shopId) }).select('_id');
// how to make this query?
const sales = await Sale.find({ _product: { $in: [ products ]}});
res.status(200).json(sales);
});
The result of the first query products looks like this:
[
{ _id: 5be3601f90e40b35547ae6b4 },
{ _id: 5be4b5a3443abf196cb4cc9a }
]
What I'm trying to achieve:
const sales = await Sale.find({ _product: {
$in:
[
mongoose.Types.ObjectId(prod1),
mongoose.Types.ObjectId(prod2),
...
]
}});
I need to find all the sales that have their _product equals to the values of the above array. Right now it doesn't work and show this message: "Cast to ObjectId failed for value "[ { _id: 5be3601f90e40b35547ae6b4 },↵ { _id: 5be4b5a3443abf196cb4cc9a } ]" at path "_product" for model "sale"". How to make this last query work?
youre inserting an array, into an array, try this instead
const productIds = products.map((v) => v)
const sales = await Sale.find({ _product: { $in: productIds }});

Deleting relational data in MongoDB - Removing specific element from Array

I have a collection of users and a collection of articles. A user holds multiple articles in an array. Now I'm trying to delete an article from a User's array of articles in MongoDB. So far I have
exports.delete = function(req, res, next) {
const articleId = req.params.id;
Article.findOneAndRemove({_id: articleId})
.then((deletedArticle)=> {
const authorId = deletedArticle.author;
console.log("AUTHOR:"+authorId);
User.update( { _id: authorId }, { $pull: { articles: [ _id: deletedArticle.id ] } } )
res.status(204).send(deletedArticle)
})
.catch(next);
}
this does delete this article itself, however, not the reference to the article saved in the array the User object holds. What am I doing wrong here?
Try changing the square brackets in your query to curly:
User.update( { _id: authorId }, { $pull: { articles: { _id: deletedArticle.id } } } )
This would obviously require that the _id in the articles array is the same as the article collection, which depends on how you populate the array (I have a feeling you are doing that part right, but just wanted to mention that possibility up front).

Resources