我试图检索具有特定“_id”的文档和具有另一个特定“_id”的单个嵌入文档。
我的文档代表一个目录,它包含一系列课程。
示例数据:
'_id': ObjectId('1111'),
'name': 'example catalog',
...
...
'courses': [
{
'_id': ObjectId('2222'),
'name': 'my course',
...
},
{
....
}
在mongodb中,我运行此聚合查询,并获取我想要的内容:
db.getCollection('catalogs').aggregate(
{ $match: { '_id': ObjectId('58e8da206ca4f710bab6ef74') } },
{ $unwind: '$courses' },
{ $match: { 'courses._id': ObjectId('58d65541495c851c1703c57f') } })
正如我之前提到的,我已经找回了我的单个目录实例,其中包含一个课程实例。
在我的java回购中,我试图做同样的事情:
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();
但不幸的是,我有一个带有空数组课程的“示例目录”实例。
在调试时,我发现在results
中,有两个返回的道具。第一个是我使用的,称为mappedResults
(表示从mongoDB返回的转换对象) - 包含一个空的数组课程。另一个是rawResults
,(表示数据为DBObject
) - 包含我查询的具体课程
我的Catalog类包含一个ArrayList(如果这有任何区别)。
请帮助,让我知道我应该怎样做才能正确转换结果,或者我在代码中做错了什么。
您可以尝试以下选项。关键是在映射响应时保留结构。
常规查询:
使用$positional投影
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);
使用$elemMatch投影
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);
聚合
Mongo Version> = 3.4&Spring 1.5.2 Boot / Spring 1.10.1 Mongo。
您可以使用$addFields阶段,该阶段将使用courses
值覆盖$filter字段,同时保留所有现有属性。在当前的春季版本中,我找不到任何addFields
构建器。所以我必须使用AggregationOperation
创建一个新的。
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 ..
这个想法仍然与上面相同,但是这个管道使用$project,所以你必须添加你想要保留在最终响应中的所有字段。还使用spring helper方法创建$filter
管道。
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版本<= 2.6
您将不得不使用$unwind
并添加一个course
字段以使弹簧映射正确。
你在这里遇到的问题是你的Catalog
类有一个courses
字段,它映射到List/ArrayList
。但是当您的聚合查询展开courses数组时,它将输出courses
字段作为子文档。 Spring映射器不知道如何处理它,因为它与你的Catalog
对象结构不匹配。
你还没有在这里完全定义你的问题,但更可能的是,如果你让聚合返回一个Course
对象而不是Catalog
对象。为此,您需要向聚合管道添加投影阶段,以使结果看起来与单个Course对象完全相同。关键是从MongoDB返回的数据需要与您的对象结构匹配。