Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 927
Mehraj Malik Avatar asked Sep 08 '17 07:09

Mehraj Malik


2 Answers

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.

like image 89
barbakini Avatar answered Sep 23 '22 20:09

barbakini


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);
like image 35
Oleksandr Yefymov Avatar answered Sep 26 '22 20:09

Oleksandr Yefymov