如何在spring mongodb中检索数组中的匹配元素?

我试图检索具有特定“_id”的文档和具有另一个特定“_id”的单个嵌入文档。

我的文档代表一个目录,它包含一系列课程。

示例数据:

'_id': ObjectId('1111'), 'name': 'example catalog', ... ... 'courses': [ { '_id': ObjectId('2222'), 'name': 'my course', ... }, { .... } 

在mongod中我运行这个聚合查询,然后回到我想要的:

 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 results = mongoTemplate.aggregate(aggregation, Catalog.class, Catalog.class); List catalog = results.getMappedResults(); 

但不幸的是,我有一个带有空数组课程的“示例目录”的实例。

在调试时,我发现里面的results ,有两个返回的道具。 第一个是我使用的,名为mappedResults (表示从mongoDB返回的转换对象) – 包含一个空的数组课程。 另一个是rawResults ,(表示数据为DBObject ) – 包含我查询的特定课程

我的Catalog类包含一个ArrayList(如果这有任何区别)。

请帮助并告诉我如何正确转换结果,或者如果我在代码中做错了什么。

您可以尝试以下选项。 关键是在映射响应时保留结构。

常规查询:

使用$位置投影

 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 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 = mongoTemplate.find(query, Course.class); 

聚合

Mongo Version> = 3.4Spring 1.5.2 Boot / Spring 1.10.1 Mongo。

您可以使用$ addFields阶段,该阶段将使用$ filter值覆盖courses字段,同时保留所有现有属性。 我在当前的春季版本中找不到任何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.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.2Spring 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返回的数据需要与您的对象结构匹配。