Search code examples
mongodbmongodb-queryaggregation-frameworkspring-data-mongodb

Query for nested Map of list of objects in spring data mongodb


I have an entity:

package com.gl.successPaths.user.entity;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;

import com.gl.successPaths.interests.model.InterestArea;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "user")

public class User {

    @Transient
    public static final String SEQUENCE_NAME = "users_sequence";
    @Id
    private long userId;
    private String name;
    private String email;
    private String profileUrl;
    private String profileImgUrl;
    private String contactNumber;
    private String designation;
    private String band;
    private long employeeId;
    private String experience;
    private String department;
    private boolean isIsNotificationActive;
    private Map<String, List<InterestArea>> interestAreas = new HashMap<>();
}

and another class InterestArea.java as

public class InterestArea {
    private long interestAreaId;
    private String interestAreaName;
    private boolean isIsSelected;
}

and its json as:

{
"myMentees": [],
"interestAreas": {
    "Activities & Interests": [
        {
            "interestAreaName": "Bike Riding",
            "isSelected": false,
            "interestAreaId": 17
        },
        {
            "interestAreaName": "Cycling",
            "isSelected": false,
            "interestAreaId": 18
        },
        {
            "interestAreaName": "Running",
            "isSelected": false,
            "interestAreaId": 19
        },
        {
            "interestAreaName": "Cooking",
            "isSelected": false,
            "interestAreaId": 20
        },
        {
            "interestAreaName": "Photography",
            "isSelected": false,
            "interestAreaId": 21
        },
        {
            "interestAreaName": "Yoga/Meditation",
            "isSelected": false,
            "interestAreaId": 22
        }
    ],
    "Wellness": [
        {
            "interestAreaName": "Financial Planning",
            "isSelected": false,
            "interestAreaId": 11
        },
        {
            "interestAreaName": "Health",
            "isSelected": false,
            "interestAreaId": 12
        },
        {
            "interestAreaName": "Motivation",
            "isSelected": false,
            "interestAreaId": 13
        },
        {
            "interestAreaName": "Work life balance",
            "isSelected": false,
            "interestAreaId": 14
        }
    ],
    "Social Impact": [
        {
            "interestAreaName": "Educate to Empower",
            "isSelected": false,
            "interestAreaId": 15
        },
        {
            "interestAreaName": "Environment Drive",
            "isSelected": false,
            "interestAreaId": 16
        }
    ],
    "Career": [
        {
            "interestAreaName": "Technology",
            "isSelected": false,
            "interestAreaId": 1
        },
        {
            "interestAreaName": "AI/ML",
            "isSelected": false,
            "interestAreaId": 2
        },
        {
            "interestAreaName": "Cloud",
            "isSelected": true,
            "interestAreaId": 3
        },
        {
            "interestAreaName": "AR/VR",
            "isSelected": false,
            "interestAreaId": 4
        },
        {
            "interestAreaName": "Cloud Computing",
            "isSelected": false,
            "interestAreaId": 5
        },
        {
            "interestAreaName": "DevOps",
            "isSelected": false,
            "interestAreaId": 6
        },
        {
            "interestAreaName": "Mobile Apps",
            "isSelected": false,
            "interestAreaId": 7
        },
        {
            "interestAreaName": "UI/UX",
            "isSelected": false,
            "interestAreaId": 8
        },
        {
            "interestAreaName": "Data Science",
            "isSelected": false,
            "interestAreaId": 9
        },
        {
            "interestAreaName": "Microservices & Platform",
            "isSelected": false,
            "interestAreaId": 10
        }
    ]
},
"myMentors": [],
"userDetails": {
    "profileUrl": "https://ghantee.com/wp-content/uploads/2022/10/mobile-jesus-wallpaper.jpg",
    "isNotificationActive": false,
    "profileImgUrl": "https://ghantee.com/wp-content/uploads/2022/10/mobile-jesus-wallpaper.jpg",
    "contactNumber": "9689560753",
    "name": "Rachit Kumar",
    "employeeId": 2523,
    "designation": "Software Engineer",
    "band": "Band 1",
    "department": "Engineering",
    "experience": "3Y",
    "userId": 6,
    "email": "[email protected]"
}
}

Now I want to query data for all the Users where for each entry in Map i.e. interestAreas, interestAreaName is "Cloud" and isSelected = true;

I have been trying with query:

@Query("{'interestAreas': {$exists: true, $ne: []},'interestAreas': { $elemMatch: {'v': '$in': {'interestAreaName': ?0, isSelected: true}}}}")
List<User> findAllByUserInterestAreasName(String tags);

Solution

  • You may have to use aggregation.

    db.collection.aggregate([
      {
        "$addFields": {
          "interestArray": {
            $objectToArray: "$interestAreas"    //convert to an array of k,v pair, by adding new field
          }
        }
      },
      {
        $match: {
          "interestArray.v": {        //query the v field
            $elemMatch: {
              "interestAreaName": "Cloud",
              "isSelected": true
            }
          }
        }
      },
      {
        $project: {
          "interestArray": 0        //remove the added field from output
        }
      }
    ]);
    

    Playground

    You can write this using either, Aggregation Framework Support or Aggregation Repository Methods

    @Aggregation("<the entire pipeline>")
    List<User> findAllByUserInterestAreasName(String tags);