Mongodb $lookup in Spring data mongo
Joining Two Collections with Spring Data MongoDB
Employee Class
class Employee { private String _id; private String name; private String dept_id;}
Department Class
class Department { private String _id; private String dept_name;}
Employee Result Class
public class EmpDeptResult { private String _id; private String name; private List<Object> departments;}
EmployeeService Class
public class EmployeeService { @Autowired private MongoTemplate mongoTemplate; private Logger LOGGER = LoggerFactory.getLogger(EmployeeService.class); public void lookupOperation(){ LookupOperation lookupOperation = LookupOperation.newLookup() .from("Department") .localField("dept_id") .foreignField("_id") .as("departments"); Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(Criteria.where("_id").is("1")) , lookupOperation); List<EmpDeptResult> results = mongoTemplate.aggregate(aggregation, "Employee", EmpDeptResult.class).getMappedResults(); LOGGER.info("Obj Size " +results.size()); }}
Not every "new" feature makes it immediately into abstraction layers such as spring-mongo.
So instead, all you need do is define a class that uses the AggregationOperation
interface, which will instead take a BSON Object specified directly as it's content:
public class CustomAggregationOperation implements AggregationOperation { private DBObject operation; public CustomAggregationOperation (DBObject operation) { this.operation = operation; } @Override public DBObject toDBObject(AggregationOperationContext context) { return context.getMappedObject(operation); }}
Then you can use in your aggregation like this:
Aggregation aggregation = newAggregation( match( Criteria.where("username").is("user001") ), new CustomAggregationOperation( new BasicDBObject( "$lookup", new BasicDBObject("from", "NewFeedContent") .append("localField","content.contentId") .append("foreignField", "_id") .append("as", "NewFeedContent") ) ))
Which shows the custom class mixed with the built in match()
pipeline helper.
All that happens underneath each helper is that they serialize to a BSON representation such as with DBObject
anyway. So the constructor here just takes the object directly, and returns it directly from .toDBObject()
, which is the standard method on the interface that will be called when serializing the pipline contents.
Here is an example:
Collection posts
{"_id" : ObjectId("5a198074ed31adaf5d79fe8a"),"title" : "Post 1","authors" : [1, 2]},{"_id" : ObjectId("5a198074ed31adaf5d79fe8d"),"title" : "Post 2","authors" : [2]}
Collection users
{"_id" : ObjectId("5a18b483ed31ada08fd6ed82"),"userId" : 1,"name" : "Vinod Kumar"},{"_id" : ObjectId("5a18b483ed31ada08fd6ed83"),"userId" : 2,"name" : "Jim Hazel"},{"_id" : ObjectId("5a18b483ed31ada08fd6ed84"),"userId" : 3,"name" : "Alex Wong"}
Mongodb query with lookup and match
db.users.aggregate([{ $lookup: { from: "users", localField: "userid", foreignField: "authors", as: "post" } }, { $match: { "post": { $ne: [] } } }]).pretty()
Spring Mongoopration syntax
LookupOperation lookupOperation = LookupOperation.newLookup(). from("posts"). localField("userid"). foreignField("authors"). as("post");AggregationOperation match = Aggregation.match(Criteria.where("post").size(1));Aggregation aggregation = Aggregation.newAggregation(lookupOperation, match);List<BasicDBObject> results = mongoOperation.aggregate(aggregation, "users", BasicDBObject.class).getMappedResults();