Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use projecting types in Spring Data MongoDB repository's query methods?

I have been playing around with Spring Data and MongoDB and have a question about limiting the amount of data for certain queries. I've seen adding custom queries within the MongoRepository, however I haven't seen any examples of limiting the amount of data and returning classes that are basically a subset of larger classes.

For instance I have a User class that has several fields, but I also want to create a UserShort class that has a subset of the fields in the User class. For instance UserShort would only contain the id and firstName / lastName / email fields, rather than everything.

I've seen I can specify/limit the fields that are returned, but can I have those returned into a different class? At the moment the UserShort will return null unless I specify the User class instead, but the fields will be limited to the ones I specify. Wasn't sure if this is possible at all? I realize the User class below isn't huge, but it's the concept I'm after.

A user interface:

public interface Users {}

Subset class:

public class UserShort implements Users {

    @Id
    private String id;

    private String firstName;
    private String lastName;

    @Indexed(unique = true)
    private String email;

    //getters / setters
}

Full class:

@Document
public class User implements Users {

    @Id
    private String id;

    private String firstName;
    private String lastName;
    private String username;
    private String password;
    private Date dob;
    private Status status;

    @Indexed(unique = true)
    private String email;

    private Gender gender;
    private String locale;
    private Date registerDate;

    @DBRef
    private List<UserAddress> addresses;

    public User(){
        addresses = new ArrayList<UserAddress>();
    }

    //getters / setters
}

And the repository interface:

public interface UserRepository extends MongoRepository<Users, String> {

    public User findByEmail(String email);

    @Query(value="{ 'email' : ?0 }", fields="{ 'firstName' : 1, 'lastName' : 1}")
    public UserShort findUserShortByEmail(String email);

}
like image 399
byte-crunch Avatar asked Aug 19 '14 21:08

byte-crunch


1 Answers

As long as the return type of the query method is assignable to the managed domain type (Users in your case) we will prefer the return type to determine the collection to run the query against. Thus, in your case we'd execute the query against userShort instead of users which is why you do not get any results. That behavior is in place to support storing inheritance hierarchies into different collections.

If you switched to User as the domain type for the repository, things should work exactly as expected. This would also have the benefit of preventing clients from handing UserShort instances to the save(…) method which will wipe out properties contained in User but not in UserShort. Here's the final repository interface declaration.

interface UserRepository extends MongoRepository<User, String> {

    User findByEmail(String email);

    @Query(value="{ 'email' : ?0 }", fields="{ 'firstName' : 1, 'lastName' : 1}")
    UserShort findUserShortByEmail(String email);
}

P.S.: @byte-crunch outlined in the comments that this currently only works for collections of projections but not for returning single instances. This has been reported and fixed in DATAMONGO-1030 and will be available in 1.5.4 and 1.6.0 GA.

like image 126
Oliver Drotbohm Avatar answered Oct 23 '22 05:10

Oliver Drotbohm