Leveraging mongo aggregate functionality within the meteor framework to calculate the total number of unlocked and locked

So, I have a collection where I store documents related to users with a structure like:

{_id: "hofjew233332j4", userId: "fhewojfw34324", achievementUnlocked: true };

My goal is to use the aggregate and underscore functions to group the documents by user id and calculate the percentage of records where achievementUnlocked is set to true. This would result in a document like:

{_id: "fhewojfw34324(userId)", unlockPercentage: 40 (achievementUnlocked: true / all docs) }

Is there a way to achieve this without having to retrieve the documents multiple times?

Answer №1

To start, group the data by the count of achievements unlocked with the value of true, and then use this information in a project to calculate the percentage using the following aggregation:

db.collectionName.aggregate([{
    "$group": {
        "_id": "$userId",
        "achievementUnlockedTrueCount": {
            "$sum": {
                "$cond": {
                    "if": {
                        "$eq": ["$achievementUnlocked", true] //count of achievements unlocked = true count 
                    },
                    "then": 1,
                    "else": 0
                }
            }
        },
        "totalCount": {
            "$sum": 1 // get the total count of grouped documents 
        }
    }
}, {
    "$project": {
        "unlockPercentage": {
            "$multiply": [{
                "$divide": ["$achievementUnlockedTrueCount", "$totalCount"] //use this in the project to calculate %
            }, 100]
        }
    }
}]).pretty()

Answer №2

In my opinion, there's no need for aggregation in this scenario since the data appears to be simple. It would be more efficient to keep track of "locked" and "unlocked" achievements for each user and/or game in an array.

Consider a document structured like this:

{
    "_id": "hofjew233332j4", 
    "userId": "fhewojfw34324",
    "gameId": "v3XWHHvFSHwYxxk6H",
    "achievementsCount": 5,
    "locked": ["One", "Two", "Four", "Five"],
    "lockedCount": 4,
    "unlocked": ["Three"],
    "unlockedCount": 1
}

Initially, you would set up each user and game with all achievements in the "locked" array, except for one already placed in the "unlocked" array. The "count" fields indicate the number of elements in each array.

To "unlock" a new achievement, you can simply update the document to remove it from the "locked" array and add it to the "unlocked" array, while adjusting the "count" values:

Achievements.update({
       "userId": "fhewojfw34324",
       "gameId": "v3XWHHvFSHwYxxk6H",
       "locked": "Four",
       "unlocked": { "$ne": "Four" }},
   {
       "$push": { "unlocked": "Four" },
       "$pull": { "locked": "Four" },
       "$inc": {
           "lockedCount": -1,
           "unlockedCount": 1
       }
   }
)

After this update, the document will look like this:

{
    "_id": "hofjew233332j4", 
    "userId": "fhewojfw34324",
    "gameId": "v3XWHHvFSHwYxxk6H",
    "achievementsCount": 5,
    "locked": ["One", "Two", "Five"],
    "lockedCount": 3,
    "unlocked": ["Three", "Four"],
    "unlockedCount": 2
}

This approach is simple to follow as each update maintains the correct data values. If you need additional information like a "percentage," you can easily calculate it using a simple aggregation:

Achievements.aggregate([
    { "$project": {
        "userId": 1,
        "gameId": 1,
        "percentUnlocked": { "$divide": [ "$unlockedCount", "$achivementsCount" ] }
])

Alternatively, you can perform this calculation in the client-side code. This model not only simplifies real aggregations but also provides more extensive information. Additionally, calculating data dynamically is more efficient than relying on a separate process to sum up the data.

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

Try utilizing MutationObserver to monitor changes in various nodes

I am faced with a situation where I have elements in my HTML that are dynamically populated with text from an API. My goal is to check if all these elements have a value and then trigger a function accordingly. The current code I have only allows me to obs ...

The value of the $scope variable remains constant within the function

I am facing an issue with a scope variable that I passed to a function. Even though the variable is accessible inside the function, its value does not change when I try to modify it. Below is my HTML code: <div class="console_item" ng-class="dropdwns.a ...

What is the best way to extract raw data from a kendo dataSource?

After fetching data for my Kendo grid from the backend and populating it in the alignedProcessesToRiskGridOptions, I noticed that while the data is displayed in the grid, I also need access to the raw data for additional logic. Is there a way to retrieve d ...

The getter function is called before the v-if directive in the child slot component

I have developed a Vue component that needs to perform certain logic and based on that logic, I want to render the content inside the slot. <logic-component> <div>{{ data }}</div> </logic-component> The data is retrieved using a ...

Positioning a dropdown menu on top of a Google Map in React with JavaScript: Best Practices

I've been attempting to place a dropdown menu at a specific location on my Google Map, specifically in the top right (or top left is also acceptable). Below is the current output I am seeing: Here is the expected result: Modifications After trying ...

Using AngularJS to load a custom JavaScript file within an ng-view

In my sample project, I have utilized Angular Js for the front-end. I have developed a template and implemented dynamic loading of views based on requirements. For this application, I am utilizing angular, jquery, and cusotm js. The structure of my templat ...

Issue: Unauthorized access: nodeMailer despite correct configuration

Having trouble with an error when using the less app option on Gmail. I don't have 2-factor authentication enabled, and my credentials are correct. I can log in to my file to verify this, but the error still persists. const nodemailer = require(" ...

I am unable to generate a vite application within WSL

My system is running node version 10.19.0 and npm version 6.14.4. Whenever I try to run create-vite@latest client, I encounter the following error: npx: installed 1 in 0.79s /home/victor/.npm/_npx/86414/lib/node_modules/create-vite/index.js:3 import &apos ...

How is it possible that this component is able to work with slotting without needing to specify the slot name

I have created two Web Components using Stencil.js: a Dropdown and a Card. Within my Dropdown, the structure is as follows: <div class='dropdown'> <slot name="button"/> <slot/> </div> The nested chil ...

What could be causing my button to not capture the value of this input text field?

After clicking the button, I am trying to log the value of the input text field in the console. However, it just shows up as blank. Despite checking my code multiple times, I can't seem to figure out why. Any insights would be greatly appreciated! &l ...

Adding a plethora of HTML using jQuery

Struggling to create a single-page website, I've found myself relying heavily on jQuery's .append function. But surely, there has to be a more efficient method for appending large chunks of code (like tables, graphs, etc.) onto a page. Take this ...

Error occurred when sending form data while uploading a file

Upon trying to upload a file using the formData.append(key, value);, an error message is displayed in the value section: The argument of type 'unknown' cannot be assigned to a parameter of type 'string | Blob'. Type '{}' is ...

Issue with clearTimeout function not functioning properly on keyup event in iFrame

While there may be many similar questions out there, I have yet to find a solution that works for me. Currently, I am developing a WYSIWYG editor and I want it to save when the user performs a keyup action. However, I do not want it to update after every ...

What is the most effective way to programmatically recreate scope in AngularJS?

Currently, I am working on implementing UI-router to handle state changes in my application. My initial belief was that switching states on a dynamic route would result in the current scope getting destroyed and a new scope being created for the template w ...

Fading Out with JQuery

My page contains about 30 same-sized divs with different classes, such as: .mosaic-block, .events, .exhibitions, .gallery, .sponsors, .hospitality, .workshops, .lectures { background:rgba(0, 0, 0, .30); float:left; position:relative; overf ...

Guide to dynamically add data to two columns

I have a challenge with organizing multiple inputs into rows with two columns each, ensuring that each input is appended to the appropriate side (50% for each column unless there's an odd number of inputs). Currently, the inputs are being added to se ...

IE page refresh causing jQuery blur to malfunction

Our website features a textbox with a watermark that appears and disappears based on focus and blur events in jQuery. However, we have encountered a unique issue with Internet Explorer browsers. Whenever a user clicks on the textbox, the watermark disapp ...

What is the best way to display live data to multiple users with React and Firebase?

I'm working on a messaging app that needs to update in real-time. Currently, I have implemented the functionality to log in using Google, post messages, and display them on the screen. However, when another user logs in with a different Google account ...

If the visitor navigated from a different page within the site, then take one course of action; otherwise

How can I achieve the following scenario: There are two pages on a website; Parent page and Inside page. If a user navigates directly to the Inside page by entering the URL or clicking a link from a page other than the Parent page, display "foo". However, ...

Creating a Recursive Facebook Page Data Scraper using Selenium and Node.js

I am trying to iterate through an array of Facebook page IDs and retrieve the code from each event page. However, I am encountering a problem where I only get the code of the last page ID in the array repeated multiple times. For example, if there are 3 ID ...