Como implementar a paginação e os totais de pesquisa com a pesquisa FHIR

A implementação de pesquisa FHIR da API Cloud Healthcare é altamente escalonável e tem bom desempenho, além de seguir as diretrizes e limitações do REST e a especificação FHIR. Para ajudar nesse sentido, a pesquisa FHIR tem as seguintes propriedades:

  • O total da pesquisa é uma estimativa até que a última página seja retornada. Os resultados da pesquisa retornados pelo método fhir.search incluem o total da pesquisa (a propriedade Bundle.total), que é o número total de correspondências na pesquisa. O total da pesquisa é uma estimativa até que a última página de resultados seja retornada. O total da pesquisa retornada com a última página de resultados é a soma precisa de todas as correspondências na pesquisa.

  • Os resultados da pesquisa oferecem paginação sequencial e direta. Quando há mais resultados da pesquisa para retornar, a resposta inclui um URL de paginação (Bundle.link.url) para receber a próxima página de resultados.

Casos de uso básicos

A pesquisa FHIR fornece soluções para os seguintes casos de uso:

Consulte as seções a seguir para conhecer possíveis soluções para esses casos de uso.

Navegar sequencialmente

É possível criar um aplicativo de baixa latência que permita ao usuário navegar sequencialmente pelas páginas de resultados até encontrar a correspondência desejada. Essa solução é viável se o número de correspondências for pequeno o suficiente para que o usuário possa encontrar a desejada navegando página por página sem pular os resultados. Se o caso de uso exigir que os usuários naveguem para frente várias páginas de uma vez ou para trás, consulte Navegar para uma página próxima.

Essa solução não fornece um total de pesquisa preciso até que a última página de resultados seja retornada. No entanto, ele pode fornecer um total de pesquisa aproximado com cada página de resultados. Embora um total preciso de pesquisas possa ser um requisito para um processo em segundo plano, um total de pesquisa aproximado normalmente é adequado para um usuário humano.

Fluxo de trabalho

Veja um exemplo de fluxo de trabalho para essa solução:

  1. Um app chama o método fhir.search, que retorna a primeira página de resultados da pesquisa. A resposta inclui um URL de paginação (Bundle.link.url) se houver mais resultados para retornar. A resposta também inclui o total da pesquisa (Bundle.total). Se houver mais resultados para retornar do que na resposta inicial, o total da pesquisa será apenas uma estimativa. Para mais informações, consulte Paginação e classificação.

  2. O app mostra a página de resultados da pesquisa, um link para a próxima página de resultados (se houver) e o total da pesquisa.

  3. Se o usuário quiser ver a próxima página de resultados, ele deverá clicar no link, o que resultará em uma chamada para o URL de paginação. A resposta vai incluir um novo URL de paginação se houver mais resultados para retornar. A resposta também inclui o total da pesquisa. Essa é uma estimativa atualizada se houver mais resultados para retornar.

  4. O app mostra a nova página de resultados da pesquisa, um link para a próxima página de resultados (se houver) e o total da pesquisa.

  5. As duas etapas anteriores são repetidas até que o usuário pare de pesquisar ou a última página de resultados seja retornada.

Prática recomendada

Escolha um tamanho de página adequado para uma pessoa ler sem dificuldade. Dependendo do seu caso de uso, há 10 a 20 correspondências por página. Páginas menores carregam mais rápido, e muitos links em uma página podem ser difíceis de gerenciar. Você controla o tamanho da página com o parâmetro _count.

Processar um conjunto de resultados da pesquisa

Você pode receber um conjunto de resultados da pesquisa fazendo chamadas sucessivas para o método fhir.search usando o URL de paginação. Se o número de resultados da pesquisa for pequeno o suficiente, continue até que não haja mais páginas a serem retornadas para ter um conjunto completo. Um total preciso da pesquisa está incluído na última página de resultados. Após receber os resultados da pesquisa, seu app pode lê-los e executar qualquer processamento, análise ou agregação necessários.

Lembre-se de que, se você tem um número muito grande de resultados da pesquisa, talvez não seja possível ver a última página de resultados (e o total preciso da pesquisa) em um período prático.

Fluxo de trabalho

Veja um exemplo de fluxo de trabalho para essa solução:

  1. O app chama o método fhir.search, que retorna a primeira página de resultados da pesquisa. A resposta vai incluir um URL de paginação (Bundle.link.url) se houver mais resultados para retornar.

  2. Se houver mais resultados para retornar, o app chamará o URL de paginação da etapa anterior para acessar a próxima página de resultados da pesquisa.

  3. O app repete a etapa anterior até que não haja mais resultados a serem retornados ou que algum outro limite predefinido seja atingido. Se você chegar à última página de resultados da pesquisa, o total da pesquisa será exato.

  4. O app processa os resultados da pesquisa.

Dependendo do caso de uso, seu app pode:

  • Aguarde até que todos os resultados da pesquisa sejam recebidos antes de processar os dados.
  • Processe os dados à medida que eles são recebidos a cada chamada sucessiva para fhir.search.
  • Definir algum tipo de limite, como o número de correspondências retornadas ou o tempo decorrido. Quando o limite é atingido, é possível processar os dados, não processá-los ou realizar alguma outra ação.

Opções de design

Aqui estão algumas opções de design que podem diminuir a latência da pesquisa:

  • Defina um tamanho de página grande. Use o parâmetro _count para definir um tamanho de página grande, de 500 a 1.000, dependendo do caso de uso. Usar um tamanho de página maior aumenta a latência para cada busca de página, mas pode acelerar o processo geral, já que são necessárias menos buscas de página para exibir todo o conjunto de resultados da pesquisa.

  • Limite os resultados da pesquisa. Se você só precisa de um total de pesquisa preciso (não é necessário retornar o conteúdo do recurso), defina o parâmetro _elements do método fhir.search como identifier. Isso pode diminuir a latência da consulta de pesquisa em comparação com a solicitação de retorno de recursos FHIR completos. Para mais informações, consulte Como limitar os campos retornados nos resultados da pesquisa.

Casos de uso que exigem pré-busca e armazenamento em cache

Talvez você precise de mais funcionalidades usando o mecanismo simples de chamar sucessivamente o método fhir.search usando URLs de paginação. Uma possibilidade é criar uma camada de armazenamento em cache entre seu app e o armazenamento FHIR que mantém o estado da sessão durante a pré-busca e o armazenamento em cache dos resultados da pesquisa. O app pode agrupar os resultados da pesquisa em pequenas "páginas do aplicativo" de 10 ou 20 correspondências. Em seguida, o app pode exibir rapidamente essas pequenas páginas ao usuário, de maneira direta e não sequencial, com base nas seleções do usuário. Consulte Navegar até uma página próxima para conferir um exemplo desse tipo de fluxo de trabalho.

É possível criar uma solução de baixa latência que permita que um usuário navegue por um grande número de resultados da pesquisa até encontrar a correspondência que está procurando. Ele pode escalonar para um número de correspondências praticamente ilimitado, mantendo a latência baixa e gerando um aumento relativamente pequeno no consumo de recursos. Um usuário pode navegar diretamente para uma página de resultados da pesquisa, até um número predeterminado de páginas para frente ou para trás da página atual. Você pode fornecer um total estimado de pesquisas com cada página de resultados. Para referência, esse design é semelhante ao da Pesquisa Google.

Fluxo de trabalho

A Figura 1 mostra um exemplo de fluxo de trabalho para esta solução. Com esse fluxo de trabalho, cada vez que o usuário selecionar uma página de resultados para visualizar, o app vai fornecer links para páginas próximas. Nesse caso, o app fornece links para páginas de até cinco páginas voltadas à página selecionada e até quatro páginas seguintes. Para disponibilizar as quatro páginas futuras para visualização, o app pré-busca 40 correspondências adicionais quando o usuário seleciona uma página de resultados.

pré-busca e armazenamento em cache

Figura 1. O app agrupa os resultados da pesquisa em "páginas do app" que são armazenadas em cache e disponibilizadas para o usuário.

A Figura 1 ilustra essas etapas:

  1. O usuário insere uma consulta de pesquisa no front-end do app, iniciando as seguintes ações:

    1. O app chama o método fhir.search para fazer a pré-busca da primeira página de resultados da pesquisa.

      O parâmetro _count é definido como 100 para manter o tamanho da página da resposta relativamente pequeno, resultando em tempos de resposta relativamente rápidos. A resposta inclui um URL de paginação (Bundle.link.url) se houver mais resultados para retornar. A resposta também inclui o total da pesquisa (Bundle.total). O total da pesquisa é uma estimativa se há mais resultados a serem retornados. Para mais informações, consulte Paginação e classificação.

    2. O app envia a resposta para a camada de armazenamento em cache.

  2. Na camada de armazenamento em cache, o app agrupa as 100 correspondências da resposta em 10 páginas do app, sendo 10 correspondentes a cada uma. Uma página do app é um pequeno grupo de correspondências que o app pode exibir para o usuário.

  3. O app mostra a página 1 ao usuário. A página 1 do app inclui links para as páginas 2 a 10 do app e o total estimado da pesquisa.

  4. O usuário clica em um link para outra página do app (página 10 neste exemplo), iniciando as seguintes ações:

    1. O app chama o método fhir.search usando o URL de paginação que foi retornado com a pré-busca anterior para pré-buscar a próxima página de resultados da pesquisa.

      O parâmetro _count é definido como 40 para fazer a pré-busca das próximas 40 correspondências da consulta de pesquisa do usuário. As 40 correspondências são referentes às próximas quatro páginas que o app disponibiliza para o usuário.

    2. O app envia a resposta para a camada de armazenamento em cache.

  5. Na camada de armazenamento em cache, o app agrupa as 40 correspondências da resposta em quatro páginas do app, com 10 páginas correspondentes a cada uma.

  6. O app mostra a página 10 para o usuário. A página 10 do app inclui links para as páginas 5 a 9 do app (as cinco páginas anteriores à 10) e links para as páginas 11 a 14 do app (as quatro páginas do app a partir da 10). A página 10 do app também inclui o total de pesquisa estimado.

Isso poderá continuar enquanto o usuário quiser continuar clicando em links para páginas do app. Se o usuário navegar da página atual do app e você já tiver todas as páginas do app próximas armazenadas em cache, poderá optar por não fazer uma nova pré-busca, dependendo do caso de uso.

Essa solução é rápida e eficiente pelos seguintes motivos:

  • Pré-buscas pequenas e páginas de apps ainda menores são processadas rapidamente.
  • Isso reduz a necessidade de fazer várias chamadas para os mesmos resultados.
  • O mecanismo permanece rápido, independentemente da escala do número de resultados da pesquisa.

Opções de design

Aqui estão algumas opções de design a serem consideradas, dependendo do seu caso de uso:

  • Tamanho da página do app. As páginas do app podem conter mais de 10 correspondências, se isso se encaixar no seu caso de uso. Lembre-se de que páginas menores carregam mais rápido e de muitos links em uma página, o que pode dificultar o gerenciamento do usuário.

  • Número de links de página do app. No fluxo de trabalho sugerido aqui, cada página do app retorna nove links para outras páginas do app: cinco links para as páginas do app diretamente para trás da página atual do app e quatro links para as páginas diretamente encaminhadas da página atual do app. Ajuste esses números de acordo com seu caso de uso.

Práticas recomendadas

  • Use a camada de armazenamento em cache somente quando necessário. Se você configurar uma camada de armazenamento em cache, use-a somente quando for necessário para o caso de uso. As pesquisas que não exigem a camada de armazenamento em cache precisam ignorá-la.

  • Reduza o tamanho do cache. Para economizar recursos, reduza o tamanho do cache. Para isso, limpe os resultados de pesquisa antigos e mantenha os URLs das páginas usados para receber os resultados. Em seguida, reconstrua o cache conforme necessário chamando os URLs da página. Lembre-se de que os resultados de várias chamadas para o mesmo URL de paginação podem mudar com o tempo, à medida que os recursos no armazenamento FHIR são criados, atualizados e excluídos em segundo plano. Fazer a limpeza, como limpar e com que frequência limpar seu cache são decisões de design que dependem do seu caso de uso.

  • Limpe o cache para uma determinada pesquisa. Para economizar recursos, você pode remover completamente do cache os resultados das pesquisas inativas. Remova primeiro as pesquisas inativas por mais tempo. Tenha em mente que, se uma pesquisa limpa ficar ativa novamente, isso pode causar um estado de erro que força a camada de armazenamento em cache a reiniciar a pesquisa.

Se você quiser que um usuário consiga navegar para qualquer página nos resultados da pesquisa, e não apenas as páginas próximas à página atual, use uma camada de armazenamento em cache semelhante à descrita em Navegar para uma página próxima. No entanto, para permitir que um usuário navegue até qualquer página de resultado da pesquisa do app, é necessário fazer a pré-busca e o armazenamento em cache de todos os resultados da pesquisa. Isso é possível com um número relativamente pequeno de resultados da pesquisa. Com um número muito grande de resultados da pesquisa, pode ser impraticável ou impossível pré-buscar todos eles. Mesmo com um número modesto de resultados da pesquisa, o tempo necessário para fazer a pré-busca deles pode ser mais longo do que o esperado.

Fluxo de trabalho

Configure um fluxo de trabalho semelhante a Navegar até uma página próxima, com esta diferença importante: o app continua pré-buscando resultados da pesquisa em segundo plano até que todas as correspondências sejam retornadas ou algum outro limite predefinido seja atingido.

Veja um exemplo de fluxo de trabalho para essa solução:

  1. O app chama o método fhir.search para fazer a pré-busca da primeira página de resultados da pesquisa do usuário. A resposta inclui um URL de paginação (Bundle.link.url) se houver mais resultados para retornar. A resposta também inclui o total da pesquisa (Bundle.total). Trata-se de uma estimativa se houver mais resultados a serem retornados.

  2. O app agrupa as correspondências da resposta em páginas do app de 20 correspondências e as armazena no cache. Uma página do app é um pequeno grupo de correspondências que o app pode exibir para o usuário.

  3. O app mostra a primeira página do app para o usuário. A página do app inclui links para as páginas armazenadas em cache e para o total de pesquisa estimado.

  4. Se houver mais resultados para retornar, o app fará o seguinte:

    • Chama o URL de paginação retornado da pré-busca anterior para receber a próxima página de resultados da pesquisa.
    • Agrupa as correspondências da resposta em páginas do app de 20 correspondências e as armazena no cache.
    • Atualiza a página do app que o usuário está visualizando no momento com novos links para as páginas do app recém-buscadas e armazenadas em cache.
  5. O app repete a etapa anterior até que não haja mais resultados a serem retornados ou que algum outro limite predefinido seja atingido. Um total preciso da pesquisa é retornado com a última página de resultados da pesquisa.

Enquanto o app faz a pré-busca e o armazenamento em cache de correspondências em segundo plano, o usuário pode continuar clicando em links para páginas em cache.

Opções de design

Aqui estão algumas opções de design a serem consideradas, dependendo do seu caso de uso:

  • Tamanho da página do app. As páginas do seu app podem conter mais ou menos de 20 correspondências, se isso for adequado ao seu caso de uso. Lembre-se de que páginas menores são carregadas mais rapidamente e de que pode ser difícil para o usuário gerenciar um excesso de links em uma página.

  • Atualizar o total da pesquisa. Enquanto seu app faz a pré-busca e o armazenamento em cache dos resultados da pesquisa em segundo plano, é possível exibir totais de pesquisa progressivamente mais precisos para o usuário. Configure seu app para fazer o seguinte:

    • Em um intervalo definido, receba o total da pesquisa (a propriedade Bundle.total) da pré-busca mais recente na camada de armazenamento em cache. Essa é a melhor estimativa atual do total de pesquisas. Mostre o total da pesquisa para o usuário, indicando que é uma estimativa. Determine a frequência dessa atualização com base no seu caso de uso.

    • Reconhece quando o total da pesquisa na camada de armazenamento em cache é preciso. Ou seja, o total da pesquisa vem da última página de resultados da pesquisa. Quando a última página de resultados da pesquisa é alcançada, o app mostra o total da pesquisa e indica ao usuário que o total está correto. Em seguida, o app para de receber os totais de pesquisa da camada de armazenamento em cache.

    Observe que, com um grande número de correspondências, a pré-busca em segundo plano e o armazenamento em cache podem não alcançar a última página de resultados da pesquisa (e o total preciso da pesquisa) antes de o usuário concluir a sessão de pesquisa.

Práticas recomendadas

  • Elimine a duplicação de recursos incluídos. Se você usar os parâmetros _include e _revinclude ao fazer a pré-busca e armazenar os resultados da pesquisa em cache, recomendamos eliminar os recursos incluídos no cache após cada pré-busca. Isso ajuda a economizar memória, reduzindo o tamanho do cache. Ao agrupar correspondências em páginas do app, adicione os recursos apropriados incluídos em cada página do app. Para mais informações, consulte Como incluir outros recursos nos resultados da pesquisa.

  • Definir um limite para a pré-busca e o armazenamento em cache. Com um número muito grande de resultados da pesquisa, pode ser impraticável ou impossível pré-buscar todos eles. Recomendamos definir um limite para o número de resultados da pesquisa para pré-busca. Isso mantém o cache em um tamanho gerenciável e ajuda a economizar memória. Por exemplo, você pode limitar o tamanho do cache a 10.000 ou 20.000 correspondências. Como alternativa, você pode limitar o número de páginas a serem pré-buscadas ou definir um limite de tempo após o que a pré-busca vai ser interrompida. O tipo de limite e a maneira como você o impõe são decisões de design que dependem do seu caso de uso. Se o limite for atingido antes que todos os resultados da pesquisa sejam retornados, indique isso ao usuário, inclusive que o total da pesquisa ainda é uma estimativa.

Armazenamento em cache de front-end

O front-end do aplicativo, como um navegador da Web ou app para dispositivos móveis, pode fornecer algum armazenamento em cache dos resultados da pesquisa como uma alternativa à introdução de uma camada de armazenamento em cache na arquitetura. Essa abordagem pode fornecer navegação para a página anterior ou para qualquer página no histórico de navegação, aproveitando chamadas AJAX e armazenando resultados da pesquisa e/ou URLs de paginação. Veja algumas vantagens dessa abordagem:

  • Ela pode consumir menos recursos do que uma camada de armazenamento em cache.
  • É mais escalonável, porque distribui o trabalho de armazenamento em cache entre vários clientes.
  • É mais fácil determinar quando os recursos em cache não são mais necessários, por exemplo, quando o usuário fecha uma guia ou sai da interface de pesquisa.

Práticas recomendadas gerais

Confira algumas práticas recomendadas que se aplicam a todas as soluções deste documento.

  • Planeje páginas menores que o valor de _count. Em algumas circunstâncias, uma pesquisa pode retornar páginas com menos correspondências do que o valor _count especificado. Por exemplo, isso pode acontecer se você especificar um tamanho de página muito grande. Se a pesquisa retornar uma página menor que o valor de _count e o app usar uma camada de armazenamento em cache, talvez seja necessário decidir entre (1) exibir menos resultados do que o esperado em uma página do app ou (2) buscar mais alguns resultados para ter uma página completa do app. Para mais informações, consulte Paginação e classificação.

  • Erros de solicitação HTTP repetíveis. O app mostrará erros de solicitação HTTP repetíveis, como 429 ou 500, e tentará novamente depois de recebê-los.

Avalie seus casos de uso

Implementar recursos como navegar até qualquer página, ver totais de pesquisa precisos e atualizar valores totais estimados aumenta a complexidade e os custos de desenvolvimento do app. Esses recursos também podem aumentar a latência e os custos monetários pelo uso de recursos do Google Cloud. Recomendamos avaliar cuidadosamente seus casos de uso para garantir que o valor desses recursos justifique os custos. Veja alguns pontos a serem considerados:

  • Navegue para qualquer página. Um usuário normalmente não precisa navegar para uma página específica, muitas páginas a partir da página atual. Na maioria dos casos, navegar para uma página próxima é adequado.

  • Totais de pesquisas precisos. Os totais da pesquisa podem mudar à medida que os recursos no armazenamento FHIR são criados, atualizados e excluídos. Por esse motivo, um total preciso é preciso no momento em que é retornado (com a última página dos resultados da pesquisa), mas pode não permanecer preciso ao longo do tempo. Portanto, os totais de pesquisa precisos podem ter um valor limitado para seu app, dependendo do seu caso de uso.