Playframework Siena Filtering and Ordering

754 views Asked by At

This is my first question on any of these websites so pardon my unprofessionalism.

I use playframework with SIENA module (with GAE) and I came accross the following problem: Given 3 entities:

public class Meeting extends Model{

    @Id
    public Long id;

    public String place;

    @Owned
    Many<MeetingUser> users;
    .
    .
    .

}

public class User extends Model{

    @Id
    public Long id;

    public String firstName;
    public String lastName;

    @Owned
    Many<MeetingUser> meetings;
    .
    .
    .

}

public class MeetingUser extends Model{

    @Id
    public Long id;

    public Meeting meeting;
    public User user;
    .
    .
    .
    public User getUser(){
        return Model.all(User.class).filter("id", user).get();
    }

    public Meeting getMeeting(){
        return Model.all(Meeting.class).filter("id", meeting).get();
    }

}

For instance I am listing a meeting and all their users:

public static void meetingInfo(Long meetingId){
    Meeting meeting = Models.all(Meeting.class).filter("id",meetingId);
    List<MeetingUser> meetingusers = meeting.asList();
    List<User> users = new ArrayList<User>();
    for(MeetingUser mu: meetingusers){
        users.add(mu.getUser());
    }
    render(users);
}

This is done(is there any better way here?) however when it comes to filtering (especially dynamic filtering for many many fields) I can not use the Query's filter method on the MeetingUser as I need to filter on a MeetingUser's field's field (firstName). The same problem arise for ordering. I need the solution for both problems.

I hope my problem is clear and I appreciate any kind of help here.

1

There are 1 answers

8
mandubian On

Remember that you are in GAE which is a NoSQL DB. So you can't do Join request as in RDBMS. Yet, this is not really the pb you have so this was just to be sure you are aware of it ;)

So if you want to find the person having given firstname in a given meeting, can you try the following:

List<MeetingUser> meetingusers = meeting.users.asQuery().filter("firstname", "XXX"); 

(you can also order)

Nevertheless, knowing that you can't join, remember that you can't write a query searching for a meeting in which there are users whose firstname is XXX as it would require some joins and it doesn't exist in GAE. In this case, you need to change your model following NoSQL philosophy but this is another subject

regards


Let's try to give a way to do what you want...

Your relation is a Many-to-Many which is always the worst case :)

You want to filter Meeting by User's firstname.
It requires a join request which is not possible in GAE. In this case, you must change your model by denormalizing it (sometimes use redundancy also) and manage the join by yourself. Actually, you must do the job of the RDBMS by yourself. It seems overkill but in fact, it's quite easy. The only drawback is that you must perform several requests to the DB. NoSQL means No Schema (& No Join) so there are a few drawbacks but it allows to scale and to manage huge data load... it depends on your needs :)

The choice you did to create the MeetingUser which is a "joined" table and a kind of denormalization is good in GAE because it allows to manage the join yourself.

Solution:

// fetch users by firstname
List<User> users = users.all().filter("firstName", "John").fetch();
// fetch meetingusers associated to these users (verify the "IN" operator works because I didn't use that for a long time and don't remember if it works with this syntax)
List<MeetingUser> meetingusers = MeetingUser.all().filter("user IN", users);
// now you must fetch the whole meeting because in MeetingUser, only the Meeting ID is stored (other fields are Null or O)
List<Meeting> meetings = new ArrayList<Meeting>()
for(MeetingUsers mu:meetingusers) {
   meetings.add(meetingusers.meeting);
}
// use the batch feature to fetch all objects
Meeting.batch(Meeting.class).get(meetings);

// you have your meetings

Hope this helps!