Mongoose - find documents with maximum no of counts - mongodb

I am using Mongoose to fetch data from MongoDB. Here is my model.
var EmployeeSchema = new Schema({
name: String,
viewCount: { type: Number, default: 0 },
description: {
type: String,
default: 'No description'
},
departments: []
});
I need to find top 5 employees where count(viewCount) is highest order by name.
I am thinking of finding all the employee by using find() & then read viewCount property & produce the result. is there any better way to get the desired result.

All you need here is .sort() and .limit():
Employee.find().sort({ "viewCount": -1, "name": 1 }).limit(5)
.exec(function(err,results) {
});
And that is the top 5 employees in views ordered by name after the viewCount.
If you want them ordered by "name" in your final five, then just sort that result:
Employee.find().sort({ "viewCount": -1, "name": 1 }).limit(5)
.exec(function(err,results) {
// sort it by name
results.sort(function(a,b) {
return a.name.localeCompare(b.name);
});
// do something with results
});

You can sort by the view count and limit the search results to 5.
In code it might look like this:
Employee
.find()
.sort([['viewCount',-1], ['name',-1]])
.limit(5)
.exec(function(err, results){
//do something with the results here
});

Related

how to sort by an auto-calculated field in mongoose.js

i have a product schema in my project, users can rate the products..the way i set the schema up is to store the number and sum of user ratings, then have a rating field where i would calculate the average of ratings so i can sort by it, but i can't find a way to automatically update this field whenever the product is updated.
the closest solution i had is to have the rating field's default value be a function that calculates the rating, but again it doesn't run automatically after the product's update
my schema looks like this
const productSchema = new Schema({
title: {
type: String,
required: true
},
views: {
type: Number,
default: 0
},
sumOfRatings: {
type: Number,
default: 0
},
numOfRatings: {
type: Number,
default: 0
},
rating: {
type: Number,
default: function() {
return this.sumOfRatings / this.numOfRatings
}
}, { collection: "Products", timestamps: true });
the question here describes something similar to what i want but there is no answer that does what i need
You can define a middleware for the findAndUpdateOne function, like this:
schema.pre('findOneAndUpdate', async function() {
const docToUpdate = await this.model.findOne(this.getQuery());
});
In this, you will first fetch the document and then set the updated value of the ratings.
i had to do something like this in the end..find the place, get sum and num of ratings from it and then calculate rating and do another query to update it with it, then call the updating function whenever needed..not a big fan of this solution but that's what i could come up with
function updateProductRating(id) {
Product.findById(id).then(product=> {
let calcRating = product.sumOfRatings / product.numOfRatings;
Product.findByIdAndUpdate(id, { rating: calcRating }).then(result=>{
console.log("###### update rating ######")
console.log(result)
})
})
}

how to find multiple data in mongoose?

I cannot see data matching status and name this way. When I run this, the status and name are searching separately. but I want to get the data matching both.
return await Product.find({
$or: [
{ status: args.status },
{ product_name: args.product_name}
]
}).sort({ createdAt: "desc" });
I tried filter and aggregate but I could not get the correct data.
status can be null. $and it doesn't work for me.
solved
let filter = {};
if (args.status) {
filter.status = args.status;
}
if (args.product_name) {
filter.product_name = args.product_name;
}
const result = await Product.find({
...filter
}).sort({ createdAt: "desc" });
return result;
If you want to find products where status == args.status and product_name == args.product_name you would pass a query object like this:
{
status: args.status,
product_name: args.product_name
}
Edit
It sounds like you want to build the filter document depending on available parameters. It might look something like this.
let filter = {}
if (args.status) {
filter.status = args.status
}
if (args.product_name) {
filter.product_name = args.product_name
}
You would then pass this filter object to the find call.

MongoDB updating the wrong subdocument in array

I've recently started using MongoDB using Mongoose (from NodeJS), but now I got stuck updating a subdocument in an array.
Let me show you...
I've set up my Restaurant in MongoDB like so:
_id: ObjectId("5edaaed8d8609c2c47fd6582")
name: "Some name"
tables: Array
0: Object
id: ObjectId("5ee277bab0df345e54614b60")
status: "AVAILABLE"
1: Object
id: ObjectId("5ee277bab0df345e54614b61")
status: "AVAILABLE"
As you can see a restaurant can have multiple tables, obviously.
Now I would like to update the status of a table for which I know the _id. I also know the _id of the restaurant that has the table.
But....I only want to update the status if we have the corresponding tableId and this table has the status 'AVAILABLE'.
My update statement:
const result = await Restaurant.updateOne(
{
_id: ObjectId("5edaaed8d8609c2c47fd6582"),
'tables._id': ObjectId("5ee277bab0df345e54614b61"),
'tables.status': 'AVAILABLE'
},
{ $set: { 'tables.$.status': 'CONFIRMED' } }
);
Guess what happens when I run the update-statement above?
It strangely updates the FIRST table (with the wrong table._id)!
However, when I remove the 'tables.status' filter from the query, it does update the right table:
const result = await Restaurant.updateOne(
{
_id: ObjectId("5edaaed8d8609c2c47fd6582"),
'tables._id': ObjectId("5ee277bab0df345e54614b61")
},
{ $set: { 'tables.$.status': 'CONFIRMED' } }
);
Problem here is that I need the status to be 'AVAILABLE', or else it should not update!
Can anybody point me in the wright direction with this?
according to the docs, the positional $ operator acts as a placeholder for the first element that matches the query document
so you are updating only the first array element in the document that matches your query
you should use the filtered positional operator $[identifier]
so your query will be something like that
const result = await Restaurant.updateOne(
{
_id: ObjectId("5edaaed8d8609c2c47fd6582"),
'tables._id': ObjectId("5ee277bab0df345e54614b61"),
'tables.status': 'AVAILABLE'
},
{
$set: { 'tables.$[table].status': 'CONFIRMED' } // update part
},
{
arrayFilters: [{ "table._id": ObjectId("5ee277bab0df345e54614b61"), 'table.status': 'AVAILABLE' }] // options part
}
);
by this way, you're updating the table element that has that tableId and status
hope it helps

MongoDB: Get all mentioned items

I've got two relations in my Mongoose/MongoDB-Application:
USER:
{
name: String,
items: [{ type: mongoose.Schema.ObjectId, ref: 'Spot' }]
}
and
ITEM
{
title: String,
price: Number
}
As you can see, my user-collection containing a "has-many"-relation to the item-collection.
I'm wondering how to get all Items which are mentioned in the items-field of on specific user.
Guess its very common question, but I haven't found any solution on my own in the Docs or elsewhere. Can anybody help me with that?
If you are Storing items reference in user collection,then fetch all items from user,it will give you a array of object ids of items and then you can access all items bases on their ids
var itemIdsArray = User.items;
Item.find({
'_id': { $in: itemIdsArray}
}, function(err, docs){
console.log(docs);
});
You can get the items at the same time you query for the user, by using Mongoose's support for population:
User.findOne({_id: userId}).populate('items').exec(function(err, user) {
// user.items contains the referenced docs instead of just the ObjectIds
});

Is it possible to extract only embedded documents from a Model in Mongoose?

I'd like to run a query on a Model, but only return embedded documents where the query matches. Consider the following...
var EventSchema = new mongoose.Schema({
typ : { type: String },
meta : { type: String }
});
var DaySchema = new mongoose.Schema({
uid: mongoose.Schema.ObjectId,
events: [EventSchema],
dateR: { type: Date, 'default': Date.now }
});
function getem() {
DayModel.find({events.typ : 'magic'}, function(err, days) {
// magic. ideally this would return a list of events rather then days
});
}
That find operation will return a list of DayModel documents. But what I'd really like is a list of EventSchemas alone. Is this possible?
It's not possible to fetch the Event objects directly, but you can restrict which fields your query returns like this:
DayModel.find({events.typ : 'magic'}, ['events'], function(err, days) {
...
});
You will still need to loop through the results to extract the actual embedded fields from the documents returned by the query, however.