Hibernate Grails条件:id不在hasMany列表中

Hibernate Grails条件:id不在hasMany列表中,hibernate,grails,gorm,criteria,hibernate-criteria,Hibernate,Grails,Gorm,Criteria,Hibernate Criteria,我要查找当前用户的所有未查看邮件 消息可供多个用户查看,因此使用布尔值指示消息是否已被查看是行不通的。因此,邮件中有一个被查看者列表 简化的域类如下所示: class Message { static hasMany = [ viewees: User ] String content } class User { String name } 查找用户已查看的邮件非常简单: Message.withCriteri

我要查找当前用户的所有未查看邮件

消息可供多个用户查看,因此使用布尔值指示消息是否已被查看是行不通的。因此,邮件中有一个被查看者列表

简化的域类如下所示:

   class Message {
        static hasMany = [ viewees: User ]
        String content 
    }

    class User {
        String name
    }
查找用户已查看的邮件非常简单:

Message.withCriteria {
    viewees {
       idEq(currentUser.id)
    }
}
因此,要查找用户未查看的消息,我想我可以添加一个
而不是{}
,如下所示:

Message.withCriteria {
    viewees {
        not {
            idEq(currentUser.id)
        }
    }
}
但这将返回所有具有非当前用户的查看者的消息。这意味着它将返回另一个用户在查看列表中的消息,而不管当前用户是否在查看列表中

此外,如果有多个查看者,则返回同一消息的多个实例

标准生成器在列表中也有

not { 
    inList( propertyName, list ) 
}
但我需要的是相反的结果。比如:

not { 
    inList( currentUser.id, 'viewees' ) 
}
但这并不受支持


有这样的东西我可以用吗?还是有别的办法?也许会以某种方式更改域模型?

您可以使用HQL来获得所需的内容。下面是一个简单的查询:

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]
        )
    }
}

我建议通过创建一个额外的域类(如下所示)来显式地建模已读/未读信息:

如果
用户
收到一条新消息,它将以未读状态添加到
用户消息
,当
用户
读取消息时,您将状态更改为
已读
。现在,您可以轻松地检查单个用户接收的消息的状态

这样做的好处是,您不需要将特定于用户的信息添加到
消息中。我不认为跟踪谁将消息视为消息的责任(当然它取决于您的业务域中的<代码>消息< /代码>的含义)。p>

如果您有更多与消息相关的特定于用户的信息,例如删除的标志(不再为该用户显示)或带星号的标志(我想保留它),那么这个额外的类也很有用。

查询性能不太好的一种方法是
message.all.findAll{!(其中的currentUser.id.viewees*.id)}
。这是执行查询的最坏情况。工作完美!非常感谢。我总是能够用Criteria Builder解决我的查询,所以我对HQL不是很熟悉。然而,在阅读了@Martin Hauner的答案后,我意识到这更适合我的业务领域。我同意你的观点,并意识到我需要更多关于这条消息的信息,如星号、喜欢、书签。。谢谢
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])
    }
class UserMessage {
    enum State = {READ, UNREAD}

    State state = State.UNREAD

    Message message
    User user
}