[RAILS] Comment implémenter la pagination dans GraphQL (pour ruby)

Contexte

Dans Carely, notre service, le côté serveur est implémenté en Ruby (on Rails), et l'échange de données avec le front (Vue) est implémenté en graphQL. La pagination dans Rails utilise souvent une gemme appelée Kaminari, mais dans le cas de graphQL, la pagination du curseur de style relais semble être standard, j'ai donc essayé les deux méthodes d'implémentation.

À propos de la gemme et de la version que vous utilisez (au 26 juin 2020)

kaminari est la version 1.2.1 graphql-ruby est la version 1.10.10 est.

Implémentation à l'aide de la pagination du curseur de type relais

(Ceci est l'URL pour expliquer la pagination de graphql-ruby) https://graphql-ruby.org/pagination/using_connections.html

Exemple d'implémentation côté serveur

Décrivez la classe de schéma pour utiliser le plugin de pagination comme indiqué ci-dessous.


class MySchema < GraphQL::Schema
  .
  .
  use GraphQL::Pagination::Connections
  .
end

Utilisez la description :: connection_type pour définir la requête à laquelle vous souhaitez ajouter la fonctionnalité de pagination.

field :users, Types::UserType::connection_type, null: true do
  .
  .
  argument :name, String, "Nom", required: false
  .
end

C'est tout pour l'implémentation côté serveur.

Exemple d'appel de requête frontale

Vous pouvez maintenant spécifier les paramètres pour premier (dernier), après (avant). Avec la requête ci-dessous, 10 éléments seront acquis à partir du premier et 10 éléments seront acquis par après en spécifiant après. Pour la chaîne de caractères spécifiée dans after, spécifiez la chaîne de caractères obtenue par le curseur. De plus, un champ appelé «pageInfo» peut être spécifié, et y a-t-il une page précédente ou une page suivante? Vous pouvez obtenir la position des curseurs de début et de fin.


query MyQuery {
  users (first: 10, after: "xxxx") {
    pageInfo {
      hasPreviousPage  
      hasNextPage
      endCursor
      startCursor
    }
   edges {
     cursor
        node {
          firstName
          lastName
          mailAddress
          age
          .
          .
        }
      }
   ##Peut être pris avec des nœuds
   nodes {
     firstName
     lastName
     mailAddress
     age
     .
     .
   }


##Exemple de résultat
{
  "data": {
    "users": {
      "pageInfo": {
        "hasPreviousPage": false,
        "hasNextPage": true,
        "endCursor": "MTA",
        "startCursor": "MQ"
      },
      "edges": [
        {
          "cursor": "MQ",
          "node": {
            "firstName": "Hogehoge",
            "lastName": "Fuga Fuga",
            "mailAddress": "[email protected]",
            "age": "20"
          }
        },
        {
          "cursor": "Mg",
          "node": {
            "firstName": "Hogehoge 2",
            "lastName": "Fuga Fuga 2",
            "mailAddress": "[email protected]",
            "age": "30"
          }
        },
        .
        .
     ],
     "nodes": [
       {
         "firstName": "Hogehoge",
         "lastName": "Fuga Fuga",
         "mailAddress": "[email protected]",
         "age": "20"
       },
       {
         "firstName": "Hogehoge 2",
         "lastName": "Fuga Fuga 2",
         "mailAddress": "[email protected]",
         "age": "30"
       },
        .
        .
     ]

Implémentation avec Kaminari

Les méthodes générales utilisées pour la pagination en kaminari sont les suivantes.

#Obtenez la première page divisée tous les 10
User.page(1).per(10)
#nombre total
User.page(1).per(10).total_count
#nombre de pages tolal
User.page(1).total_pages
#Nombre de pages par page
User.page(1).limit_value
#Nombre actuel de pages
User.page(1).current_page
#Nombre de pages suivantes
User.page(1).next_page
#Nombre de pages précédentes
User.page(1).prev_page
#Que ce soit la première page
User.page(1).first_page?
#Que ce soit la dernière page
User.page(1).last_page?

Exemple d'implémentation lors de l'utilisation de la fonction kaminari avec GraphQL

Créez un type pour la pagination comme indiqué ci-dessous.


module Types
  class PaginationType < Types::BaseObject
    field :total_count, Int, null: true
    field :limit_value, Int, null: true
    field :total_pages, Int, null: true
    field :current_page, Int, null: true
  end
end

Créez un ʻUserType et un ʻUsersType qui renvoie plusieurs informations utilisateur et une pagination comme suit:


module Types
  class UserType < Types::BaseObject
    field :uuid, String, null: true
    field :first_name, String, null: true
    field :last_name, String, null: true
    field :mail_address, String, null: true
    field :age, String, null: true
    .
    .
  end
end

module Types
  class UsersType < Types::BaseObject
    field :pagination, PaginationType, null: true
    field :users, [UserType], null: true
  end
end


Ajoutez le traitement suivant à Query pour renvoyer les informations de pagination.


#Page avec argument,Ajouté pour pouvoir passer par
field :users, Types::UserType, null: true do
  .
  .
  argument :name, String, "Nom", required: false
  argument :page, Int, required: false
  argument :per, Int, required: false
  .
end

#Utilisez la pagination de kaminari s'il y a une page d'arguments et par
def users(**args)
  .
  .
  users = User.page(args[:page]).per(args[:per])
  {
     users: users,
     pagination: pagination(users)
  }
end

#Renvoie le nombre en utilisant la méthode de Kaminari
def pagination(result)
  {
    total_count: result.total_count,
    limit_value: result.limit_value,
    total_pages: result.total_pages,
    current_page: result.current_page
  }
end

Voici un exemple de requête qui récupère la première page divisée en 10 cas et le résultat.


query MyQuery {
  users (per:10, page:1) {
    pagination {
      currentPage
      limitValue
      totalCount
      totalPages
    }
   users {
     firstName
     lastName
     mailAddress
     age
     .
     .
   }
}
   

##Exemple de résultat
{
  "data": {
    "users": {
      "pagination": {
        "currentPage": 1,
        "limitValue": 10,
        "totalCount": 100,
        "totalPages": 10
      },
      "users": [
        {
          "firstName": "Hogehoge",
          "lastName": "Fuga Fuga",
          "mailAddress": "[email protected]",
          "age": "20"
        },
        {
          "firstName": "Hogehoge 2",
          "lastName": "Fuga Fuga 2",
          "mailAddress": "[email protected]",
          "age": "30"
        },
        .
        .
     ]

À propos de l'utilisation appropriée

Relay-Style Cursor Pagination Cela a l'air bien car il est facile à utiliser si vous recherchez simplement des informations avec l'API. Si vous souhaitez créer une interface utilisateur qui affiche le nombre total de cas et le nombre total de pages au recto car elle ne contient que les informations de localisation par curseur, Personnalisé ) Semble avoir besoin de créer une connexion.

kaminari Si vous n'utilisez que des ingénieurs internes et créez une interface utilisateur qui affiche le nombre total de cas et le nombre total de pages au recto, l'utilisation de kaminari demande moins de travail.

Personnellement, je pense qu'il serait préférable de créer une connexion personnalisée en utilisant Relay-Style Cursor Pagination car cela convient au style de graphQL.

Recommended Posts

Comment implémenter la pagination dans GraphQL (pour ruby)
Comment itérer indéfiniment en Ruby
Essayez d'implémenter Yuma dans Ruby
Comment installer Bootstrap dans Ruby
Comment implémenter la fonctionnalité de recherche dans Rails
Comment implémenter le filtre de Kalman par Java
[Ruby] Comment utiliser slice pour les débutants
Comment implémenter la fonctionnalité de classement dans Rails
Comment implémenter le traitement asynchrone dans Outsystems
[Pour les débutants] Comment déboguer avec Eclipse
Comment créer une requête à l'aide de variables dans GraphQL [Utilisation de Ruby on Rails]
Comment créer des pages pour le tableau "kaminari"
[Rails] Comment implémenter un test unitaire d'un modèle
[Pour les débutants] Comment implémenter la fonction de suppression
Comment implémenter une fonctionnalité similaire dans Rails
Comment implémenter le verrouillage optimiste dans l'API REST
Comment créer la blockchain la plus simple de Ruby
Comment implémenter UICollectionView avec du code uniquement dans Swift
[Ruby] Comment utiliser la sortie standard dans le branchement conditionnel
Comment implémenter la connexion invité en 5 minutes sur le portefeuille de rails
Comment implémenter une fonctionnalité intéressante dans Ajax avec Rails
[Ruby on Rails] Comment écrire enum en japonais
Rails / Ruby: Comment obtenir du texte HTML pour Mail
[Ruby On Rails] Comment réinitialiser DB dans Heroku
Comment lancer une autre commande dans un programme Ruby
Comment gérer les fichiers TSV et les fichiers CSV dans Ruby
Résumé de la mise en œuvre des arguments par défaut en Java
Comment résoudre l'erreur SSL_connect dans le SDK PayPal Ruby
Comment utiliser le retour Ruby
Ruby: Comment utiliser les cookies
[Rails] Comment mettre en œuvre le scraping
[Java] Comment implémenter le multithreading
[Ruby on Rails] Comment implémenter la fonction de balisage / recherche incrémentielle pour les articles (sans gemme)
[Ruby] Comment diviser chaque requête GraphQL en fichiers
Comment changer une chaîne dans un tableau en un nombre dans Ruby
Comment récupérer la valeur de hachage dans un tableau dans Ruby
Comment afficher des graphiques dans Ruby on Rails (LazyHighChart)
Comment implémenter l'affichage sur une seule ligne de TextView dans le développement Android
Comment installer une application Web pour chaque langue dans Nginx
Comment utiliser Lombok au printemps
Comment spécifier la validation pour time_field
Comment exécuter JUnit dans Eclipse
Comment installer JMeter pour Mac
Comment exécuter Ant dans Gradle
Comment maîtriser la programmation en 3 mois
Comment installer Ruby via rbenv
Comment apprendre JAVA en 7 jours
Comment obtenir des paramètres dans Spark
Implémenter le client gRPC dans Ruby
Comment installer jQuery dans Rails 6