Constructing a framework through an aggregation query to categorize by two identifiers

I possess a variety of documents similar to the example displayed below as 3 distinct objects.

{
comment:{ 
      text_sentiment: "positive",
      topic: "A"
   }
}, // DOC-1

{
comment:{ 
      text_sentiment: "negative",
      topic: "A"
}}, // DOC-2

{
comment:{ 
      text_sentiment: "positive",
      topic: "B"
}},..//DOC-3 .. 

My goal is to construct an aggregation that produces outcomes in the format outlined below:

{
   topic: "A",
   topicOccurance: 2,
   sentiment: {
      positive: 3,
      negative: 2,
      neutral: 0
   }

},...

I have devised an aggregation that successfully groups data by topic and text_sentiment, but I am uncertain about how to generate a structure resembling the one specified above. The aggregation I have created is presented below.

   db.MyCollection.aggregate({
       $match: {
           _id: "xyz",
           "comment.topic": {$exists: 1},
       }
   },{
       $group: {
           _id: {
               topic: "$comment.topic",
               text_sentiment: "$comment.text_sentiment"
               
           },
           total: {$sum: 1},
           
       }
   },{
       $project: {
           topic: {
               name: "$_id.topic",
               occurence: "$total"
           },
           sentiment: "$_id.text_sentiment"
       }
   },{
       $sort: {"topic.occurence": -1}
   })

Although it successfully divides data by topic and sentiment, the resulting structure does not match the desired format. How can I achieve a structure similar to the required one?

Answer №1

Response 1

To achieve the desired outcome, it is necessary to have 2 $group stages.

  1. $match
  2. $group - Group by comment.topic and comment.text_sentiment, then use $sum.
  3. $group - Group by _id.topic with $sum; include text_sentiment and total from the previous stage in text_sentiments using $push.
  4. $project - Customize output documents by setting sentiment through converting the array of text_sentiments into key-value pairs with $arrayToObject.
  5. $sort
db.collection.aggregate([
  {
    $match: {
      _id: "xyz",
      "comment.topic": {
        $exists: 1
      },
      
    }
  },
  {
    $group: {
      _id: {
        topic: "$comment.topic",
        text_sentiment: "$comment.text_sentiment"
      },
      total: {
        $sum: 1
      },
      
    }
  },
  {
    $group: {
      _id: "$_id.topic",
      total: {
        $sum: 1
      },
      text_sentiments: {
        $push: {
          k: "$_id.text_sentiment",
          v: "$total"
        }
      }
    }
  },
  {
    $project: {
      topic: "$_id",
      topicOccurance: "$total",
      sentiment: {
        "$arrayToObject": "$text_sentiments"
      }
    }
  },
  {
    $sort: {
      "topicOccurance": -1
    }
  }
])

Example Mongo Playground (Response 1)


Response 2

If the values for text_sentiment are constant, the following query can be utilized:

db.collection.aggregate([
  {
    $match: {
      _id: "xyz",
      "comment.topic": {
        $exists: 1
      },
      
    }
  },
  {
    $group: {
      _id: "$comment.topic",
      total: {
        $sum: 1
      },
      text_sentiments: {
        $push: "$comment.text_sentiment"
      }
    }
  },
  {
    $project: {
      topic: "$_id",
      topicOccurance: "$total",
      sentiment: {
        "positive": {
          $reduce: {
            input: "$text_sentiments",
            initialValue: 0,
            in: {
              $sum: [
                "$$value",
                {
                  "$cond": {
                    "if": {
                      $eq: [
                        "$$this",
                        "positive"
                      ]
                    },
                    "then": 1,
                    "else": 0
                  }
                }
              ]
            }
          }
        },
        "negative": {
          $reduce: {
            input: "$text_sentiments",
            initialValue: 0,
            in: {
              $sum: [
                "$$value",
                {
                  "$cond": {
                    "if": {
                      $eq: [
                        "$$this",
                        "negative"
                      ]
                    },
                    "then": 1,
                    "else": 0
                  }
                }
              ]
            }
          }
        },
        "neutral": {
          $reduce: {
            input: "$text_sentiments",
            initialValue: 0,
            in: {
              $sum: [
                "$$value",
                {
                  "$cond": {
                    "if": {
                      $eq: [
                        "$$this",
                        "neutral"
                      ]
                    },
                    "then": 1,
                    "else": 0
                  }
                }
              ]
            }
          }
        }
      }
    }
  },
  {
    $sort: {
      "topicOccurance": -1
    }
  }
])

Drawback: Modification of the query is required when adding/removing a value for text_sentiment.

Example Mongo Playground (Response 2)


Response 3

Another approach similar to Response 2 involves utilizing $size and $filter instead of $reduce.

db.collection.aggregate([
  {
    $match: {
      //_id: "xyz",
      "comment.topic": {
        $exists: 1
      }, 
    }
  },
  {
    $group: {
      _id: "$comment.topic",
      total: {
        $sum: 1
      },
      text_sentiments: {
        $push: "$comment.text_sentiment"
      }
    }
  },
  {
    $project: {
      topic: "$_id",
      topicOccurance: "$total",
      sentiment: {
        "positive": {
          $size: {
            $filter: {
              input: "$text_sentiments",
              cond: {
                $eq: [
                  "$$this",
                  "positive"
                ]
              }
            }
          }
        },
        "negative": {
          $size: {
            $filter: {
              input: "$text_sentiments",
              cond: {
                $eq: [
                  "$$this",
                  "negative"
                ]
              }
            }
          }
        },
        "neutral": {
          $size: {
            $filter: {
              input: "$text_sentiments",
              cond: {
                $eq: [
                  "$$this",
                  "neutral"
                ]
              }
            }
          }
        }  
      }
    }
  },
  {
    $sort: {
      "topicOccurance": -1
    }
  }
])

Example Mongo Playground (Response 3)

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

Exploring the process of defining methods within a child Vue component

componentA.vue: <script lang="ts"> import { Vue } from 'vue-property-decorator' @Component export default class ComponentA extends Vue { public methodA(): void { // } } </script> componentB.vue: <template> ...

Subscribe on Footer triggers automatic scrolling to the bottom of the page

Whenever I fill out the form in the footer and hit submit, it directs me to a different page while automatically scrolling back down to the footer. I'm looking for a solution that prevents this automatic scrolling. I've attempted using window.sc ...

What is the method to set precise values on a range slider?

Currently, I am developing a PHP website that requires a slider for displaying the years of publications. In essence, I need the slider to navigate through the different years when the publications were released. // Database connection and other PHP code ...

Looking for non-case-sensitive letters in a text input field

Having trouble with case-insensitive search using jquery? The search option seems to be working fine, but there's an issue with uppercase and lowercase letters in the content. Check out my full code on jsfiddle where if I search for "senthil" it doesn ...

Storing JSON data in LocalStorage or within the App on Ionic 2

I am currently in the process of developing a mobile app for both IOS and Android platforms. The app will feature a list of objects including images, names, etc., which are stored on a backend server powered by node.js. My goal is to allow users of the ap ...

EasyWaySaveArchive in ninja training - mastering the art of retrieving components

Recently started learning about dojo and JavaScript in general. I am currently working on a code snippet that requires a button to change based on the result of a php database query. The PHP script is already complete, and the code I am using so far looks ...

What is the most effective way to access content from a webpage that is rendered

Is there a reliable way to download from links on a JavaScript rendered webpage using Python as the preferred language? I have attempted to use the Selenium Python bindings on a headless server, but it has proven to be slow, error-prone, and unable to acc ...

The database did not respond, causing the API to resolve without sending a response for the specified endpoint (/api/dates). This could potentially lead to requests becoming stalled in Next

I have been attempting to retrieve a list of dates from my database in a straightforward manner, but unfortunately, I am not receiving any response (even after trying to log the response data to the console). The only feedback I seem to be getting when sel ...

Mongoose is unable to update arrays, so it will simply create a new array

Having trouble updating my collection without any errors. Can someone lend a hand? I've been at this for 3 hours now. const product_id = req.body.cartItems.product_id; const item = cart.cartItems.find(c => c.product_id == product_id); i ...

Is there a specific jest matcher available for comparing Array<Objects>?

I'm currently testing the equality of two arrays of objects and have found that the toEqual matcher in Jest only works for arrays of strings. Is there another matcher available in Jest that can handle this condition? Please refrain from marking this a ...

Ways to fix the loading error in AngularJS version 1.3.5?

My HTML page includes AngularJS components. Below is the code snippet: <!DOCTYPE html> <html ng-app="MyApp"> <head> <base href="/"> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> &l ...

how to bind data to an array in Angular

I recently developed an Angular 7 application that features the capability to dynamically add and remove controls. However, I am facing challenges when it comes to binding the data to the form. In the code snippet below, I am focusing on the process of ad ...

Using Slick Slider and Bootstrap CSS to display text on top of images

Currently, I am utilizing the slick slider from and I want to overlay text on top of the image. I tried using bootstrap carousel-caption, which works well with any image. However, with slick slider, it seems like the text is not at the highest level as I ...

Receiving an undefined error response when attempting to submit a form using JQuery ajax for file retrieval

Due to security issues with file manipulation, I must use an Ajax post method to request the file. The server-side URL responds with a pdf file. The Java Servlet code on the server side is as follows: ServletOutputStream out = response.getOutp ...

AngularJS Controller managing the entry field

I am still learning AngularJS and attempting to retrieve user input from a text box to use for querying an endpoint and displaying the result in another text box. Here is a preview of the user interface: https://i.sstatic.net/zXCgQ.jpg Here is the HTML c ...

What is the optimal strategy for Database Design?

Database Design | Strategy 1: Strategy 1 involves the management of two distinct tables for different types of entities, with relationships established as shown in the accompanying image. Database Design | Strategy 2: Strategy 2 features a single table ...

Elegant Bootstrap 4 Carousel featuring a glimpse of the upcoming slide alongside the primary carousel item

I am in search of a straightforward Bootstrap 4 carousel that showcases a glimpse of the next slide on the right. Despite exploring similar questions, I have not found a suitable solution. The links to those questions are: 1)Bootstrap carousel reveal part ...

What is the best way to update a value in Angular's HTML binding?

I'm working with some HTML code <div ng-bind-html="safeHtml"></div> <button ng-click="refresh()">refresh</button> What should the refresh function look like in order to update the value? ...

Rendering JSON Data in JavaScript using Table Pagination

Currently, I am working on rendering JSON data from a URL onto a table. My challenge is to display only 10 rows per page and I'm seeking guidance on how to achieve this. Below is the code snippet that I am using for rendering the data: const url = " ...

Direct your attention to the final item in a visible array within a ReactJS component

Currently, I'm in the process of developing a chat application using reactjs and am faced with the challenge of adjusting focus to the latest message whenever a new one is added to the array. The structure of my react chat window is as follows: < ...