Grails withCriteria fetch,或一种按预定顺序排序的快速方法
Grails withCriteria fetch, or a fast way to sort in predetermined order
数据库函数 returns 特定半径内所有事件的 ID,按距离排序。
之后,为了保持性能,我急切地在 withCriteria
中加载必要的集合,如下所示:
def events = Event.withCriteria {
'in'('id', ids)
fetchMode("someRelation", FetchMode.JOIN)
// a few more joins
setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
}
然而,这弄乱了顺序。我注意到此条件查询的结果 returns 所有按 id 排序的事件。这确实有点道理,因为 in
不保证任何类型的特殊排序(它应该也没有任何意义)。但是,这会带来一些问题,因为我希望对这个列表进行排序。
所以我所做的是:
List<Event> temp = [];
ids.each { id -> temp << events.find { it.id == id } }
events = temp;
然而,当列表包含约 2400 个元素时,这段代码会增加大约 1 秒的总执行时间,这是我希望尽可能降低的时间。
有没有其他方法可以加快这个过程?
我认为至少有三种方法可以解决你的问题(我做了额外的研究和考虑):
SQL 方式
根据 this gist,按字段排序 也可以用于 Postgres,不仅在 mysql 中,但它有点棘手,因为它不是直接支持。正如您在要点讨论中所读到的那样,有不同的方法,但我认为最后一种更清晰、更简单:添加自定义 order by 子句!
ORDER BY (ID=10, ID=2, ID=56, ID=40) DESC
要在 Grails 中创建 returns 具有特定类型对象的自定义 SQL 查询,您可以按照 this tutorial
Groovy方式(你现在的)
// 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 }
通过这种方式,您在 O(n) 中进行了排序,消耗了一些额外的内存。
我不知道这个解决方案是否真的比你的更好,但你应该试一试。
另请参阅 this interesting article,了解如何查找 groovy 代码中的性能问题。
客户端方式
Return 将未排序的事件列表发送给带有排序数据的客户端,然后在客户端对它们进行排序。如果您的应用程序的客户端可以以更多方式对事件列表进行排序,我认为此解决方案可能会有用(每个排序操作仅在客户端完成)。
第 4 个,第 5 个,...
由于这也是一个性能问题,还有其他可能的解决方案(取决于您的 domain/environment):
- 急切地缓存一切,甚至是有序的结果
- 使用固定的点列表进行排序(如果我在p1附近,则使用p1进行排序)
- 你的想法
希望对您有所帮助。
数据库函数 returns 特定半径内所有事件的 ID,按距离排序。
之后,为了保持性能,我急切地在 withCriteria
中加载必要的集合,如下所示:
def events = Event.withCriteria {
'in'('id', ids)
fetchMode("someRelation", FetchMode.JOIN)
// a few more joins
setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY)
}
然而,这弄乱了顺序。我注意到此条件查询的结果 returns 所有按 id 排序的事件。这确实有点道理,因为 in
不保证任何类型的特殊排序(它应该也没有任何意义)。但是,这会带来一些问题,因为我希望对这个列表进行排序。
所以我所做的是:
List<Event> temp = [];
ids.each { id -> temp << events.find { it.id == id } }
events = temp;
然而,当列表包含约 2400 个元素时,这段代码会增加大约 1 秒的总执行时间,这是我希望尽可能降低的时间。
有没有其他方法可以加快这个过程?
我认为至少有三种方法可以解决你的问题(我做了额外的研究和考虑):
SQL 方式
根据 this gist,按字段排序 也可以用于 Postgres,不仅在 mysql 中,但它有点棘手,因为它不是直接支持。正如您在要点讨论中所读到的那样,有不同的方法,但我认为最后一种更清晰、更简单:添加自定义 order by 子句!
ORDER BY (ID=10, ID=2, ID=56, ID=40) DESC
要在 Grails 中创建 returns 具有特定类型对象的自定义 SQL 查询,您可以按照 this tutorial
Groovy方式(你现在的)
// 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 }
通过这种方式,您在 O(n) 中进行了排序,消耗了一些额外的内存。 我不知道这个解决方案是否真的比你的更好,但你应该试一试。
另请参阅 this interesting article,了解如何查找 groovy 代码中的性能问题。
客户端方式
Return 将未排序的事件列表发送给带有排序数据的客户端,然后在客户端对它们进行排序。如果您的应用程序的客户端可以以更多方式对事件列表进行排序,我认为此解决方案可能会有用(每个排序操作仅在客户端完成)。
第 4 个,第 5 个,...
由于这也是一个性能问题,还有其他可能的解决方案(取决于您的 domain/environment):
- 急切地缓存一切,甚至是有序的结果
- 使用固定的点列表进行排序(如果我在p1附近,则使用p1进行排序)
- 你的想法
希望对您有所帮助。