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

此生再无相见时 提交于 2019-12-18 05:10:23

问题


I am using spring boot 1.5.1 and MongoDB version 3.4.6 .

I have a mongo document Hotel which has a list of Review .

The Review class has property userName.

@Document
public class Hotel {

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

I want to search all the hotel by Review userName.

My HotelRepository has public List<Hotel> findByReviewsUserName(String userName);

When I am calling with user 'Salman' -

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

this method returns result like below :

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

What I want the reviews only of 'Salman' but it's also returning for others also.

What am I missing or how to do it?

What I have noticed is that if a single review user matched it returns the whole list of reviews which I don't want, I want reviews I have searched by name.


回答1:


The named query works as it should be. You are not explicitly saying that you want only a portion of document so query returns whole document. To achieve that you cannot use named queries (see @alexefimov answer for using named queries with help of @Query annotation) but you can use MongoTemplatebeside of MongoRepository. to do that you have to make some changes:

First your repository should be like this:

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);
}

For implementation of MongoTemplateRepository methods, you will write a new class. The important thing here is that you should named this class your repository class name + Impl. Otherwise spring-data cannot find where your methods implementation those defined in MongoTemplateRepository. So your implementation class's name should be 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);
    }
}

Usage:

hotelRepository.findByReviewsUserName("userName");

As you can see in codes we can .include() or .excludefields for the query. While you want to include just matched part of an array field, we use $operator with array field name.

Conclusion: You can still use spring-data well supported named queries and additionally if you need aggregation or some complex queries for sub documents that a named query cannot be built by spring, you can do it in your newly created mongoTemplate repository class. And you can access all of your repository methods from HotelRepository.




回答2:


Great answer from @barbakini, but that also can be done without creating custom repository implementation with Criteria, just 'describe' which fields you would like to get, where 0 - .exclude, 1 - .include(

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


来源:https://stackoverflow.com/questions/46110743/how-to-get-only-matched-result-from-an-array-field-of-document-in-mongo-using-sp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!