Implementazione dell'impaginazione e dei totali di ricerca con la ricerca FHIR

L'implementazione della ricerca FHIR dell'API Cloud Healthcare è altamente scalabile e ad alte prestazioni, pur rispettando le linee guida e le limitazioni di REST e della specifica FHIR. Per aiutarti a raggiungere questo obiettivo, la ricerca FHIR ha le seguenti proprietà:

  • Il totale della ricerca è una stima fino a quando non viene restituita l'ultima pagina. I risultati di ricerca restituiti dal metodo fhir.search includono il totale della ricerca (la proprietà Bundle.total), ovvero il numero totale di corrispondenze nella ricerca. Il totale delle ricerche è una stima fino a quando non viene restituita l'ultima pagina dei risultati di ricerca. Il totale della ricerca restituito con l'ultima pagina dei risultati è una somma accurata di tutte le corrispondenze nella ricerca.

  • I risultati di ricerca forniscono una paginazione sequenziale in avanti. Quando ci sono altri risultati di ricerca da restituire, la risposta include un URL di paginazione (Bundle.link.url) per ottenere la pagina successiva dei risultati.

Casi d'uso di base

La ricerca FHIR fornisce soluzioni per i seguenti casi d'uso:

Consulta le sezioni seguenti per possibili soluzioni per questi casi d'uso.

Sfogliare in sequenza

Puoi creare un'applicazione a bassa latenza che consenta a un utente di scorrere вперед seguentemente le pagine dei risultati finché non trova la corrispondenza che cerca. Questa soluzione è fattibile se il numero di corrispondenze è sufficientemente ridotto da consentire all'utente di trovare la corrispondenza desiderata sfogliando le pagine senza saltare i risultati. Se il tuo caso d'uso richiede agli utenti di passare a più pagine contemporaneamente o di tornare indietro, consulta Passare a una pagina vicina.

Questa soluzione non può fornire un totale di ricerca accurato finché non viene restituita l'ultima pagina dei risultati. Tuttavia, può fornire un totale approssimativo della ricerca con ogni pagina di risultati. Sebbene un totale di ricerche preciso possa essere un requisito per un processo in background, un totale di ricerche approssimativo è in genere adeguato per un utente umano.

Flusso di lavoro

Ecco un esempio di flusso di lavoro per questa soluzione:

  1. Un'app chiama il metodo fhir.search, che restituisce la prima pagina dei risultati di ricerca. La risposta include un URL di paginazione (Bundle.link.url) se sono presenti altri risultati da restituire. La risposta include anche il totale della ricerca (Bundle.total). Se sono presenti più risultati da restituire rispetto alla risposta iniziale, il totale della ricerca è solo una stima. Per ulteriori informazioni, consulta la sezione Paginazione e ordinamento.

  2. L'app mostra la pagina dei risultati di ricerca, un link alla pagina successiva dei risultati (se presente) e il totale della ricerca.

  3. Se l'utente vuole visualizzare la pagina di risultati successiva, fa clic sul link, che genera una chiamata all'URL di paginazione. La risposta include un nuovo URL di paginazione se sono presenti altri risultati da restituire. La risposta include anche il totale delle ricerche. Si tratta di una stima aggiornata se sono disponibili altri risultati.

  4. L'app mostra la nuova pagina dei risultati di ricerca, un link alla pagina successiva dei risultati (se presente) e il totale della ricerca.

  5. I due passaggi precedenti vengono ripetuti finché l'utente non interrompe la ricerca o non viene restituita l'ultima pagina di risultati.

Best practice

Scegli una dimensione di pagina appropriata per la lettura da parte di una persona senza difficoltà. A seconda del caso d'uso, potrebbero essere presenti da 10 a 20 corrispondenze per pagina. Le pagine più piccole si caricano più velocemente e troppi link su una pagina possono essere difficili da gestire per un utente. Puoi controllare le dimensioni della pagina con il parametro _count.

Elaborare un insieme di risultati di ricerca

Puoi ottenere un insieme di risultati di ricerca effettuando chiamate successive al metodo fhir.search utilizzando l'URL di paginazione. Se il numero di risultati di ricerca è sufficientemente ridotto, puoi ottenere un insieme completo di risultati continuando fino a quando non ci sono più pagine da restituire. Un totale di ricerca accurato è incluso nell'ultima pagina dei risultati. Dopo aver ottenuto i risultati di ricerca, la tua app può leggerli e eseguire qualsiasi elaborazione, analisi o aggregazione necessaria.

Tieni presente che, se hai un numero molto elevato di risultati di ricerca, potrebbe non essere possibile ottenere l'ultima pagina dei risultati di ricerca (e il totale accurato della ricerca) in un lasso di tempo ragionevole.

Flusso di lavoro

Ecco un esempio di flusso di lavoro per questa soluzione:

  1. L'app chiama il metodo fhir.search, che restituisce la prima pagina dei risultati di ricerca. La risposta include un URL di paginazione (Bundle.link.url) se ci sono altri risultati da restituire.

  2. Se sono presenti altri risultati da restituire, l'app chiama l'URL di paginazione del passaggio precedente per ottenere la pagina successiva dei risultati di ricerca.

  3. L'app ripete il passaggio precedente finché non ci sono più risultati da restituire o non viene raggiunto un altro limite predefinito. Se raggiungi l'ultima pagina dei risultati di ricerca, il totale della ricerca è accurato.

  4. L'app elabora i risultati di ricerca.

A seconda del caso d'uso, la tua app può eseguire una delle seguenti operazioni:

  • Prima di elaborare i dati, attendi che vengano ricevuti tutti i risultati di ricerca.
  • Elabora i dati man mano che vengono ricevuti con ogni chiamata successiva a fhir.search.
  • Imposta un tipo di limite, ad esempio il numero di corrispondenze restituite o il tempo trascorso. Quando viene raggiunto il limite, puoi elaborare i dati, non elaborarli o eseguire un'altra azione.

Opzioni di design

Ecco alcune opzioni di progettazione che potrebbero ridurre la latenza di ricerca:

  • Impostare dimensioni di pagina grandi. Utilizza il parametro _count per impostare una dimensione della pagina grande, ad esempio da 500 a 1000, a seconda del caso d'uso. L'utilizzo di dimensioni della pagina più grandi aumenta la latenza per ogni recupero della pagina, ma potrebbe velocizzare il processo complessivo, poiché sono necessari meno recuperi della pagina per ottenere l'intero insieme di risultati di ricerca.

  • Limita i risultati di ricerca. Se ti serve solo un totale di ricerca accurato (non devi restituire i contenuti della risorsa), imposta il parametro _elements del metodo fhir.search su identifier. In questo modo, la latenza della query di ricerca potrebbe diminuire rispetto alla richiesta del ritorno di risorse FHIR complete. Per ulteriori informazioni, consulta Limitare i campi restituiti nei risultati di ricerca.

Casi d'uso che richiedono il pre-caricamento e la memorizzazione nella cache

Potresti aver bisogno di funzionalità oltre a quelle possibili utilizzando il semplice meccanismo di chiamata consecutiva del metodo fhir.search utilizzando gli URL di paginazione. Una possibilità è creare un livello di memorizzazione nella cache tra l'app e il repository FHIR che mantenga lo stato della sessione durante il pre-caricamento e la memorizzazione nella cache dei risultati di ricerca. L'app può raggruppare i risultati di ricerca in piccole "pagine di app" di 10 o 20 corrispondenze. L'app può quindi mostrare rapidamente queste piccole pagine dell'app all'utente in modo diretto e non sequenziale, in base alle sue selezioni. Per un esempio di questo tipo di flusso di lavoro, consulta Andare a una pagina nelle vicinanze.

Puoi creare una soluzione a bassa latenza che consenta a un utente di esaminare un numero elevato di risultati di ricerca finché non trova la corrispondenza che cerca. Può scalare fino a un numero di corrispondenze praticamente illimitato, mantenendo bassa la latenza e registrando un aumento relativamente ridotto del consumo di risorse. Un utente può passare direttamente a una pagina dei risultati di ricerca, fino a un numero predeterminato di pagine avanti o indietro rispetto alla pagina corrente. Puoi fornire un totale di ricerche stimato con ogni pagina di risultati. Come riferimento, questo design è simile a quello della Ricerca Google.

Flusso di lavoro

La figura 1 mostra un esempio di flusso di lavoro per questa soluzione. Con questo flusso di lavoro, ogni volta che l'utente seleziona una pagina di risultati da visualizzare, l'app fornisce link alle pagine vicine. In questo caso, l'app fornisce link a un massimo di cinque pagine indietro rispetto alla pagina selezionata e a un massimo di quattro pagine вперед rispetto alla pagina selezionata. Per rendere disponibili per la visualizzazione le quattro pagine successive, l'app esegue il pre-caricamento di altre 40 corrispondenze quando l'utente seleziona una pagina di risultati.

precaricamento e cache

Figura 1. L'app raggruppa i risultati di ricerca in "pagine dell'app" che vengono memorizzate nella cache e messe a disposizione dell'utente.

La figura 1 illustra questi passaggi:

  1. L'utente inserisce una query di ricerca nel frontend dell'app, avviando le seguenti azioni:

    1. L'app chiama il metodo fhir.search per eseguire il pre-caricamento della prima pagina dei risultati di ricerca.

      Il parametro _count è impostato su 100 per mantenere le dimensioni della pagina della risposta relativamente piccole, con tempi di risposta relativamente rapidi. La risposta include un URL di paginazione (Bundle.link.url) se sono presenti altri risultati da restituire. La risposta include anche il totale della ricerca (Bundle.total). Il totale della ricerca è una stima se ci sono altri risultati da restituire. Per ulteriori informazioni, consulta la sezione Paginazione e ordinamento.

    2. L'app invia la risposta al livello di memorizzazione nella cache.

  2. Nel livello di memorizzazione nella cache, l'app raggruppa le 100 corrispondenze della risposta in 10 pagine dell'app contenenti ciascuna 10 corrispondenze. Una pagina dell'app è un piccolo raggruppamento di corrispondenze che l'app può mostrare all'utente.

  3. L'app mostra all'utente la pagina 1 dell'app. La pagina 1 dell'app include i link alle pagine 2-10 dell'app e il totale stimato delle ricerche.

  4. L'utente fa clic su un link a una pagina dell'app diversa (pagina 10 dell'app in questo esempio), avviando le seguenti azioni:

    1. L'app chiama il metodo fhir.search, utilizzando l'URL di paginazione restituito con il pre-caricamento precedente, per pre-caricare la pagina successiva dei risultati di ricerca.

      Il parametro _count è impostato su 40 per prelevare le 40 corrispondenze successive dalla query di ricerca dell'utente. Le 40 corrispondenze riguardano le quattro pagine successive dell'app messe a disposizione dell'utente.

    2. L'app invia la risposta al livello di memorizzazione nella cache.

  5. Nel livello di memorizzazione nella cache, l'app raggruppa le 40 corrispondenze della risposta in quattro pagine dell'app con 10 corrispondenze ciascuna.

  6. L'app mostra all'utente la pagina 10 dell'app. La pagina dell'app 10 include link alle pagine dell'app 5-9 (le cinque pagine dell'app precedenti alla pagina 10) e alle pagine dell'app 11-14 (le quattro pagine dell'app successive alla pagina 10). La pagina 10 dell'app include anche il totale stimato delle ricerche.

Questa operazione può continuare finché l'utente vuole continuare a fare clic sui link alle pagine dell'app. Tieni presente che se l'utente torna indietro dalla pagina dell'app corrente e hai già memorizzato nella cache tutte le pagine dell'app nelle vicinanze, puoi scegliere di non eseguire un nuovo pre-caricamento, a seconda del caso d'uso.

Questa soluzione è rapida ed efficiente per i seguenti motivi:

  • I precaricamenti di piccole dimensioni e le pagine dell'app ancora più piccole vengono elaborate rapidamente.
  • I risultati di ricerca memorizzati nella cache riducono la necessità di effettuare più chiamate per gli stessi risultati.
  • Il meccanismo rimane veloce indipendentemente dall'elevato numero di risultati di ricerca.

Opzioni di design

Ecco alcune opzioni di progettazione da prendere in considerazione, a seconda del caso d'uso:

  • Dimensioni della pagina dell'app. Le pagine dell'app possono contenere più di 10 corrispondenze se si adattano al tuo caso d'uso. Tieni presente che le pagine più piccole si caricano più velocemente e che troppi link su una pagina possono essere difficili da gestire per un utente.

  • Numero di link alle pagine dell'app. Nel flusso di lavoro suggerito qui, ogni pagina dell'app restituisce nove link ad altre pagine dell'app: cinque link alle pagine dell'app direttamente indietro rispetto alla pagina dell'app corrente e quattro link alle pagine direttamente вперед rispetto alla pagina dell'app corrente. Puoi modificare questi numeri in base al tuo caso d'uso.

Best practice

  • Utilizza il livello di memorizzazione nella cache solo quando necessario. Se configuri un livello di memorizzazione nella cache, utilizzalo solo se il caso d'uso lo richiede. Le ricerche che non richiedono il livello di memorizzazione nella cache devono ignorarlo.

  • Riduci le dimensioni della cache. Per risparmiare risorse, puoi ridurre le dimensioni della cache eliminando i vecchi risultati di ricerca, mantenendo però gli URL delle pagine che hai utilizzato per ottenere i risultati. Puoi quindi ricostruire la cache in base alle tue esigenze chiamando gli URL pagina. Tieni presente che i risultati di più chiamate allo stesso URL di pagina possono cambiare nel tempo, poiché le risorse nello spazio FHIR vengono create, aggiornate ed eliminate in background. La scelta se eseguire o meno la pulizia, come e con quale frequenza eseguire la pulizia della cache sono decisioni di progettazione che dipendono dal caso d'uso.

  • Svuota la cache per una determinata ricerca. Per risparmiare risorse, puoi rimuovere completamente dalla cache i risultati delle ricerche non attive. Ti consigliamo di rimuovere prima le ricerche inattive da più tempo. Tieni presente che se una ricerca eliminata diventa di nuovo attiva, potrebbe verificarsi uno stato di errore che forza il livello di memorizzazione nella cache a riavviare la ricerca.

Se vuoi che un utente possa accedere a qualsiasi pagina nei risultati di ricerca, non solo alle pagine vicine alla pagina corrente, puoi utilizzare un livello di memorizzazione nella cache simile a quello descritto in Andare a una pagina vicina. Tuttavia, per consentire a un utente di accedere a qualsiasi pagina dell'app dei risultati di ricerca, dovrai prelevare e memorizzare nella cache tutti i risultati di ricerca. Con un numero relativamente ridotto di risultati di ricerca, è possibile. Con un numero molto elevato di risultati di ricerca, può essere impraticabile o impossibile precaricarli tutti. Anche con un numero modesto di risultati di ricerca, il tempo necessario per il pre-caricamento può essere superiore a quello che è ragionevole aspettarsi che un utente attenda.

Flusso di lavoro

Configura un flusso di lavoro simile a Vai a una pagina nelle vicinanze, con questa differenza fondamentale: l'app continua a eseguire il pre-caricamento dei risultati di ricerca in background finché non vengono restituite tutte le corrispondenze o non viene raggiunto un altro limite predefinito.

Ecco un esempio di flusso di lavoro per questa soluzione:

  1. L'app chiama il metodo fhir.search per eseguire il pre-caricamento della prima pagina di risultati di ricerca dalla query di ricerca dell'utente. La risposta include un URL di paginazione (Bundle.link.url) se sono presenti altri risultati da restituire. La risposta include anche il totale della ricerca (Bundle.total). Si tratta di una stima se ci sono altri risultati da restituire.

  2. L'app raggruppa le corrispondenze della risposta in pagine dell'app di 20 corrispondenze ciascuna e le memorizza nella cache. Una pagina dell'app è un piccolo raggruppamento di corrispondenze che l'app può mostrare all'utente.

  3. L'app mostra all'utente la prima pagina dell'app. La pagina dell'app include link alle pagine dell'app memorizzate nella cache e al totale stimato delle ricerche.

  4. Se sono presenti altri risultati da restituire, l'app esegue le operazioni seguenti:

    • Chiama l'URL di paginazione restituito dal prefetch precedente per ottenere la pagina successiva dei risultati di ricerca.
    • Raggruppa le corrispondenze della risposta in pagine dell'app di 20 corrispondenze ciascuna e le memorizza nella cache.
    • Aggiorna la pagina dell'app attualmente visualizzata dall'utente con nuovi link alle pagine dell'app appena precaricate e memorizzate nella cache.
  5. L'app ripete il passaggio precedente finché non ci sono più risultati da restituire o non viene raggiunto un altro limite predefinito. Un totale accurato della ricerca viene restituito con l'ultima pagina dei risultati di ricerca.

Mentre l'app esegue il pre-caricamento e la memorizzazione nella cache delle corrispondenze in background, l'utente può continuare a fare clic sui link alle pagine memorizzate nella cache.

Opzioni di design

Ecco alcune opzioni di progettazione da prendere in considerazione, a seconda del caso d'uso:

  • Dimensioni della pagina dell'app. Le pagine dell'app possono contenere più o meno di 20 corrispondenze, a seconda del caso d'uso. Tieni presente che le pagine più piccole si caricano più velocemente e che troppi link su una pagina possono essere difficili da gestire per un utente.

  • Aggiorna il totale delle ricerche. Mentre l'app esegue il pre-caricamento e la memorizzazione nella cache dei risultati di ricerca in background, puoi mostrare all'utente totali di ricerca progressivamente più accurati. A tale scopo, configura l'app in modo che:

    • A un intervallo impostato, ottieni il totale delle ricerche (la proprietà Bundle.total) dall'ultimo prefetch nel livello di memorizzazione nella cache. Questa è la stima migliore attuale del totale delle ricerche. Mostrare all'utente il totale delle ricerche, indicando che si tratta di una stima. Determina la frequenza di questo aggiornamento in base al tuo caso d'uso.

    • Riconoscere quando il totale delle ricerche dal livello di memorizzazione nella cache è accurato. ovvero il totale della ricerca proviene dall'ultima pagina dei risultati di ricerca. Quando viene raggiunta l'ultima pagina dei risultati di ricerca, l'app mostra il totale della ricerca e indica all'utente che il totale della ricerca è accurato. L'app smette quindi di ricevere i totali delle ricerche dal livello di memorizzazione nella cache.

    Tieni presente che, con un numero elevato di corrispondenze, il pre-caricamento in background e la memorizzazione nella cache potrebbero non raggiungere l'ultima pagina dei risultati di ricerca (e il totale della ricerca accurata) prima che l'utente completi la sessione di ricerca.

Best practice

  • Elimina le risorse duplicate incluse. Se utilizzi i parametri _include e _revinclude per il pre-caricamento e la memorizzazione nella cache dei risultati di ricerca, consigliamo di deduplicare le risorse incluse nella cache dopo ogni pre-caricamento. In questo modo, potrai risparmiare memoria riducendo le dimensioni della cache. Quando raggruppi le corrispondenze nelle pagine dell'app, aggiungi le risorse incluse appropriate a ogni pagina dell'app. Per saperne di più, consulta Includi altre risorse nei risultati di ricerca.

  • Imposta un limite per il pre-caricamento e la memorizzazione nella cache. Con un numero molto elevato di risultati di ricerca, può essere impraticabile o impossibile precaricarli tutti. Ti consigliamo di impostare un limite per il numero di risultati di ricerca da prelevare. In questo modo, la cache rimane di dimensioni gestibili e contribuisce a risparmiare memoria. Ad esempio, puoi limitare le dimensioni della cache a 10.000 o 20.000 corrispondenze. In alternativa, puoi limitare il numero di pagine da precaricare o impostare un limite di tempo al termine del quale il precaricamento si interrompe. Il tipo di limite che imposti e il modo in cui lo imposti sono decisioni di progettazione che dipendono dal tuo caso d'uso. Se il limite viene raggiunto prima che vengano restituiti tutti i risultati di ricerca, ti consigliamo di indicarlo all' utente, specificando che il totale della ricerca è ancora una stima.

Memorizzazione nella cache frontend

Il frontend dell'applicazione, ad esempio un browser web o un'app mobile, può fornire un po' di memorizzazione nella cache dei risultati di ricerca come alternativa all'introduzione di un livello di memorizzazione nella cache nell'architettura. Questo approccio può consentire di passare alla pagina precedente o a qualsiasi pagina nella cronologia di navigazione sfruttando le chiamate AJAX e memorizzando i risultati di ricerca e/o gli URL di paginazione. Ecco alcuni vantaggi di questo approccio:

  • Può richiedere meno risorse di un livello di memorizzazione nella cache.
  • È più scalabile, in quanto distribuisce il lavoro di memorizzazione nella cache su molti client.
  • È più facile determinare quando le risorse memorizzate nella cache non sono più necessarie, ad esempio quando l'utente chiude una scheda o esce dall'interfaccia di ricerca.

Best practice generali

Di seguito sono riportate alcune best practice che si applicano a tutte le soluzioni in questo documento.

  • Pianifica pagine più piccole del valore _count. In alcune circostanze, una ricerca potrebbe restituire pagine contenenti meno corrispondenze rispetto al valore _count specificato. Ad esempio, questo potrebbe accadere se specifichi un formato di pagina particolarmente grande. Se la ricerca restituisce una pagina più piccola del valore _count e la tua app utilizza un livello di memorizzazione nella cache, potresti dover decidere se (1) mostrare meno risultati del previsto in una pagina dell'app o (2) recuperare qualche altro risultato per averne abbastanza per una pagina dell'app completa. Per ulteriori informazioni, consulta la sezione Paginazione e ordinamento.

  • Riesegui gli errori di richiesta HTTP ripetibili. L'app deve prevedere errori di richiesta HTTP ripetibili, ad esempio 429 o 500, e riprovare dopo averli ricevuti.

Valutare i casi d'uso

L'implementazione di funzionalità come la navigazione in qualsiasi pagina, la generazione di totali di ricerca accurati e l'aggiornamento dei totali stimati aumenta la complessità e i costi di sviluppo della tua app. Queste funzionalità possono anche aumentare la latenza e i costi monetari per l'utilizzo delle risorse Google Cloud. Ti consigliamo di valutare attentamente i casi d'uso per assicurarti che il valore di queste funzionalità giustifichi i costi. Ecco alcuni aspetti da prendere in considerazione:

  • Passaggio a una pagina qualsiasi. In genere, un utente non deve visitare una pagina specifica, ma molte pagine dalla pagina corrente. Nella maggior parte dei casi, l'opzione Passa a una pagina nelle vicinanze è sufficiente.

  • Totali di ricerca accurati. I totali di ricerca possono cambiare man mano che le risorse nell'archivio FHIR vengono create, aggiornate ed eliminate. Per questo motivo, un totale di ricerca accurato è preciso al momento in cui viene restituito (con l'ultima pagina dei risultati di ricerca), ma potrebbe non rimanere accurato nel tempo. Pertanto, i totali delle ricerche accurati potrebbero avere un valore limitato per la tua app, a seconda del tuo caso d'uso.