//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.
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"]}}}