user2171214
user2171214

Reputation: 11

Grails get latest in a child collection - sort not working?

Head scratcher here, not sure if its the code or the test.

We have a requirement to keep track of status changes for an object and the history of those changes. There are times when only the latest status should be displayed and others where the entire history list is needed.

Originally we figured 'piece of cake' follow the documentation (grails.org/doc/2.2.x/ref/Database%20Mapping/sort.html ) and what this guy found (stackoverflow.com/questions/12482995/sort-a-collection-in-grails-by-date ) and were golden.

Come to unit test time and we created the test to make two statuses and pull the latest. Bingo it works. Then, trouble; at random intervals, the test fails. So we seem to be finding what this guy found about sort not working. Maybe I'm missing something obvious and hopefully a fresh pair of eyes can see it.

class mainObject {
    static hasMany = [ statusHistory : StatusHistory]

    static mapping = {
           sort id: 'asc'
           statusHistory sort:"statusDate"
    }


 String getCurrentStatus(){
     if (!this.statusHistory){
         return""
     }else{
         this.statusHistory.sort{it.sstatusDate}
         return statusHistory.status.first()
     }
 }
}


class statusHistory {

     static mapping = {
        sort statusDate: "asc"
     }

 static belongsTo = [ mainObjects : MainObject]

 Date statusDate
 String strStatus
 String notes

 String toString(){
    if (statusDate ==null){
        return "${strStatus}" + " - No Date"
    }else{
        return "${strStatus}" +" - "+ "${statusDate.getDateString()}"
    }
 }
}

Unit Test

@TestMixin(GrailsUnitTestMixin)
class MainObjectTests {

    def util = new UnitTestUtil()
    def mockMainObj

    @Before
    void setUp {

          mockMainObj = util.initMockMainObj()
          mockForConstraintsTests(MainObject, [mockMainObj])
     }

    void testgetCurrentStatus(){
        assertEquals("", mockMainObj.getCurrentStatus())

        def mockObjStatus1 = util.initMockStatus(mockMainObj, new SimpleDateFormat(dd/MM/yyyy hh:mm:ss).parse("01/12/2008 15:00:00"), "First Status")
        mockDomain (StatusHistory, [mockObjStatus1])
        mockForConstaintsTests (StatusHistory, [mockObjStatus1])
        mockObjStatus1.save()
        assertEquals(1, mockMainObj.statusHistory.size())
        assertEquals("First Status", mockMainObj.getCurrentStatus())

        def mockObjStatus2 = util.initMockStatus(mockMainObj, new Date(), "Latest Status")
        mockDomain (StatusHistory, [mockObjStatus2])
        mockForConstaintsTests (StatusHistory, [mockObjStatus2])
        mockObjStatus2.save()
        assertEquals(2, mockMainObj.statusHistory.size())
        assertEquals("Latest Status", mockMainObj.getCurrentStatus())

    }
 }

I'm a little disturbed that there seems to be an issue with GORM, Many-To-One/Many, and performance/scaling of searches according to this blog and Mr. Beckwith (www.infoq.com/presentations/GORM-Performance)

But I keep going back to the documentation and looking at my code and thinking that its right. So I'm in a circular reference now. Any help would be greatly appreciated.

Upvotes: 0

Views: 186

Answers (1)

user2171214
user2171214

Reputation: 11

From a Co-worker - There may be several solutions to this, but this would be my approach. Do not use the hasMany collection to get the first value because you cannot be certain of the order (there may be a bug with this in Grails). Instead use a single query to obtain the value. Try replace the getCurrentStatus() method with this:

String getCurrentStatus() {
            StatusHistory.createCriteria().get() {
                            eq(‘mainObjects’, this)

                            maxResults(1)
                            order(‘statusDate, ‘asc’)

                            projections {
                                            property(‘strStatus’)
                            }
            }
}

This will obtain ONLY the earliest strStatus for the current MainObject from the StatusHistory. It is better performing than retrieving ALL of the history and only then selecting the first one.

This appears to have solved the problem

Upvotes: 1

Related Questions