If I understood your goal correctly you as a result need the following:
- a collection of
Group objects that have to be inserted into the new DB and a collection of Group objects that have to be removed from the new DB;
- the same for the
Person objects;
- the same for the
Qualification objects;
My approach is to generate two maps Map<Group, Set<Person>> for old and new DB. And then use them to get unions and differences (in terms of the set theory) for groups, people and qualifications.
I assume that the equality of Group and Person objects is based solely on their id.
Note: multi-line functions (especially those that you see in getQualifByPersToRemove() and getQualifByPersToAdd()) has to be avoided and normally extracted into a separate method.
public static void main(String[] args) {
List<DatabaseResponse> oldResp = getResp() // fetching responces
List<DatabaseResponse> newResp = getResp() // fetching responces
Map<Group, Set<Person>> persByGroupIdOld = getPersByGroupId(oldResp);
Map<Group, Set<Person>> persByGroupIdNew = getPersByGroupId(newResp);
List<Group> groupsToRemove = getGroupsToRemove(persByGroupIdOld, persByGroupIdNew);
List<Group> groupsToAdd = getGroupsToAdd(persByGroupIdOld, persByGroupIdNew);
Set<Person> persToRemove = getPersToRemove(persByGroupIdOld, persByGroupIdNew);
Set<Person> persToAdd = getPersToAdd(persByGroupIdOld, persByGroupIdNew);
Map<Person, Set<Qualification>> qualifByPersToRemove = getQualifByPersToRemove(persByGroupIdOld, persByGroupIdNew);
Map<Person, Set<Qualification>> qualifByPersToAdd = getQualifByPersToAdd(persByGroupIdOld, persByGroupIdNew);
}
public static Map<Group, Set<Person>> getPersByGroupId(List<DatabaseResponse> responses) {
return responses.stream()
.flatMap(resp -> resp.getGroups().stream())
.collect(Collectors.groupingBy(Function.identity(),
Collectors.flatMapping(group -> group.getPeople().stream(),
Collectors.toSet())));
}
public static List<Group> getGroupsToRemove(Map<Group, Set<Person>> persByGroupIdOld,
Map<Group, Set<Person>> persByGroupIdNew) {
return persByGroupIdNew.keySet().stream()
.filter(group -> !persByGroupIdOld.containsKey(group))
.collect(Collectors.toList());
}
public static List<Group> getGroupsToAdd(Map<Group, Set<Person>> persByGroupIdOld,
Map<Group, Set<Person>> persByGroupIdNew) {
return persByGroupIdOld.keySet().stream()
.filter(group -> !persByGroupIdNew.containsKey(group))
.collect(Collectors.toList());
}
public static Set<Person> getPersToRemove(Map<Group, Set<Person>> persByGroupIdOld,
Map<Group, Set<Person>> persByGroupIdNew) {
return persByGroupIdNew.keySet().stream()
.filter(persByGroupIdOld::containsKey) // exist in both DB
.flatMap(group -> {
List<Person> persDiff = new ArrayList<>(persByGroupIdNew.get(group));
persDiff.removeAll(persByGroupIdOld.get(group));
return persDiff.stream();
})
.collect(Collectors.toSet()); // set is used because person could potentially belong to many groups
}
public static Set<Person> getPersToAdd(Map<Group, Set<Person>> persByGroupIdOld,
Map<Group, Set<Person>> persByGroupIdNew) {
return persByGroupIdOld.keySet().stream()
.filter(persByGroupIdOld::containsKey) // exist in both DB
.flatMap(group -> {
List<Person> persDiff = new ArrayList<>(persByGroupIdOld.get(group));
persDiff.removeAll(persByGroupIdNew.get(group));
return persDiff.stream(); // assumption that equality of the Person class is based on the personId only
})
.collect(Collectors.toSet()); // set is used because person could potentially belong to many groups
}
public static Map<Person, Set<Qualification>> getQualifByPersToRemove(Map<Group, Set<Person>> persByGroupIdOld,
Map<Group, Set<Person>> persByGroupIdNew) {
return persByGroupIdOld.keySet().stream()
.filter(persByGroupIdNew::containsKey) // exist in both DB
.flatMap(group -> {
Map<Person, Set<Qualification>> qualifByCommonPersOld = getQualifByPers(persByGroupIdOld.get(group));
Map<Person, Set<Qualification>> qualifByCommonPersNew = getQualifByPers(persByGroupIdNew.get(group));
return qualifByCommonPersNew.entrySet().stream()
.filter(entry -> qualifByCommonPersOld.containsKey(entry.getKey())) // intersection of people
.filter(entry -> !entry.getValue()
.equals(qualifByCommonPersOld.get(entry.getKey()))) // qualifications sets for a particular person don't match
.map(entry -> { // qualification that aren't present in the old DB
Set<Qualification> newQualif = new HashSet<>(entry.getValue());
newQualif.removeAll(qualifByCommonPersOld.get(entry.getKey()));
return Map.entry(entry.getKey(), newQualif); // Person object and qualifications are present only in new DB
});
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
public static Map<Person, Set<Qualification>> getQualifByPersToAdd(Map<Group, Set<Person>> persByGroupIdOld,
Map<Group, Set<Person>> persByGroupIdNew) {
return persByGroupIdOld.keySet().stream()
.filter(persByGroupIdNew::containsKey) // exist in both DB
.flatMap(group -> {
Map<Person, Set<Qualification>> qualifByCommonPersOld = getQualifByPers(persByGroupIdOld.get(group));
Map<Person, Set<Qualification>> qualifByCommonPersNew = getQualifByPers(persByGroupIdNew.get(group));
return qualifByCommonPersOld.entrySet().stream()
.filter(entry -> qualifByCommonPersNew.containsKey(entry.getKey())) // intersection of people
.filter(entry -> !entry.getValue()
.equals(qualifByCommonPersNew.get(entry.getKey()))) // qualifications sets for a particular person don't match
.map(entry -> {
Set<Qualification> oldQualif = new HashSet<>(entry.getValue());
oldQualif.removeAll(qualifByCommonPersNew.get(entry.getKey()));
return Map.entry(entry.getKey(), oldQualif); // Person object and qualifications are not present or differ
});
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
public static Map<Person, Set<Qualification>> getQualifByPers(Set<Person> people) {
return people.stream()
.collect(Collectors.groupingBy(Function.identity(),
Collectors.flatMapping(pers -> pers.getQualifications().stream(), Collectors.toSet())));
}