问题
I'm working on Spring Boot v.2.1.3.RELEASE and Spring Data MongoDB. Due to critical requirement, I had to model like below assuming Employee knows multiple technologies, but primary language would be anyone.
So, I decided to keep technology collection separate and somehow related Employee and technology in the Employee Collection.
{
"_id" : ObjectId("5ec65750fdcd4e960f4b2f24"),
"technologyCd" : "AB",
"technologyName" : "My ABC",
"ltechnologyNativeName" : "XY",
"status" : "A"
}
So, I've done association like below -
Note: Multiple Technologies can be associated with one Employee
One Employee can be associated with multiple technologies
Employee can have one and only one Primary Technology
{
"_id" : ObjectId("5ec507c72d8c2136245d35ce"),
"firstName" : "John",
"lastName" : "Doe",
"email" : "j.d@gmail.com",
.......
.......
.......
"employeeTechnologyRefs" : [
{
"technologyCd" : "AB",
"primaryTechnologySw" : "Y",
"Active" : "A"
},
{
"technologyCd" : "AB",
"primaryTechnologySw" : "N",
"Active" : "A"
},
{
"technologyCd" : "PR",
"primaryTechnologySw" : "N",
"Active" : "A"
},
{
"technologyCd" : "PR",
"primaryTechnologySw" : "N",
"Active" : "A"
}
],
"countryPhoneCodes" : [
"+352"
],
....
...
}
I used below query, how to query over the Technology documents to get the result and map it and create final object?
Criteria criteria = new Criteria();
criteria.andOperator(
StringUtils.isNotBlank(firstName) ? Criteria.where("firstName").is(firstName.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(lastName) ? Criteria.where("lastName").is(lastName.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(email) ? Criteria.where("email").is(email.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(technologyCd) ? Criteria.where("employeeTechnologyRefs.technologyCd").is(technologyCd.toUpperCase())
: Criteria.where(""));
MatchOperation matchStage = Aggregation.match(criteria);
GroupOperation groupOp = Aggregation
.group("firstName", "lastName", "email","_id")
.addToSet("employeeTechnologyRefs").as("employeeTechnologyRefs");
ProjectionOperation projectStage = Aggregation.project("employeeTechnologyRefs");
Aggregation aggregation = Aggregation.newAggregation(matchStage, groupOp, projectStage);
AggregationResults<CustomObject> results = mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(Employee.class), CustomObject.class);
System.out.println(results);
Result should look like below
{
"_id" : ObjectId("5ec507c72d8c2136245d35ce"),
"firstName" : 442,
"lastName" : "LU",
"email" : "LUX",
.......
.......
.......
"employeeTechnologyRefs" : [
{
"technologyCd" : "AB",
"technologyName" : "My ABC",
"ltechnologyNativeName" : "XY",
"primaryTechnologySw" : "Y",
"Active" : "A"
},
{
"technologyCd" : "AB",
"technologyCd" : "AB",
"technologyName" : "My ABC",
"ltechnologyNativeName" : "XY",
"primaryTechnologySw" : "Y",
"Active" : "A"
},
{
"technologyCd" : "PR",
"technologyCd" : "AB",
"technologyName" : "My ABC",
"ltechnologyNativeName" : "XY",
"primaryTechnologySw" : "Y",
"Active" : "A"
},
{
"technologyCd" : "PR",
"technologyCd" : "AB",
"technologyName" : "My ABC",
"ltechnologyNativeName" : "XY",
"primaryTechnologySw" : "Y",
"Active" : "A"
}
],
....
}
回答1:
If you use below lookup operation in your code, you should able to get the answer as expected, and you don't need group operation in your code.
Edited Answer: This is how whole code should look like. One more thing, you don't need projection and if you need try projecting specific field only, and as part of the lookup operation do not use the same field as it is in your collection, otherwise it will override existing data from employee collection.
Criteria criteria = new Criteria();
criteria.andOperator(
StringUtils.isNotBlank(firstName) ? Criteria.where("firstName").is(firstName.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(lastName) ? Criteria.where("lastName").is(lastName.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(email) ? Criteria.where("email").is(email.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(technologyCd) ? Criteria.where("employeeTechnologyRefs.technologyCd").is(technologyCd.toUpperCase())
: Criteria.where(""));
MatchOperation matchStage = Aggregation.match(criteria);
/*GroupOperation groupOp = Aggregation
.group("firstName", "lastName", "email","_id")
.addToSet("employeeTechnologyRefs").as("employeeTechnologyRefs");
*/
LookupOperation lookupOperation = LookupOperation.newLookup().
from("technology_collection_name").
localField("employeeTechnologyRefs").
foreignField("technologyCd").
as("employeeTechnologyRefsOtherName");
/* ProjectionOperation projectStage = Aggregation.project("employeeTechnologyRefs");
*/
// And if you want to project specific field from employee array you can use something like.
ProjectionOperation projectStage = Aggregation.project("employeeTechnologyRefs.fieldName")
Aggregation aggregation = Aggregation.newAggregation(matchStage, lookupOperation, projectStage);
AggregationResults<CustomObject> results = mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(Employee.class), CustomObject.class);
System.out.println(results);
来源:https://stackoverflow.com/questions/61953464/spring-data-mongodb-aggregation-with-other-collection-as-well