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

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 }});

Related

Mongoose Find() - Return all docs if query is undefined

I have mongoose Find code function , the query is recieved from params (Here currently hardcoded)
Suppose this as the query
var arrayOfCategories = [ '63073212ed3e0f85ccffc8cf' , '63073324ed3e0f85ccffc8ff']
var arrayOfSkill = []
This is my find code
const astro =await Astrologer.find({ 'category.astolCat_id' : {$in: arrayOfCategories},
'skills.astolSkill_id': { $in: arrayOfSkill}})
It works fine if I have a value in 'arrayOfSkill' array but I want it to ignore ''skills.astolSkill_id': { $in: arrayOfSkill}' and only query for arrayOfCategories if arrayOfSkill is blank. If arrayOfSkill is blank then I is returning blank array
Build up your query options progressively...
const query = {};
if (arrayOfCategories.length) {
query["category.astolCat_id"] = { $in: arrayOfCategories };
}
if (arrayOfSkill.length) {
query["skills.astolSkill_id"] = { $in: arrayOfSkill };
}
const astro =await Astrologer.find(query);
const filter = [{}]
arrayOfCategories.length > 0 && filter.push({'category.astolCat_id': {$in: arrayOfCategories}})
arrayOfSkill.length > 0 && filter.push({'skills.astolSkill_id': { $in: arrayOfSkill }})
const astro =await Astrologer.find({$and:filter})
If need, can be modified as ; find({$or: filter})

Building a product-filter in NodeJS/MongoDB: only works when selecting all filters

I am building a product-filter in nodeJS. It works when i hard-code the following find()
const products = await Product.find({
price: {
$gte: price[0],
$lte: price[1],
}, category: categories, shipping: shipping})
The problem is of course that it only gives result if entered all the search options in the front-end.
However i also want the filter to work if i choose to NOT enter all the search options.
Thus i came up with the following code, to store all filters in an array, but it doesn't give any results.
The output of the array is =
{ price: { '$gte': 0, '$lte': 40820 } },
{
category: [
'60657e1d328b25043581e46f',
'601ef1ea8e06fc04cdaa5080',
'601eeaeb8e06fc04cdaa507f'
]
},
{ shipping: 'Yes' }
] <<<_--- content of searchArry????
This is my code, but it returns no results =
exports.searchFilters = async (req, res) => {
try {
const categories = req.body.categoryIds;
const price = req.body.price;
const shipping = req.body.shipping;
console.log(price, "<<<------- price data received in backend???");
console.log(categories, "<<--- categories data received in backend???");
console.log(shipping, "<<---- shipping data received in backend");
const searchArray = [];
if (req.body.price) {
const price = {
$gte: req.body.price[0],
$lte: req.body.price[1],
}
searchArray.push({ "price" : price });
}
if (req.body.categoryIds) {
searchArray.push({ "category" : categories });
}
if (req.body.shipping) {
searchArray.push({ "shipping" : shipping });
}
console.log(searchArray, "<<<_--- content of searchArry????");
const products = await Product.find({searchArray})
.populate("category", "_id name")
.populate("subs", "_id name")
.populate("postedBy", "_id name")
.exec();
res.json(products);
// console.log(products, "<<--producte?? ") console.log(products.length, "<<--- lengte") } catch(err) { console.log(err); }}
} catch(err) {
console.log(err);
}
}
Your hardcoded call looks like this:
Product.find({price, category, shipping}) // the arg is an object
And the second call (with searchArray) looks like:
Product.find({searchArray: [{price}, {category}, {shipping}]}) // the arg is an object containing an array of objects
Try merging the elements of searchArray into a single object:
Product.find(Object.assign({}, ...searchArray))
Note: Be aware that {variableName} is a shorthand for {variableName: variableName} or {"variableName": variableName}.

NodeJS Mongoose updateOne giving no match every time

I am trying to update a document in mongo with mongoose using updateOne method:
const updateResult = await UserModel.updateOne({
_id: mongoose.Types.ObjectId(userId)
}, {
$set: {
a: 'B'
}
})
userId contains a string of the ID of the user.
I have tried using the following
1. { _id: userId }
2. { email: theEmailOfTheUser }
But still, the updateResult is
n:0, nModified:0, ok:0
So I think it's must be something with the method itself and not in my query.
Also, when I'm trying to find the user using the query below, it can find it:
const user = await UserModel.find({
_id: userId
});
//user is found
Actually mongoose takes care of the $set and you do not have to add it. just:
const updateResult = await UserModel.updateOne({
_id: userId
}, {
a: 'B'
})
but the better solution would to just use findByIdAndUpdate():
const updateResult = await UserModel.findByIdAndUpdate(userId, {
a: 'B'
})

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);
});

How to use $in in mongoose reference schema

I have 2 schemas 1st is city and second is pincode. Pincode having reference of city. They both look like this
CITY schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// create a all city list
var allCitySchema = new Schema({
cities: {
type: String
}
}, {collection: 'allcities'});
var allcities = mongoose.model('allcities', allCitySchema);
module.exports = allcities;
Pincode schemas
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var allPincode = new Schema({
city_id: {
type: Schema.ObjectId,
ref: 'allcities'
},
pincode: {
type: String
}
}, {collection: 'pincode'});
var allPincode = mongoose.model('pincode', allPincode);
module.exports = allPincode;
Now the problem is when i tried to fetch all pincode based upon city id for that i tries like this
app.post('/api/getPincodeByCity', function(req, res) {
console.log("In pincode");
var cities_id = [];
cities_id = req.body.cities_id;
console.log(req.body); // { cities_id: '["5597aa08c0a0beb40be128d4","5597aa2bbb18fefc142b6915"]' }
console.log(cities_id);
pincodes.findById( {city_id: { $in: cities_id }}, function(err,pincodeIds){
if(err) res.send(err.message);
res.send(pincodeIds);
res.end('{"success" : "Recieved Successfully", "status" : 200}');
});
});
But it's not working its giving me this error
Cast to ObjectId failed for value "[object Object]" at path "_id"
I also try with find() instead of findById() method but it giving me this error
undefined is not a function
The $in operator is not just "strictly" for querying arrays as that can be done with basically any operator for a singular value.
It's actually a "list of arguments" which evaluates to an $or condition, but with shorter syntax:
var idList = ["559e0dbd045ac712fa1f19fa","559e0dbe045ac712fa1f19fb"];
var pincode = mongoose.model('pincode');
pincode.find({ "city_id": { "$in": idList } },function(err,docs) {
// do something here
});
Which as mentioned is short form for this:
pincode.find(
{
"$or": [
{ "city_id": "559e0dbd045ac712fa1f19fa" },
{ "city_id": "559e0dbe045ac712fa1f19fb" }
]
},
function(err,docs) {
// do something here
}
)
You are getting an error because you are overwriting the "array" definition with a "string" which is what all "request" objects are unless parsed otherwise.
The other reason for the error is you are calling the wrong method. .findById() expects a single argument of the _id for the document. To query other fields use .findOne() or in this case .find() since an $in will possibly match more than one document.

Resources