如何使用Spring数据从Mongo中的数组文档字段中仅获取匹配结果

时间:2017-09-08 07:16:50

标签: java spring-boot spring-data spring-data-mongodb

我正在使用spring boot 1.5.1MongoDB version 3.4.6

我有一个mongo文件酒店,里面有一份评论清单。

Review类具有属性userName

@Document
public class Hotel {

    @Id
    private String id;
    private List<Review> reviews;

我想通过评论userName搜索所有酒店。

我的HotelRepositorypublic List<Hotel> findByReviewsUserName(String userName);

当我打电话给用户&#39; Salman&#39; -

List<Hotel> list = this.hotelRepository.findByReviewsUserName(user);

此方法返回如下结果:

[
    {
        "id": "59b23c39c70ff63135f76b14",
        "name": "Signature",
        "reviews": [
            {
                "id": 1,
                "userName": "Salman",
                "rating": 8,
                "approved": true
            },
            {
                "id": 2,
                "userName": "Shahrukh",
                "rating": 5,
                "approved": false
            }
        ]
    }
]

我想要的评论只有&#39; Salman&#39;但它也会回归其他人。

我缺少什么或怎么做?

我注意到,如果单个评论用户匹配,则会返回我不想要的评论列表,我希望我按名称搜索评论。

2 个答案:

答案 0 :(得分:3)

命名查询可以正常工作。您没有明确表示只需要文档的一部分,因此查询将返回整个文档。要实现,您不能使用命名查询 (请参阅@alexefimov使用@Query注释帮助使用命名查询的答案,但您可以使用MongoTemplateMongoRepository旁边。要做到这一点,你必须做出一些改变:

首先,您的存储库应该是这样的:

public interface HotelRepository extends MongoRepository<Hotel, String>, MongoTemplateRepository {
    // You can continue to write your named queries here. Spring will create that.
}

MongoTemplateRepository:

public interface MongoTemplateRepository {
    // You will write your queries which will use mongoTemplate here. 
    List<Hotel> findByReviewsUserName(String userName);
}

为了实现MongoTemplateRepository方法,您将编写一个新类。 重要的事情是,您应该将此类命名为您的存储库类名+ Impl 。否则,spring-data无法找到您的方法实现MongoTemplateRepository中定义的方法。因此,您的实施类名称应为HotelRepositoryImpl

public class HotelRepositoryImpl implements MongoTemplateRepository {

    @Autowired
    private MongoTemplate mongoTemplate; // we will use this to query mongoDb

    @Override
    public List<Hotel> findByReviewsUserName(String userName) {
        Query query = new Query();
        query.addCriteria(Criteria.where("reviews.username").is(userName));
        query.fields().include("reviews.$");
        return mongoTemplate.find(query, Hotel.class);
    }
}

用法:

hotelRepository.findByReviewsUserName("userName");

正如您在代码中看到的那样,我们可以为查询提供.include().exclude个字段。虽然您想要包含数组字段的匹配部分,但我们使用$运算符和数组字段名称。

结论:您仍然可以使用弹性数据支持良好的命名查询,此外,如果您需要聚合或子文档的一些复杂查询,则无法构建命名查询到了春天,您可以在新创建的mongoTemplate存储库类中执行此操作。您可以从HotelRepository访问所有存储库方法。

答案 1 :(得分:3)

@barbakini给出了很好的答案,但也可以在不使用Criteria创建自定义存储库实现的情况下完成,只需“描述”您希望获得哪些字段,0 - .exclude, 1 - .include(

@Query(fields = "{ '_id': 0, 'reviews.$': 1 }")
List<Hotel> findByReviewsUserName(String userName);