What is the most effective way to use mongoose's aggregate function to calculate a score for each item using a specific equation?

In my quest to create a sorting algorithm that identifies the most popular questions, I rely on the following equation:

Rating = (AV * (1 / 50)) + (AL * 3) - (AD * 6)

The parameter I use for sorting is called Rating. The questions with the highest ratings are considered the trendiest.

AV represents average views, calculated by dividing Views by days ago

AL represents average likes, calculated by dividing Likes by days ago

AD represents average dislikes, calculated by dividing Dislikes by days ago

Below is the data structure for the documents we are sorting:

const Question = mongoose.model(
  "Question",
  new Schema({
    title: { type: String, required: true },
    text: { type: String },
    authorUsername: { type: String, required: true },
    dateCreated: {},
    answers: { type: Array, required: true },
    likes: { type: Array, required: true },
    dislikes: { type: Array, required: true },
    tag: { type: String, required: true },
    views: {type: Array, required:true},
  })
);

Here's an example of a question:

document:_id:620935985f6865b4e85c333d,
title:"How do i make a lemon?",
text:"yeet",
authorUsername:"SweetWhite",
dateCreated:2022-02-13T16:45:12.598+00:00,
answers: Array,
likes:
0:1,
1:"SweetWhite",
dislikes: Array,
0:0,
tag:
"Social",
views:Array,
0:1,
1:"SweetWhite",
__v:0

Note that the length of the views, likes, and dislikes arrays are stored in the first item which is a number, so when using $size it will be one too big.

Is there anyone who can utilize Question.aggregate with the given equation and document to filter out the top 15 trendy questions (highest rating)?

Thanks!
EDIT: Here is the code snippet I have implemented:

router.get('/questions/hot', async (req,res,next) => {
  try{
    const results = await Question.aggregate([
        /*{$arrayElemAt:[{$ifNull:["$likes",[0]]}, 0]},
        {$arrayElemAt:[{$ifNull:["$dislikes",[0]]}, 0]},
        {$arrayElemAt:[{$ifNull:["$views",[0]]}, 0]}*/
      {$project: {rating: {$add:[
        {$divide: 
          [
            {$divide: [{$arrayElemAt:[{$ifNull:["$views",[0]]}, 0]},  1]},
            50
          ]},
        {$subtract: 
          [
            {$multiply: [{$divide: [{$arrayElemAt:[{$ifNull:["$likes",[0]]}, 0]},  1]}, 3]},
            {$multiply: [{$divide: [{$arrayElemAt:[{$ifNull:["$dislikes",[0]]}, 0]},  1]}, 6]}
          ]
        }
        ]}
      }},
      {$sort: {rating:-1}},
      {$limit: 15}
    ]).exec();
    console.log(results)
    const questions = await Promise.all(results.map(({_id}) => Question.findOne({_id}).exec()));
    res.render('content', { whichOne: 5, user: req.user, questions:questions});
  }
  catch(err){
    next(err);
  }
});

Although the algorithm is working correctly overall, the issue lies in the calculation of the average likes/dislikes/views per day. Currently, I am dividing by 1 instead of considering the actual days elapsed since creation. Any insights on how to incorporate this time factor would be greatly appreciated.

Answer №1

Finally cracked the code! Sharing my solution for future reference:

router.get('/questions/hot', async (req,res,next) => {
  try{
    let now = new Date();
    const results = await Question.aggregate([
        /*{$arrayElemAt:[{$ifNull:["$likes",[0]]}, 0]},
        {$arrayElemAt:[{$ifNull:["$dislikes",[0]]}, 0]},
        {$arrayElemAt:[{$ifNull:["$views",[0]]}, 0]}*/
      {$project: {rating: {$add:[
        {$divide: 
          [
            {$divide: [{$arrayElemAt:[{$ifNull:["$views",[0]]}, 0]},  {$subtract:[now, '$dateCreated']}]},
            50
          ]},
        {$subtract: 
          [
            {$multiply: [{$divide: [{$arrayElemAt:[{$ifNull:["$likes",[0]]}, 0]}, {$subtract:[now, '$dateCreated']}]}, 3]},
            {$multiply: [{$divide: [{$arrayElemAt:[{$ifNull:["$dislikes",[0]]}, 0]},  {$subtract:[now, '$dateCreated']}]}, 6]}
          ]
        }
        ]}
      }},
      {$sort: {rating:-1}},
      {$limit: 15}
    ]).exec();
    console.log(results)
    const questions = await Promise.all(results.map(({_id}) => Question.findOne({_id}).exec()));
    res.render('content', { whichOne: 5, user: req.user, questions:questions});
  }
  catch(err){
    next(err);
  }
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

A guide on transforming Jonatas Walker's TimePicker into a custom JavaScript class or a versatile jQuery plugin

I came across a timepicker solution on this Stack Overflow answer that I really liked. However, I encountered difficulties trying to implement it in a project where input elements are dynamically created. It seemed like the timepicker required specific han ...

Could you please explain the distinctions among onLoad, onDomready, No wrap - in <head>, and No wrap - in <body>?

When it comes to editing my code, I rely on JSFiddle. However, I have noticed that in certain instances when running JavaScript or jQuery, the code only works if I choose "No wrap - <head>" or "No wrap - <body>". CLICK HERE FOR THE JSFIDDLE EX ...

Run a Promise with RxJS, followed by a combination of the latest values

Apologies for bombarding you with more questions recently, but I'm really struggling to understand how everything links together. Currently, a user is utilizing promise-based storage to store the names of feeds they wish to filter out. The Social Fee ...

Activate the react-select autocomplete feature once a certain number of keystrokes have been input

Is there a method to activate autocomplete only after typing three keystrokes? I am employing the react-select library for this purpose. ...

Using v-bind:class in Vue.js does not successfully assign a value in the method

Why is the width of my div not changing when I try to bind it to a data attribute that ranges from 0 to 100? <div class="bar" :style="{ width: percentage + '%' }"></div> <script> export default { name: 'app&ap ...

JQuery is having trouble with playing multiple sound files or causing delays with events

I've been working on a project that involves playing sounds for each letter in a list of text. However, I'm encountering an issue where only the last sound file is played instead of looping through every element. I've attempted to delay the ...

Creating an HTML table using data from a JSON object

I have successfully converted a CSV file to a JSON object and now I want to display this data in an HTML table. However, when using the populatetable() function, the data is not being displayed correctly. The conversion from CSV to JSON can be viewed here. ...

The input field for Google Places Autocomplete now includes the code "display: none"

I'm currently working on an AngularJS application and have implemented this directive to enable Google Maps API places autocomplete in the input field below. <div> <input type="text" autocomplete="off" g-places-autocomplete ...

Using Meteor methods to selectively share a specific MongoDB document publicly while keeping the rest private

Just starting out with Meteor and Mongo. I have some JSON data stored in mongo that I want to make accessible publicly using an encrypted token without relying on the standard method: //app/models/stuff.js Stuff = new Mongo.Collection("stuff"); Meteor.pu ...

Showing Nested Arrays in Angular 2

If I have an array with image links as shown below, how can I display them in HTML? array = [ { img: [ {0: 'http://hairsalonfurniture.eu/wp-uploads/750x480_how-to-create-a-nice-hair-salon-s-reception-gjzd.jpg',}, {1: 'http ...

Determine if a cookie exists using jQuery when the page loads

Visit my awesome website. Check out the following JQuery code snippet: (function($){ var MSG_THANK_YOU = 'Thanks a lot for your vote!'; var MSG_CANT_VOTE = "Looks like you have already voted!"; var MSG_SELECT_ONE = "Please select an option fi ...

An extremely basic inquiry regarding JavaScript form validation

Is there a way to add a simple icon next to a form field when users type in their name or email address? I only want the icon to show if even one character is typed. The icon should change based on whether the field has content (success) or not (fail). L ...

A guide on organizing and categorizing data by name with angularjs

Presented here is a list of descriptions associated with specific names. I am seeking guidance on how to group or list the descriptions by name. html: <body ng-app="app" ng-controller="MainCtrl"> <div ng-repeat="nameGroup in loopData"> & ...

Why aren't my messages showing up once I exit the textbox on my website?

After writing various functions to compare two passwords, I encountered an issue. My goal was for the message "The passwords match" or "Please enter your password again because the two passwords don't match" to be displayed when clicking out of the "v ...

Transfer me to Mobile, please

Is it possible to redirect users to mobil.navn.dk when they log on from a mobile device such as an iPhone, iPad, or other smartphones? If accessing the site from a browser, users should not be able to log in on navn.dk. I've attempted to achieve this ...

What is the best way to convert Arabic language HTML into a PDF document on the client side?

Utilizing jsPDF and vue js, I successfully implemented a feature to export PDFs. However, I encountered an issue with Arabic characters not displaying properly. Can anyone provide assistance in resolving this problem? ...

What are the counterparts of HasValue and .Value in TypeScript?

There is a method in my code: public cancelOperation(OperationId: string): Promise<void> { // some calls } I retrieve OperationId from another function: let operationId = GetOperationId() {} which returns a nullable OperationId, operat ...

My C# desktop application does not disclose my exact whereabouts

I have successfully retrieved my current location (latitude and longitude) in a web application using the following HTML5 code: <!DOCTYPE html> <html> <body> <p id="demo">Click the button to get your coordinates:</p> ...

The text becomes distorted and unreadable after the function is applied. It is impossible to decipher the message

There is a function called getResourceText(''), which takes a key as an argument. This function provides a translation of the requested text when it is called. setFilterName = (isFilterChanged, buttonId, filterName) => { switch (filterName ...

What causes a stack trace to be logged alongside a rejected Promise in Node version 7.2.0?

After executing this code in Node 7.2.0: let prms = Promise.reject(new Error('error')); prms.catch(() => {}); console.log(prms); I anticipated Promise {<rejected> Error: error} to be displayed on the console, however I was instead pre ...