Count the occurrences of different fields in a document based on a specified condition

Seeking a way to extract specific values and calculate the frequency of those values in a collection based on a certain key's ID. Consider this example of a single document from a Game Logs collection:

{
    "_id": "5af88940b73b2936dcb6dfdb",
    "date": "2018-05-13T18:51:44.548Z",
    "playerOne": "5af888d0b73b2936dcb6dfd3",
    "playerOneThrew": "Scissors",
    "playerTwo": "5af88918b73b2936dcb6dfd7",
    "playerTwoThrew": "Rock",
    "winner": "5af88918b73b2936dcb6dfd7",
     "__v": 0
}

The goal is to query based on a Player's ID (either playerOne or playerTwo) and retrieve the hand that player threw for each game. The objective is to obtain a total count of all hands thrown (rock, paper, scissors) by a specific Player's ID.

Is there a method to aggregate a count for each "thrown hand" value across all documents in a collection and generate an output like:

{
    "player": "5af888d0b73b2936dcb6dfd3",
    "Rock": 3,
    "Paper": 6,
    "Scissors": 12
}

I've been contemplating setting a variable based on matching playerOne || playerTwo IDs, then employing a switch statement to aggregate counts of "rock", "paper", "scissors". However, my limited experience with MongoDB syntax has made it challenging to execute such a query effectively.

Below is my current approach:

GameLogs.aggregate(
  [
    {
      $let: {
        vars: {
          playerThrew: {
            $cond: {
              if: {
                $eq: { playerOne: id },
              }
              }, then: playerOneThrew, else: playerTwoThrew
            }
        }
      }
    },
    {
      $switch: {
        branches: [
          { case: { $eq: 'Rock' }, then: { $count: 'Rock' } },
          { case: { $eq: 'Paper' }, then: { $count: 'Paper' } },
          { case: { $eq: 'Scissors' }, then: { $count: 'Scissors' } },
        ]
      }
    }
  ]
)

Pseudocode:

if playerOne == id
  playerThrew = playerOneThrows
else if playerTwo == id
  playerThrew = playerTwoThrows

switch playerThrew {
  case 'rock':
    rockCount++
    break
  case 'paper':
    paperCount++
    break
  case 'scissors':
    scissorsCount++
    break
}

return {
  player_id: id,
  rock: rockCount,
  paper: paperCount,
  scissors: scissorCount
}

Any guidance on this matter would be greatly appreciated.

Answer №1

Radosław Miernik's answer is on point, however, an essential $match statement is still required since the original poster specifically needs data for a single player.

Assuming that playerId represents the unique identifier of the player whose information is requested.

The code snippet below is expected to function correctly and deliver the desired outcome:

collection.aggregate([
  {$project: {
    player: [
      {id: '$playerOne', threw: '$playerOneThrew'},
      {id: '$playerTwo', threw: '$playerTwoThrew'}
    ],
  }},
  {$unwind: '$player'},
  {$match : { 
    "player.id" : playerId 
  }},
  {$group: {
    _id: '$player.id',
    player : {$first : "$player.id},
    Paper:    {$sum: {$cond: [{$eq: ['$player.threw', 'Paper'   ]}, 1, 0]}},
    Rock:     {$sum: {$cond: [{$eq: ['$player.threw', 'Rock'    ]}, 1, 0]}},
    Scissors: {$sum: {$cond: [{$eq: ['$player.threw', 'Scissors']}, 1, 0]}}
  }}
])

This query will yield the precise data you require along with an additional _id attribute.

{
    "_id": "5af888d0b73b2936dcb6dfd3",
    "player": "5af888d0b73b2936dcb6dfd3",
    "Rock": 3,
    "Paper": 6,
    "Scissors": 12
}

Answer №2

If you want to organize your documents more efficiently, consider splitting them into two normalized sets and then grouping them easily:

collection.aggregate([
  {$match: {
    $or: [
      {playerOne: playerId},
      {playerTwo: playerId}
    ]
  },
  {$project: {
    player: [
      {id: '$playerOne', threw: '$playerOneThrew'},
      {id: '$playerTwo', threw: '$playerTwoThrew'}
    ],
  }},
  {$unwind: '$player'},
  {$match: {'player.id': playerId},
  {$group: {
    _id: '$player.id',
    Paper:    {$sum: {$cond: [{$eq: ['$player.threw', 'Paper'   ]}, 1, 0]}},
    Rock:     {$sum: {$cond: [{$eq: ['$player.threw', 'Rock'    ]}, 1, 0]}},
    Scissors: {$sum: {$cond: [{$eq: ['$player.threw', 'Scissors']}, 1, 0]}}
  }}
])

UPDATE: I have included $match stages to effectively filter scores specifically for the player in question.

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

Angular2 - HTML not displaying the response

I am currently mastering angularjs2. In my latest project, I attempted to fetch data from an API and received a response successfully. However, I encountered an issue where the response is not rendering in homepage.component.html as expected. I am unsure o ...

React splits the page and interrupts the scroll event listener

For some reason, my webpage has been split by the React.js library. When attempting to scroll down while hovering over the top navigation menu, scrolling becomes impossible. However, scrolling works fine when done on any other part of the screen. I' ...

Error occurred in Flask due to request names being dynamically generated using JavaScript

My current project involves creating an app that calculates transit projections based on input years and other variables. I've written a JavaScript script where users can add new types of vehicles, each generating a unique div with specific ids and na ...

React DOM's offsetHeight prior to rendering

I am struggling to figure out how to position a React DOM element based on its offsetHeight. The issue is that I cannot determine the offsetHeight of an element that hasn't been created yet (so the height cannot be passed as a parameter to the render ...

Efficient method for iterating over elements in a sizable JSON object, making alterations, and sequentially adding them to a mongodb collection

When making a request to a URL and receiving a large JSON, I aim to loop through each item to modify the JSON objects before inserting them into a MongoDB collection. However, I am facing difficulties in achieving this as a newcomer to technologies like No ...

Is it necessary to implement clustering for each route in an Express.js application?

Currently, I am utilizing the cluster module to fork my application within my index.js, which serves as the primary file in the root directory of my website. My application consists of numerous routes. Should I incorporate the cluster code to encapsulate a ...

Executing all promises later in Node.js using Promise.all

Having a series of promises set up as follows: module.exports = function (a, b, c) { return someAsync(() => { someFunc(a); }) .then(() => myPromises(a, b, c)) .then(result => { console.log(&apos ...

Associating information with a dropdown menu

My goal is to bind a drop-down using a global variable (the array name). The binding works correctly here: Click here - dropdown is populating fine var name = ['us', 'china', 'kenya', 'us', 'china', &ap ...

The Challenge of the Universe's Origin

While watching the latest episode of The Big Bang Theory (Season 11, Episode 20), I found myself intrigued by Dr. Wolcott's unusual encryption method. In a quirky twist, this nutty theoretical cosmologist wrote his notes backward and converted all let ...

Dynamically linking tabbable tabs is a useful technique that allows for

I have been working on an app that organizes websites into groups displayed as tabs in a tabbable navigator using Twitter Bootstrap. The idea is that each time a new group is added, a new tab should appear. Currently, this is how it looks: The functionali ...

Using JavaScript to detect the Facebook like button on a webpage

I am currently developing an application for Facebook using HTML and Javascript. My goal is to be able to detect when a user clicks the "Like" button on my company's Facebook page without needing to embed a separate "Like" button within my app itself. ...

Navigating with Angular: Understanding ng-view and Routing

How does Angular understand which template view to request containing the 'ng-view' element? If I directly navigate within my application to http://localhost/Categories/List/accessories , a request is still sent to '/' or the index ...

Is it possible to determine the outcome of a JavaScript function using Python?

I am currently in the process of creating a web crawler. Extracting links from HTML is simple, but finding links that are generated by JavaScript proves to be more challenging for me. Is there a way to access the JavaScript output in order to determine w ...

What is the reason for the request body being undefined?

I have a JavaScript file named index.js that contains: const express = require('express'); const bodyParser = require('body-parser'); const cors = require('cors'); const db = require('./db'); const movieRouter = re ...

Is there a way to utilize javascript std input/output in repl.it effectively?

I created a straightforward program that calculates the factorial of a specified number, and I am interested in running it on repl.it. During its execution, I would like to interact with standard input and output through the command line. Is there a way ...

Dynamic fade effect with GSAP on scroll

Currently, I am attempting to implement a fade out animation with GSAP Scroll Trigger. The aim is for the page to first scroll across the X axis of the title before scrolling up and fading out. While I have made some progress, I am facing an issue where th ...

React.js: Why does the array index change after dropping an element?

When I create a table with items in a checkbox list, the issue arises; after selecting and submitting some items, the index of the remaining items changes. Consequently, re-submitting the remaining items becomes impossible. Below is my code snippet: expo ...

Tips for creating a vertical drawer using jQuery and CSS

Hello, I am currently working on developing a drawer component using Ember.js. If you want to view the progress so far, feel free to check out this jsbin http://jsbin.com/wulija/8/edit My goal is to have the drawer look like the following initially: +--- ...

How can I create 3 conditions in an AJAX request to verify a user's ID?

I am trying to create 3 conditions in Ajax to check when creating a new user id. It works for 2 conditions (username can be used and username is already in use), but I encounter a problem when the user leaves the username field empty, and it still displays ...

Enhancing a Stripe customer object with additional metadata

While setting up a payment system using Stripe, I encountered an issue when trying to add metadata to the customer object. Specifically, I wanted to include my workspace id in the metadata property of the customer. However, upon executing the code below, I ...