# Pagination with total count (specifically in EHRBase) **Category:** [Apps](https://discourse.openehr.org/c/app-dev/8) **Created:** 2024-09-30 10:36 UTC **Views:** 171 **Replies:** 4 **URL:** https://discourse.openehr.org/t/pagination-with-total-count-specifically-in-ehrbase/5761 --- ## Post #1 by @pskillen-nes We have a system which builds AQL queries and runs them against an EHRBase 2.2 server. Users can specify a set of search criteria (time ranges, participant identifiers, fields from various archetypes, etc), and the code builds an AQL query for a known template ID. Not long before going to prod we realised we'd need to implement pagination, because without it we could often cause queries which would hang our Postgres server. The implementation of this is pretty easy (`fetch` and `offset` params are passed alongside the AQL). However, we no longer have a mechanism to get the total number of results which match the search criteria - we only get the number returned in the current page. We've considered a few approaches to fix this, such as running a second query, wrapping the initial query in a COUNT(..) function, etc, but all of these have their issues. I'm interested to see if others have solved this issue, or can we just not present users with a "total count" field? --- ## Post #2 by @vidi42 @pskillen-nes tl;dr there is not total count metadata returned with the EHRbase AQL query response, so you would need to use a count query upfront to obtain that. --- f I remember correctly with EHRbase 2.2 we've introduced some custom AQL Metadata info to help with pagination, and in every response you should get also these 3 values: ``` "fetch": 2, "offset": 8, "resultsize": 2 ``` * `fetch` represents to the `fetch` request param or AQL `LIMIT` keywork used for the request * `offset` represents to the `offset` request param or AQL keyword * `resultsize` represents the number of results that matched your current request (including fetch and offset params). However, to do a total count you would have to obtain it trough a count query, that you already mentioned. But I'm curious what other approaches have you or others considered. --- ## Post #3 by @pskillen-nes Hi Alex. Yes the `meta` block has been very useful. I've only just realised that these values aren't part of the [official openEHR spec](https://specifications.openehr.org/releases/ITS-REST/latest/query.html#tag/Response/RESULT_SET-response), so thanks for adding them. I know it's likely that I'll need to run two queries - one for results, and one for count. My main problem is the AQL is dynamically generated, so it's hard to infer the count query from the select query. My first approach was simply trying various permutations along the lines of `SELECT count(*) FROM `, as you can sometimes do with postgres, however I couldn't find something that EHRBase would accept as a valid query. My last approach was to use string manipulation to remove the `SELECT .... FROM` from the front of the query, and replace it with count: ```typescript const countQuery = `SELECT count(c/uid/value) as counter FROM ${fromClauseAndBeyond}`; ``` This approach was far too naive. For a start it didn't even preserve the `distinct`. But also, somehow, it ran with much worse performance than the actual select query. --- ### Query snippets, for reference ```sql -- Dynamically generated AQL SELECT DISTINCT e/ehr_id/value as ehrId, e/ehr_status/subject/external_ref/id/value as chi, c/uid/value as compositionUid, c as fullComposition, c/context/start_time/value as eventDateTime FROM EHR e CONTAINS COMPOSITION c[$archetype_id] CONTAINS ( ACTION op[openEHR-EHR-ACTION.service.v1] AND ACTION p[openEHR-EHR-ACTION.procedure.v1] CONTAINS CLUSTER dev[openEHR-EHR-CLUSTER.device.v1] ) WHERE c/context/participations/performer/external_ref/id/value = $qp0 AND c/name/value = $qp1 ORDER BY c/context/start_time/value ASC ``` ```json // JSON wrapper { "q": "", "query_parameters": { "qp0": "VGPOCMR1798", "qp1": "NES_TS Medical Devices Data Hub", "archetype_id": "openEHR-EHR-COMPOSITION.report-procedure.v1" }, "fetch": 100, "offset": 0 } ``` --- ## Post #4 by @HolgerReiseVSys @pskillen-nes For the given example the following count query should be sufficient: ```sql SELECT COUNT (DISTINCT c/uid/value) FROM COMPOSITION c[$archetype_id] CONTAINS ( ACTION op[openEHR-EHR-ACTION.service.v1] AND ACTION p[openEHR-EHR-ACTION.procedure.v1] CONTAINS CLUSTER dev[openEHR-EHR-CLUSTER.device.v1] ) WHERE c/context/participations/performer/external_ref/id/value = $qp0 AND c/name/value = $qp1 ``` `ORDER BY`, `LIMIT`, and `OFFSET` are not needed, of cause. As EHRbase currently is not optimized for this kind of population query, depending on the size of the database, counting the results will not be fast. There are also some areas where the original query could be improved: - Ordering by c/context/start_time/value is usually not intended, because this orders by the full date string. Instead, it should be ordered by its magnitude: `c/context/start_time` In the not-too-distant future, the performance of this feature will be significantly improved. - When paging, care has to be taken that a total ordering is specified. As the database is free to optimize the query, results may be appear on several pages, ore on none at all. Additionally sorting by `c/uid/value` will stabilize the order. Also, please be aware that in general, performance tends to degrade for higher page numbers. The database will have to perform the query up to that point in order to retrieve the relevant data. --- ## Post #5 by @pskillen-nes Thanks for the replies @HolgerReiseVSys and @vidi42. In the end we used Alex's idea of two queries, and Holger's updated query (my fault, I almost had the right AQL at the start but couldn't quite get the DISTINCT to work). This separate count query adds about 900ms on top of our response time. Though the actual select query takes 11-15sec, so the count isn't a massive overhead. This is all on a tiny AWS RDS instance with a modest dataset (~8000 records), so I'm interested to see how this scales. And thanks for the tips on ordering, I've updated our ordering AQL snippets accordingly. --- **Canonical:** https://discourse.openehr.org/t/pagination-with-total-count-specifically-in-ehrbase/5761 **Original content:** https://discourse.openehr.org/t/pagination-with-total-count-specifically-in-ehrbase/5761