0

Document structure in cities collection is like this

cities

  {
   _id:  ObjectId("5e78ec62bb5b406776e92fac"),
   city_name: "Mumbai",
   ...
   ...
   subscriptions: [
    {
       _id: 1,
       category: "Print Magazine",
       subscribers: 183476
       options: [
         {
            name: "Time",
            subscribers: 56445
         },
         {
            name: "The Gentlewoman",
            subscribers: 9454
         },
         {
            name: "Gourmand",
            subscribers: 15564
         }
         ...
         ...
       ]
     },
     {
       _id: 2,
       category: "RSS Feed",
       subscribers: 2645873
       options: [
         {
            name: "Finance",
            subscribers: 168465
         },
         {
            name: "Politics",
            subscribers: 56945
         },
         {
            name: "Entrepreneurship",
            subscribers: 56945
         },
         ...
         ...
       ]
     }
   ]
}

Now when a user subscribes like below

{
  cityId: 5e78ec62bb5b406776e92fac
  selections: [
    {
      categoryId: 1,
      options : ["Time", "Gourmand"]
    }, 
    {
      categoryId: 2, 
      selected: ["Politics", "Entrepreneurship"]
    }    
  ]
}

I want to update the following in the cities document

  • Increment subscribers for "Print Magazine" by 1
    • Increment subscribers for "Time" by 1
    • Increment subscribers for "Gourmand" by 1
  • Increment subscribers for "RSS Feed" by 1
    • Increment subscribers for "Politics" by 1
    • Increment subscribers for "Entrepreneurship" by 1

So when an item is subscribed, its subscribers count is incremented by 1. And the category it falls into, its subscriber count is also incremented by 1.

I want to achieve this in a single update query. Any tips how can I do this?

Use case details

Each user's subscription details are stored in user_subscription_details collection(not listed here). subscriptions property in cities holds just the subscription summary for each city.

9
  • user subscribes data is a JSON (not a collection document)? An update with aggregation might be possible. Commented Mar 28, 2020 at 9:20
  • @prasad_does it matter ? since I need to update parts of the document and not the whole document. Commented Mar 28, 2020 at 9:26
  • You want update city_subscriptions based on user subscribes? Commented Mar 28, 2020 at 10:02
  • @Valijon yes. each document represents a city and its subscriptions. Commented Mar 28, 2020 at 10:03
  • Is user suscribers stored in separate collection? We need to perform aggregation with $out as last stage to override whole collection (said by prasad_) Commented Mar 28, 2020 at 10:06

1 Answer 1

0

So I was able to it with the following query

db.cities.updateOne(
{
   _id : ObjectId("5e78ec62bb5b406776e92fac")
},
{ 
   $inc: { 
     "subscriptions.$[category].subscribers" : 1,
     "subscriptions.$[category].options.$[option].subscribers" : 1
   }
},
{ multi: true,
   arrayFilters: [
     { "category._id": {$in: ["1", "2"]} },
     { "option.name": {$in: ["Time", "Gourmand", "Politics", "Entrepreneurship"]} } 
   ]
}
)

Brief Explanation

  • First the document is matched with _id.
  • In update block we will declare the fields to be updated
    • "subscriptions.$[?].subscribers" : 1,
    • "subscriptions.$[?].options.$[?].subscribers" : 1

I have used ? here to show we don't know yet for which elements in the array we need to do these update. Which we can declare in the next block by filtering the array elements that need to be updated.

  • In filter block we filter array elements on some condition
    • { "category._id": {$in: ["1", "2"]} }
    • { "option.name": {$in: ["Time", "Gourmand", "Politics", "Entrepreneurship"]} }

First we filter the elements in the outer array by _id i.e only subscription categories whose _id is either 1 or 2. Next, we filter the elements in the inner options array on the name field. Elements which will pass both filters will get updated.

Note: category in category._id and option in option.name can be any name. But the same name is to be used for fields path in update block.

For, Spring Boot MongoOperation translation of this query look at this answer

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.