【问题标题】:Grails criteria: id not in hasMany listGrails 标准:id 不在 hasMany 列表中
【发布时间】:2014-10-14 09:52:42
【问题描述】:

我想查找当前用户的所有未查看消息。

一条消息可以供多个用户查看,因此使用布尔值来指示是否查看了一条消息是行不通的。所以一条消息有一个查看者列表。

简化的领域类如下:

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

    class User {
        String name
    }

查找用户查看过的消息很简单:

Message.withCriteria {
    viewees {
       idEq(currentUser.id)
    }
}

所以要查找用户尚未查看的消息,我想我可以像这样添加not{ }

Message.withCriteria {
    viewees {
        not {
            idEq(currentUser.id)
        }
    }
}

但这将返回所有查看者不是当前用户的消息。这意味着它将返回另一个用户在被查看者列表中的消息,而不管当前用户是否在被查看者列表中。

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

标准构建器还有inList:

not { 
    inList( propertyName, list ) 
}

但我需要与此相反。类似的东西:

not { 
    inList( currentUser.id, 'viewees' ) 
}

但这不受支持。

有什么我可以使用的类似的东西吗?还是有其他方法可以解决这个问题?也许以某种方式改变领域模型?

【问题讨论】:

  • 一种性能不太好的查询方法是Message.all.findAll { !( currentUser.id in it.viewees*.id ) }。这是执行查询的最坏情况。

标签: hibernate grails grails-orm criteria hibernate-criteria


【解决方案1】:

我建议通过创建一个额外的域类(类似这样)来显式建模已读/未读信息:

class UserMessage {
    enum State = {READ, UNREAD}

    State state = State.UNREAD

    Message message
    User user
}

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

这样做的好处是您无需将用户特定信息添加到Message。我不考虑将跟踪谁看过该消息作为消息的责任(当然这取决于您的业务领域中Message 的含义)。

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

【讨论】:

  • 我同意您的观点,并意识到我需要更多有关已加星标、已点赞、已添加书签等消息的信息。谢谢!
【解决方案2】:

您可以使用 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]
        )
    }
}

/

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

【讨论】:

  • 工作完美!谢谢你。我一直能够使用 Criteria Builder 解决我的查询,所以我对 HQL 不是很熟悉。但是,在阅读了@Martin Hauner 的答案后,我意识到这更适合我的业务领域。
猜你喜欢
  • 2012-09-26
  • 1970-01-01
  • 2012-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多