문제

I am writing a scheduling feature in a new Grails 2.1.0 application. I am transitioning from a Ruby on Rails project, so much of my query strategy derives from Rails style. I have the following domain classes:

Schedule.groovy

class Schedule {

    // Number of minutes between available appointment slots
    int defaultAppointmentInterval
    Time officeOpenStart
    Time officeOpenEnd
    Time lunchStart
    Time lunchEnd

    static hasMany = [inventorySlots: InventorySlot]

    static constraints = {
       // long validation rules
    }

    def boolean isAvailableAt(Date dateTime) {
        def isAvailable = true
        if (inventorySlots.isEmpty()) {
            isAvailable = false
        } else if (inventorySlotsSurroundingTime(dateTime).isEmpty()) {
            isAvailable = false
        }
        isAvailable
    }

    def inventorySlotsSurroundingTime(Date dateTime) {
        InventorySlot.surroundingTime(dateTime) {
            and {
                inventorySlot.schedule = this
            }
        }
    }
}

InventorySlot.groovy

class InventorySlot {

    Date startTime

    static belongsTo = [schedule: Schedule]

    static constraints = {
        startTime nullable: false, blank: false
    }

    static mapping = {
        tablePerHierarchy true
        schedule lazy: false
    }

    static namedQueries = {}

    def static surroundingTime(Date time) {
        [UnboundedInventorySlot.surroundingTime(time), BoundedInventorySlot.surroundingTime(time)].flatten()
    }

    def endTime() {
        return ((BoundedInventorySlot) this).endTime?: (UnboundedInventorySlot (this)).endTime()
    }
}

UnboundedInventorySlot.groovy

class UnboundedInventorySlot extends InventorySlot {

    static namedQueries = {
    //        surroundingTime { time ->
    //            le 'startTime', time
    //            ge 'startTime', time - 1
    //        }
    }

    @Override
    def static surroundingTime(Date time) {
        findAllByStartTimeLessThanEqualsAndStartTimeGreaterThanEquals(time, time - 1)
    }

    def Date endTime() {
        def endTime

        // If the office closing is defined, use that, otherwise use tonight @ 23:59:59
        endTime = schedule?.officeOpenEnd?: new DateTime(startTime + 1).withTimeAtStartOfDay().plusSeconds(-1).toDate()

        return endTime
    }
}

BoundedInventorySlot.groovy

class BoundedInventorySlot extends InventorySlot {

    Date endTime

    static constraints = {
        endTime nullable: false, blank: false, validator: {val, obj ->
            if (val.date != obj.startTime.date) { return ["invalid.differentDate", val.date] }
        }
    }

    static namedQueries = {
    //        surroundingTime { time ->
    //            le 'startTime', time
    //            ge 'endTime', time
    //        }
    }

    @Override
    def static surroundingTime(Date time) {
        findAllByStartTimeLessThanEqualsAndEndTimeGreaterThanEquals(time, time)
    }


    @Override
    def Date endTime() {
        endTime
    }
}

What I would like to do is to implement the Schedule#isAvailableAt(Date) method as follows:

def boolean isAvailableAt(Date dateTime) {
    def isAvailable = true

    if (inventorySlots.isEmpty()) {
        isAvailable = false
    } else if (inventorySlots.surroundingTime(dateTime).isEmpty()) {
        isAvailable = false
    }

    isAvailable
}

where the inventorySlots.surroundingTime() invocation is essentially InventorySlot.surroundingTime() but instead of querying the universe of InventorySlots, it pre-filters on just the instances associated with the schedule instance. This is very common in Rails, but any searches for "chained query" or "collection query" in Grails doesn't seem to provide good documentation. Thanks for any help.

도움이 되었습니까?

해결책

I can think of two approaches off the top of my head which would work:

A more complex dynamic finder:

InventorySlots.findAllByScheduleAndStartTimeLessThanEqualsAndEndTimeGreaterThanEquals(this, time, time -1)

You can chain named queries together, then use any of the autowired finders to run the actual query, so if you uncomment your named query:

static namedQueries = {
        surroundingTime { time ->
            le 'startTime', time
            ge 'startTime', time - 1
        }
}

You could simply call:

InventorySlots.surroundingTime(time).findAllBySchedule(this)

You might also want to look into where queries in Grails 2 if you are not a fan of the criteria builder syntax. They are more type safe than criteria queries, and can be chained in the same fashion.

Update: Unfortunately, I'm not familiar with the inner workings of how named queries work with polymorphism, and I presume trouble with that is why you commented that out. I think worst case though, you could build a query on the parent like this one:

surroundingTime { time ->
            or {
                and {
                    eq('class', BoundedInventorySlot.name)
                    le 'startTime', time
                    ge 'startTime', time
                }
                and {
                    eq('class', UnboundedInventorySlot.name)
                    le 'startTime', time
                    ge 'startTime', time - 1
                }
            }
        }

***Update: Could you leverage the spread operator to simplify your task? i.e. Keep this code, but remove the .flatten() and call surroundingTime as a named query or where query.

def static surroundingTime(Date time) {
        [UnboundedInventorySlot.surroundingTime(time), BoundedInventorySlot.surroundingTime(time)]
    }

Then you could call: Schedule.surroundingTime(time)*.findAllBySchedule(this).flatten()

Not ideal that the caller needs to know to combine the results, but an interesting approach maybe.

다른 팁

As a reference:

Query chaining in Grails' docs: http://grails.org/doc/latest/guide/GORM.html#querying

See the "Query Composition" part.

Adding an answer as this was the first search result. According to the docs, a where clause is a 'Detached Criteria'.

Example:

def query = Person.where {
     lastName == "Simpson"
}
def bartQuery = query.where {
     firstName == "Bart"
}
Person p = bartQuery.find()

Where Query Composition

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top