Mongoose: value not incrementing through $inc - javascript

I have been struggling with this issue for days. For some unknown reason, a specific field ("reviewCounts") is not incrementing no matter what alternative methods I try.
Here is my Schema
let itemSchema = new mongoose.Schema({
rank: Number,
image: String,
name: String,
title: String,
count: Number,
category: String,
ratings: Object,
reviewCounts: Number,
reviews: Array,
tags: Object,
})
and this is the update method:
Item.findOneAndUpdate({name: item,title:title}, {
$inc:{"reviewCounts":1},
$set:averageQuery,
$inc:query
},{strict:false},
function (err, data) {
}
}
$inc works completely find on "query" not it does not increment "reviewCounts". I have tried using $set to manually set the value, but that did not work too. I doubled-checked and confirmed that the field is int32 as intended. What could be the reason behind this issue?

When you build your update statement this way:
{
$inc:{"reviewCounts":1},
$set:averageQuery,
$inc:query
}
you're duplicating the $inc key in your JavaScript object. JavaScript interprets such code as:
{
$set:averageQuery,
$inc:query
}
so simply last usage of particular key "wins" thus you loose the reviewCounts part.
You need to make sure that there's only one $inc and you can use the spread operator to combine your $inc's:
$inc:{ ...query, "reviewCounts":1 }

Related

Mongoose - Deleting documents is unresponsive

I'm trying to use Mongoose (MongoDB JS library) to create a basic database, but I can't figure out how to delete the documents / items, I'm not sure what the technical term for them is.
Everything seems to work fine, when I use Item.findById(result[i].id), it returns a valid id of the item, but when I use Item.findByIdAndDelete(result[i].id), the function doesn't seem to start at all.
This is a snippet the code that I have: (Sorry in advance for bad indentation)
const testSchema = new schema({
item: {
type: String,
required: true
},
detail: {
type: String,
required: true
},
quantity: {
type: String,
required: true
}
})
const Item = mongoose.model("testitems", testSchema)
Item.find()
.then((result) => {
for (i in result) {
Item.findByIdAndDelete(result[i].id), function(err, result) {
if (err) {
console.log(err)
}
else {
console.log("Deleted " + result)
}
}
}
mongoose.connection.close()
})
.catch((err) => {
console.log(err)
})
I'm not sure what I'm doing wrong, and I haven't been able to find anything on the internet.
Any help is appreciated, thanks.
_id is a special field on MongoDB documents that by default is the type ObjectId. Mongoose creates this field for you automatically. So a sample document in your testitems collection might look like:
{
_id: ObjectId("..."),
item: "xxx",
detail: "yyy",
quantity: "zzz"
}
However, you retrieve this value with id. The reason you get a value back even though the field is called _id is because Mongoose creates a virtual getter for id:
Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field cast to a string, or in the case of ObjectIds, its hexString. If you don't want an id getter added to your schema, you may disable it by passing this option at schema construction time.
The key takeaway is that when you get this value with id it is a string, not an ObjectId. Because the types don't match, MongoDB will not delete anything.
To make sure the values and types match, you should use result[i]._id.

Why $ne: null returning the empty arrays?

I`m trying to return the arrays are that have contents but $ne: null or $exsists: true still returning the empty arrays..
const userSchema = new mongoose.Schema({
email: String,
password: String,
googleId: String,
facebookId: String,
secret: Array
});
app.get("/secrets", function(req, res) {
User.find({secret: {$ne: null} }, function(err, secrets) {
if (err) {
console.log(err);
} else {
if (secrets) {
console.log(secrets);
res.render("secrets", {
secrets: secrets
});
};
};
});
});
I googled quite a bit about it and I do understand that $ne: null would return every document where the secret array doesn't exists, but if its an empty array then why? Any suggestion how to overcome this rookie problem? Im new here be kind! :)
This is because null != [].
You will have to explicitly write your condition to handle both.
$nin is for not in. So find users where secret value is not in the given array. The given array can hold your multiple values.
User.find({secret: {$nin: [null, [] ] } },...
Also, with $exists you will even get docs where the field is null. The only docs you will not get are the ones where the secret field does not exist at all.
If the documents contain values for the secret field that include missing/undefined, null, empty array, and populated array, you have a few options to only match populated arrays:
{$eval:{$gt:[0,{$size:"$secret"}]}}- get the size for thesecrets` array, and only match arrays that are not empty.
{"secret.0":{$exists:true}} - only match the document if there is a first element in the secret array (implicitly doesn't match non-array fields)
If the elements in the secret array are all the same type, a type-based query can be used. For example, if the elements in the secret array are strings, {"secret.0":{$gte:""}} will match any array whose first element is a string. This could also be optimized by creating an index on {"secret.0":1}

How do I use a variable with the mongo 'like' operation when comparing a number?

I have tried the same without using a separate variable search_num and use the search variable itself but the same issue persists, thats the reason I typecasted. Was tested with postman
this is my schema -
const lender_details = mongoose.Schema({
lender_id : Number,
investment_id: Number,
name: String,
email: String,
mobile_no : Number,
reg_date: Number,
live_status: String,
lender_details: String,
})
All I'm trying to do is have a common search box, that searches through investment ID,phone and email.
router.post('/', async (req,res,next) => {
search = req.body.search;
search_num = Number(search);
console.log({$regex : search_num});
await a.find({
"investment_id" :($regex : search_num),
"email" : { $regex : search } }
,{ _id : false })
The /m/ concept does not work when using a variable and so I have used regex, but after trying almost everything this error persists when I'm trying to search the investment ID which is a number -
Cast to Number failed for value "/1/" at path "investment_id" for model "lender_details"
Any and all help appreciated !
Iirc you should translate it firstly to string with using some search pipe then you can search it with regex;
Let me show some of example;
a.find({
"$expr": {
"$regexMatch": {
"input": {"$toString": "$investment_id"},
"regex": search_num_regexp
}
})
or
iirc you can use where operator but it will maybe decrease the performance query and i am not sure if it will work correctly. but it would something like below
a.find({
$where: `${search_num_regexp}.test(this.example)`
})

In mongoose, how to find records based on value in related collection?

In Mongoose, I have two collections, with one referencing the other. Is it possible to have a find query that selects records based on a value in the other. An example of what I am try to get at (not actual schemas):
const CarModelSchema = new mongoose.Schema({
name: String,
brand: { type: mongoose.Schema.Types.ObjectId, ref: 'CarBrand' }
});
const CarBrandSchema = new mongoose.Schema({
name: String,
country: String
});
I then want to perform a query of the form, without needing to do two queries:
CarModelSchema.find({ 'brand.country': 'GER' });
So far I haven't been able to make this work, so I am wondering whether this can be done in Mongo or whether I am approaching it wrong?
Yes it is possible.
I realize you don't have models for your schemas so add them like this:
const CarModel = mongoose.model('CarModel', CarModelSchema);
const CarBrand = mongoose.model('CarBrand', CarBrandSchema);
Also brands should be defined like this:
brand: [{ type: mongoose.Schema.Types.ObjectId, ref: 'CarBrand' }] //added the brackets
You can then run a find query to filter by country by doing the following:
CarModel.
find(...).
populate({
path: 'brand',
match: { country: { $eq: 'GER' }},
// You can even select the field you want using select like below,
select: 'name -_id',
//Even limit the amount of documents returned in the array
options: { limit: 5 }
}).
exec();
And that should do it, as long as the ObjectIds saved in brands array in the CarModel collection are valid or exist.
Using match in your population will do the work.
CarModel.find()
.populate({
path: 'brand',
model: CarBrandModel,
match: { country: { $eq: 'GER' }},
})
.exec()
Keep in mind you have to define CarModel and CarBrandModel like this:
const CarModel = mongoose.model('CarModel', CarModelSchema)
const CarBrandModel = mongoose.model('CarBrandModel', CarBrandSchema)
Yes, you are doing it wrong.
In CarModelSchema.brand there is not string saved, there is ObjectId saved, therefore you have to find that ObjectId (the reference).
You can do it manually - first finding the CarBrandSchema.find({ 'country': 'GER' }); and then use its ObjectId (=_id), or you can use https://mongoosejs.com/docs/populate.html to populate your CarModel with the CarBrand object.

Mongoose getter is getting undefined parameter

I'm storing a price value in my Mongoose schema according to the answer from how should i store a price in mongoose?
I have the following code in my schema definition:
price: {
value: {
type: Number,
get: getPrice,
set: setPrice,
min: 0
},
currency: {
type: String,
default: 'PLN',
trim: true,
enum: ['PLN', 'EUR']
}
},
and my get function:
function getPrice(num){
return (num/100).toFixed(2);
}
However, whenever this getter function is called I can see that the num parameter in undefined.
Do you know what might be the reason for that? And how could I fix this?
Add a default of zero for value. Also, mongoose is notoriously bad about subdocuments that are not inside an array, which may be causing this problem.
value: {
type: Number,
get: getPrice,
set: setPrice,
min: 0,
default: 0
},
If getter/setters give you problems with mongoose models, use the native static methods in mongoose schemas:
mySchema.static('getPrice', function(){
//'this' in the context means a document that shares the schema
return (this.price.value/100).toString(2);
});
You can invoke the method in any document that have said schema:
var myPrice = oneMongooseDocument.getPrice();
Is a very clean approach.

Categories