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

How to Get Only Matched Result from An Array Field of Document in Mongo Using Spring Data

我正在使用 spring boot 1.5.1MongoDB version 3.4.6 .

我有一份 mongo 文档酒店,其中包含评论列表。

Review class 有 属性 userName.

@Document
public class Hotel {

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

我想通过评论用户名搜索所有酒店。

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

当我与用户通话时 'Salman' -

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

此方法 returns 结果如下:

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

我只想要 'Salman' 的评论,但它也会为其他人返回。

我缺少什么或如何做?

我注意到,如果一个评论用户匹配它 returns 我不想要的整个评论列表,我想要我按名称搜索过的评论。

命名查询正常工作。您没有明确表示您只需要文档的一部分,因此查询 returns 整个文档。要实现这一点,你不能使用命名查询 (参见@alexefimov answer for using named queries with help of @Query annotation) 但你可以在 MongoRepository 旁边使用 MongoTemplate。为此,您必须进行一些更改:

首先你的仓库应该是这样的:

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 方法,您将编写一个新的class。 这里重要的 是您应该将此命名为 class 您的存储库 class 名称 + Impl。否则 spring-data 无法找到您在 MongoTemplateRepository 中定义的方法实现的位置。所以你的实现 class 的名字应该是 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 字段进行查询。当您只想包含数组字段的匹配部分时,我们使用 $ 运算符和数组字段名称。

结论:你仍然可以使用spring-数据支持的命名查询,另外如果你需要聚合或一些spring 无法构建命名查询的子文档的复杂查询,您可以在新创建的 mongoTemplate 存储库 class 中完成。您可以从 HotelRepository.

访问所有存储库方法

来自@barbakini 的好答案,但也可以在不使用 Criteria 创建自定义存储库实现的情况下完成,只需 'describe' 您想要获得的字段,其中 0 - .exclude, 1 - .include(

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