Skip to content

Commit 488c2fd

Browse files
Merge pull request #9 from KainosSoftwareLtd/VEL-1258-pagination-performance-issues
introduce totalMethod param to get fast but approx count
2 parents d084238 + da75e93 commit 488c2fd

File tree

3 files changed

+58
-10
lines changed

3 files changed

+58
-10
lines changed

src/fhir/query_string.coffee

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@ specials =
125125
query.sort ||= []
126126
query.sort.push ['$param', key, '']
127127
query
128-
format: (query, left, right)->
128+
totalMethod: (query, left, right)->
129+
query.total_method = right;
130+
query
131+
format: (query, left, right)->
129132
query
130133

131134
grouping = (acc, expr)->

src/fhir/search.litcoffee

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ so the code split as much as possible into small modules
3232
lang = require('../lang')
3333
compat = require('../compat')
3434
35-
3635
For every search type we have dedicated module,
3736
with indexing and building search expression implementation.
3837

@@ -101,27 +100,36 @@ Then we are returning resulting Bundle.
101100
102101
resource_rows = utils.exec(plv8, honey)
103102
resources = resource_rows.map((x)-> compat.parse(plv8, x.resource))
104-
count = utils.exec(plv8, countize_query(honey))[0].count
103+
104+
should_include_total = (expr.total_method != "no")
105+
106+
if should_include_total
107+
count = get_count(plv8, honey, expr)
105108
106109
base_url = "#{query.resourceType}/#{query.queryString}"
107110
108111
if expr.summary or expr.elements
109112
resources = mask_resources(plv8, expr, idx_db, resources)
110113
111-
if expr.include && count > 0
114+
if expr.include && (count > 0 || !should_include_total)
112115
includes = search_include.load_includes(plv8, expr.include, resources)
113116
resources = resources.concat(includes)
114117
115-
if expr.revinclude && count > 0
118+
if expr.revinclude && (count > 0 || !should_include_total)
116119
includes = search_include.load_revincludes(plv8, expr.revinclude, resources)
117120
resources = resources.concat(includes)
118121
122+
if should_include_total
123+
resourceType: 'Bundle'
124+
type: 'searchset'
125+
total: count
126+
link: helpers.search_links(query, expr, count)
127+
entry: resources.map(to_entry)
128+
else
129+
resourceType: 'Bundle'
130+
type: 'searchset'
131+
entry: resources.map(to_entry)
119132
120-
resourceType: 'Bundle'
121-
type: 'searchset'
122-
total: count
123-
link: helpers.search_links(query, expr, count)
124-
entry: resources.map(to_entry)
125133

126134
Helper function to convert resource into entry bundle:
127135
TODO: add links
@@ -388,6 +396,27 @@ we just strip limit, offset, order and rewrite select clause:
388396
q.select = [':count(*) as count']
389397
q
390398
399+
get_count = (plv8, honey, query_obj) ->
400+
if !query_obj.total_method || query_obj.total_method is "std"
401+
query_obj.total_method = "std"
402+
403+
utils.exec(plv8, countize_query(honey))[0].count
404+
else if query_obj.total_method is "improved"
405+
sql_query= sql(honey)
406+
query = sql_query[0].replace /\$(\d+)/g, (match, number) ->
407+
if typeof sql_query[number] is "string"
408+
return '\''+sql_query[number]+'\''
409+
else if typeof sql_query[number] isnt 'undefined'
410+
return sql_query[number]
411+
else
412+
return match
413+
query = query.replace /LIMIT \d+/, ""
414+
query = query.replace /\'/g, '\'\''
415+
query = "SELECT count_estimate('#{query}');"
416+
tmp = plv8.execute(query)
417+
tmp[0].count_estimate
418+
else
419+
throw new Error("Invalid value of totalMethod. only 'std', 'improved' and 'no' allowed.")
391420
392421
We cache FHIR meta-data index per connection using plv8 object:
393422

utils/generate_migrations.coffee

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,22 @@ CREATE AGGREGATE FIRST (
7777
basetype = anyelement,
7878
stype = anyelement
7979
);
80+
81+
-- Create function to estimate rows of a query. It's used to improve performance of search function.
82+
CREATE OR REPLACE FUNCTION count_estimate(query text) RETURNS INTEGER AS
83+
$func$
84+
DECLARE
85+
rec record;
86+
ROWS INTEGER;
87+
BEGIN
88+
FOR rec IN EXECUTE 'EXPLAIN ' || query LOOP
89+
ROWS := SUBSTRING(rec."QUERY PLAN" FROM ' rows=([[:digit:]]+)');
90+
EXIT WHEN ROWS IS NOT NULL;
91+
END LOOP;
92+
93+
RETURN ROWS;
94+
END
95+
$func$ LANGUAGE plpgsql;
8096
"""
8197

8298
is_first = true

0 commit comments

Comments
 (0)