Grails withCriteria fetch, or a fast way to sort in predetermined order

我怕爱的太早我们不能终老 提交于 2019-12-11 09:14:35

问题


A database function returns id's of all events within a certain radius, ordered by their distance.

Afterwards, to preserve performance I'm eagerly loading necessary collections in withCriteria like this:

    def events = Event.withCriteria {
        'in'('id', ids)
        fetchMode("someRelation", FetchMode.JOIN)
        // a few more joins
        setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
    }

However this messes up the ordering. I've noticed that the result of this criteria query returns all events sorted by id. Which does somewhat make sense since in doesn't guarantee any sort of special ordering (nor does it make any sense that it should). However this poses a bit of a problem, since I want this list to be ordered.

So what I did was this:

    List<Event> temp = [];
    ids.each { id -> temp << events.find { it.id == id } }
    events = temp;

However when the list contains ~2400 elements this piece of code adds around 1 second to total execution time which is something I wish to lower as much as possible.

Is there any other way of doing this which could speed up the process?


回答1:


I think that there are at least three way to solve your problem (I've done additional researches and considerations):

SQL way

According to this gist, ordering by field is possibile also with Postgres and not only in mysql, but it's a little bit tricky since it's not directly supported. As you can read in the gist discussion there are different approaches, but I think that the cleaner and simpler is the last: adding a custom order by clause!

ORDER BY (ID=10, ID=2, ID=56, ID=40) DESC

To create a custom SQL query in Grails that returns objects with a specific type, you can follow this tutorial

Groovy way (your current)

// only four id, for the sake of simplicity
def ids = [10, 2, 56, 40];

// get your events with your criteria
def events = Event.withCriteria {
    'in'('id', ids)
    fetchMode("someRelation", FetchMode.JOIN)
    // a few more joins
    setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
}

// create an array that maps event.id -> sortedEventIndex
def indexToIdArray = [];
// event with id 2 is at index 1
// event with id 10 is at index 0 
// ...
// [null, null, 1 , null, null, null, null, null, null, null, 0, ...]
ids.eachWithIndex{ num, idx -> indexToIdArray[$num] = $idx }

def sortedEvents = [];
events.each { ev -> sortedEvents[indexToIdArray[ev.id]] = ev }

In this way you have sorted in O(n), consuming some additional memory. I don't know if this solution really performs better than your but you should give it a try.

See also this interesting article on how to find performance issue in your groovy code.

Client way

Return the unsorted list of events to the client with sorting data, then sort them in the client. If a client of your application can sort the events list in more ways, I think that this solution could be useful (every sorting action is done only client side).

the 4th, the 5th, ...

Since this is a also a performance problem, there are other possible solutions (depends on your domain/environment):

  • Cache eagerly everything, even the ordered result
  • Use a fixed list of points for sorting (if I'm near p1, use p1 for sorting)
  • Your ideas

I hope this helps.



来源:https://stackoverflow.com/questions/32278232/grails-withcriteria-fetch-or-a-fast-way-to-sort-in-predetermined-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!