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.
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 MongoTemplate
beside 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 .exclude
fields 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
.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With