Caching data in grails
Asked Answered
N

1

6

I have a site where users will first enter their search criteria. The search combines data from 2 sources, one of which is not a database. The search results are generally large enough to be paged.

Since the initial query is expensive, I would like to cache the search results if the search criteria doesn't change for subsequent page views.

My current method looks like:

def search(criteriaMap, offset, pagesize)

What is the best way of caching the search results in groovy/grails? How would I expire the search results?

Note, I see there is a springcache plugin but I am not sure if this will work for paged results. I also know that grails is packaged with ehcache but I haven't seen an API I directly have access to, I have only seen it used for 2nd level caching with GORM.

Update: I took part of @mfloryan's solution to solve this.

I created two methods in the searchService:

@Cacheable("searchCache")
def searchResults(uid, trimmedParams)
def trimParams(params)

(One is cached and the other is not.)

My code in the searchController:

def trimmedParams = searchService.trimParams(params)
def results = searchService.searchResults(trimmedParams)

//Page results
results = HelperService.pageResults(results, params.offset, params.max)

[results: results, searchParams : trimmedParams]

As a results, I invoked the search service with a trimmed list of parameters that do not include paging params. The cached search results can be returned.

The searchController takes care of the paging.

note: I didn't cache the hibernate results because I would end up with double the data in the cache. I only cached the combined results from searchService.searchResults (which hits a REST API and my local database)

Nanine answered 12/11, 2010 at 13:3 Comment(0)
S
5

Firstly, if you want to cache the query, then perhaps using the Hibernate query cache is a good start (Set cache:true in the mappings and pass cache: true to the executeQuery)

Using the SpringCache plug-in will allow you to cache the entire response from the controller. All you would need to do is decorate the controller like that: @Cacheable("searchMethodCache") or only decorate individual methods that you want results of cached. The cache will save response by query string so it should work well with paging. The @CacheFlush annotation allows to set when the cache should be cleared.

The EH Cache itself is configured in grails-app/conf/spring/resources.groovy

-- UPDATE --

Following you comment I begin to suspect your might be thinking of a slightly different solution. So there is a query that returns a list of results and that list is then paged and displayed. With standard Grails approach Hibernate will generate one query for each page (returning a subset from the full list thus running the potentially expensive query once for each page). Presumably you want the query to be run once - to return the full result set that gets cached and then serve individual pages from the it. If that's the case, far as I know, you will have to page the results manually either from the Hibernate cache or caching the result set somewhere else (Session context?)

Serrulate answered 12/11, 2010 at 13:28 Comment(4)
How would the pagination widget interact with this? can I flush the cache based on time or FIFO order?Nanine
Going to a different page changes query string so different response is cached and then served. Cache expires automatically so no need to explicitly flush based on time. Just set it in Config.groovy under sprigcache/caches/[cacheName]/timeToLive = ...Serrulate
I would like to avoid requery for different pages. How do I do that?Nanine
See the update - I don't think there is an out-of-the-box solution. On the other hand - you might be able to do something on the database side so that queries for subsequent pages are faster.Serrulate

© 2022 - 2024 — McMap. All rights reserved.