How to retrieve matching element in array in spring mongodb ?
You can try below options. The key is to preserve the structure when mapping the response.
Regular Queries:
Using $positional projection
Query query = new Query();
query.addCriteria(Criteria.where("id").is(new ObjectId("58e8da206ca4f710bab6ef74")).and("courses.id").is(new ObjectId("58d65541495c851c1703c57f")));
query.fields().include("name").position("courses", 1);
List<Course> courses = mongoTemplate.find(query, Course.class);
Using $elemMatch projection
Query query = new Query();
query.addCriteria(Criteria.where("id").is(new ObjectId("58e8da206ca4f710bab6ef74")));
query.fields().include("name").elemMatch("courses", Criteria.where("_id").is(new ObjectId("58d65541495c851c1703c57f") ) );
List<Course> Course = mongoTemplate.find(query, Course.class);
Aggregation
Mongo Version >= 3.4 & Spring 1.5.2 Boot / Spring 1.10.1 Mongo.
You can use $addFields stage which will overwrite the courses
field with the $filter value while keeping all the existing properties. I couldn't find any addFields
builder in current spring version. So I have to use AggregationOperation
to create a new one.
AggregationOperation addFields = new AggregationOperation() {
@Override
public DBObject toDBObject(AggregationOperationContext aggregationOperationContext) {
DBObject dbObject =
new BasicDBObject("courses",
new BasicDBObject("$filter",
new BasicDBObject("input", "$$courses").
append("as", "course").
append("cond",
new BasicDBObject("$eq", Arrays.<Object>asList("$$course._id", new ObjectId("58d65541495c851c1703c57f"))))));
return new BasicDBObject("$addFields", dbObject);
}
};
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("_id").is(new ObjectId("58e8da206ca4f710bab6ef74"))),
addFields
);
Mongo Version = 3.2 & Spring 1.5.2 Boot / Spring 1.10.1 Mongo..
The idea is still same as above but this pipeline uses $project so you'll have to add all the fields that you want to keep in final response. Also used spring helper methods to create the $filter
pipeline.
Aggregation aggregation = newAggregation(
Aggregation.match(Criteria.where("id").is(new ObjectId("58e8da206ca4f710bab6ef74"))),
Aggregation.project("name")
.and(ArrayOperators.Filter.filter("courses").as("course")
.by(ComparisonOperators.Eq.valueOf("course._id").equalToValue(new ObjectId("58d65541495c851c1703c57f")))
).as("courses")
);
Mongo Version <= 2.6
You'll have to use $unwind
and add a course
field to have spring map it correctly.
Admin
Updated on June 14, 2022Comments
-
Admin almost 2 years
Im trying to retrieve a document with a specific '_id' and a single embedded document with another specific '_id'.
my document is represent a catalog and it contains an array of courses.
example data:
'_id': ObjectId('1111'), 'name': 'example catalog', ... ... 'courses': [ { '_id': ObjectId('2222'), 'name': 'my course', ... }, { .... }
In mongod I run this aggregation query, and get back what I wish for:
db.getCollection('catalogs').aggregate( { $match: { '_id': ObjectId('58e8da206ca4f710bab6ef74') } }, { $unwind: '$courses' }, { $match: { 'courses._id': ObjectId('58d65541495c851c1703c57f') } })
As I mentioned earlier, I've get back I single catalog instance with a single course instance within.
In my java repo, I was trying to do the same:
Aggregation aggregation = Aggregation.newAggregation( Aggregation.match(Criteria.where(Catalog.ID_FIELD).is(catalogId)), Aggregation.unwind(Catalog.COURSES_FIELD, true), Aggregation.match(Criteria.where(Catalog.COURSES_FIELD + '.' + Course.ID_FIELD).is(embeddedCourseId)) ); AggregationResults<Catalog> results = mongoTemplate.aggregate(aggregation, Catalog.class, Catalog.class); List<Catalog> catalog = results.getMappedResults();
But unfortunately, I've got an instance of my 'example catalog' with empty array of courses.
While debugging, I've found that inside
results
, there are two props that returns back. first one is what I've used, calledmappedResults
(represents the converted object returning from mongoDB) - contains an empty array of courses. the other one is therawResults
, (represents the data asDBObject
) - contains the specific course I query formy Catalog class contains an ArrayList (if that make any difference).
Please help and let me know what should I do to convert the results properly, or if I did something wrong in my code.