Search code examples
mongodbaggregation-frameworkspring-data-mongodb

MongoDB Spring Data gives No property '$$value' found on class exception


//Sample Collection
{
  //fields
  "roleList" : [ROLE_1, ROLE_2, ROLE_3],
  "siteList" : [
  {
      "role" : ROLE_1,
      //fields
  },
  {
      "role" : ROLE_2,
      //fields
  },
  ]
}

//Expected Output
{
  //fields
  "roleDiff":[ROLE_3] // roleList subtracted by set of roles present in siteList
}
//Script that works with Studio 3t
db.getCollection("SAMPLE_COLLECTION").aggregate(

  // Pipeline
  [
      // Stage 1
      {
          $project: {
              "roleList":1,
              "siteRoles":{$ifNull: [{$reduce:{
                  input:"$siteList",
                  initialValue:[],
                  in:{$setUnion:["$$value", {
          "$split": ["$$this.role", " "]}]}
              }
              }, []]}
          }
      },

      // Stage 2
      {
          $project: {
              "roleDiff":{ $setDifference:["$roleList", "$siteRoles"]
              }
          }
      },
  ],

  // Options
  {

  }

  // Created with Studio 3T, the IDE for MongoDB - https://studio3t.com/

);
ArrayOperators.Reduce reduce = ArrayOperators.Reduce.arrayOf("siteList").withInitialValue(Collections.EMPTY_SET)
            .reduce(SetOperators.SetUnion.arrayAsSet(StringOperators.valueOf("$$this.role").split(" ")).union(ArrayOperators.Reduce.Variable.VALUE.getTarget()));
ProjectionOperation projectionOperationOne = Aggregation.project().andInclude(/*Some fields,*/ "roleList").and(ConditionalOperators.ifNull(reduce).then(Collections.EMPTY_LIST)).as("siteRoles");    
ProjectionOperation projectionOperationTwo = Aggregation.project().andInclude(/*Some fields*/).and(SetOperators.SetDifference.arrayAsSet("roleList").differenceTo("siteRoles")).as("roleDiff");
Aggregation aggregation = Aggregation.newAggregation(projectionOperationOne, projectionOperationTwo);
AggregationResults<SiteDiff> siteDiff = mongoTemplate.aggregate(aggregation, SampleCollection.class, SiteDiff.class);

The Java code above thows exception org.springframework.data.mapping.context.InvalidPersistentPropertyPath: No property '$$value' found on class Did you mean: ? The query works fine with Studio3T. My intention here is to get the difference between 2 String arrays "roleList" and "siteRoles" along with some other fields in the record. "siteRoles" has to be derived from "siteList" which is an array of object. Since $addToSet works only with $group operation, I'm finding it difficult to extract the role from "siteList". I used reduce here combining it with setUnion. There problem was role is a string and I had to convert it to array. Only way I could find was to use $split and use " " as delimiter as I'm sure role will not have space. Finally, the script worked in studio3t but the java version is not getting executed.


Solution

  • If you refer to a field in an embedded array, it should give you the entire array of those values.

    The aggregation should only need 1 stage:

    {$project: {roleDiff: { $setDifference: ["$roleList","$siteList.role"]}}}