Reputation: 13
I want to find all unviewed messages for the current user.
A message can be intended for mutiple users to view, so having a boolean to indicate whether a message was viewed will not work. So a message has a list of viewees.
The simplified domain classes are as follows:
class Message {
static hasMany = [ viewees: User ]
String content
}
class User {
String name
}
To find messages that the user has viewed is trivial:
Message.withCriteria {
viewees {
idEq(currentUser.id)
}
}
So to find messages the user hasn't viewed i thought i could just add a not{ }
like so:
Message.withCriteria {
viewees {
not {
idEq(currentUser.id)
}
}
}
But this will return all messages that has a viewee who isn't the current user. Which means it will return messages where another user is in the viewee list, regardless if the current user is in the viewee list.
And besides that, return multiple instances of the same message if there are multiple viewees.
The criteria builder also has inList
:
not {
inList( propertyName, list )
}
But i need the opposite of this. Something like:
not {
inList( currentUser.id, 'viewees' )
}
But that isn't supported.
Is there anything like this that i could use? Or is there a different way to go about this? Perhaps changing the domain model in some way?
Upvotes: 1
Views: 1140
Reputation: 1733
I would suggest to explicitly model the read/unread information by creating an extra domain class (something like this):
class UserMessage {
enum State = {READ, UNREAD}
State state = State.UNREAD
Message message
User user
}
If a User
receives a new message it is added to UserMessages
as unread and when the User
reads the message you change the state to READ
. You can now easily check the state of the messages received by a single user.
This has the advantage that you don't add user specific information to the Message
. I don't consider tracking of who has seen the message as a responsibility of the Message (of course it depends on the meaning of Message
in your business domain).
This extra class is also useful if you have more user specific information that is related to the message, for example a deleted flag (don't show it again for this user) or a starred flag (I want to keep it).
Upvotes: 0
Reputation: 5538
You can use HQL to get what you are looking for. A simple query like this:
class UserDomain {
String name
static List<MyMessage> findUnseenMessagesForUser(UserDomain user) {
MyMessage.executeQuery(
'select m from MyMessage m where :user not in elements(m.views)',
[user: user]
)
}
}
/
void "test something"() {
setup:
def m1 = new MyMessage(content: "message 1")
def m2 = new MyMessage(content: "message 2")
def m3 = new MyMessage(content: "message 3").save(flush: true)
def u1 = new UserDomain(name: "user1").save(flush: true)
def u2 = new UserDomain(name: "user2").save(flush: true)
def u3 = new UserDomain(name: "user3").save(flush: true)
def u4 = new UserDomain(name: "user4").save(flush: true)
when:
m1.addToViews u1
m2.addToViews u1
m2.addToViews u3
m2.addToViews u4
m1.save(flush: true)
m2.save(flush: true)
then:
UserDomain.findUnseenMessagesForUser (u2). containsAll ([m1, m2, m3])
UserDomain.findUnseenMessagesForUser(u1).containsAll([m3])
}
Here is an example and test case
Upvotes: 1