L'API de la pagination des meilleures pratiques
J'aimerais quelques uns aider à la manipulation d'un étrange cas de bord avec un paginé de l'API, je suis en train de construire.
Comme beaucoup d'Api, celui-ci est paginé de grands résultats. Si vous interrogez /foos, vous obtiendrez 100 résultats (c'est à dire foo #1 à 100), et un lien vers /foos?page=2 qui devrait revenir foo #101-200.
Malheureusement, si foo #10 est supprimé de la base de données avant de l'API de consommation, la prochaine requête /foos?page=2 seront compensés par 100 et retour foos #102-201.
C'est un problème pour les API les consommateurs qui tentent de tirer tous les foos - ils ne recevront pas de foo #101.
Quelle est la meilleure pratique pour gérer cela? Nous tenons à le rendre aussi léger que possible (c'est à dire en évitant les séances de traitement pour les demandes d'API). Des exemples provenant d'autres Api serait grandement apprécié!
- quel est le problème ici? semble ok pour moi, de toute façon utilisateur obtient 100 points.
- Juste édité la question - problème est que foo #101 n'apparaîtront pas dans les résultats & une API de consommation d'essayer de tirer toutes les foos va manquer un.
- J'ai été confronté à ce même problème et à la recherche d'une solution. Autant que je sache, il n'y a vraiment pas de solides garanties mécanisme pour ce faire, si chaque page exécute une nouvelle requête. La seule solution je pense est de garder une session active, et de garder le jeu de résultats dans le côté serveur, et plutôt que de l'exécution de requêtes pour chaque page, il suffit de prendre la prochaine mise en cache du jeu d'enregistrements.
- Oh, je viens de voir la partie de votre question où vous voulez éviter ce scénario
- Jetez un oeil à la façon dont twitter atteindre ce dev.twitter.com/rest/public/timelines
- Comment est la since_id paramètre de mise à jour ? Dans le twitter page web, il semble qu'ils sont à la fois des demandes avec la même valeur pour since_id . Je me demande quand sera mis à jour de sorte que si de nouveaux tweets sont ajoutés, ils peuvent être pris en compte ?
- Le since_id paramètre doit être mis à jour par le consommateur de l'API. Si vous le voyez, l'exemple, il se réfère à des clients traitement des tweets
Vous devez vous connecter pour publier un commentaire.
Je ne suis pas entièrement sûr de savoir comment vos données sont traitées, ce qui peut ou peut ne pas fonctionner, mais avez-vous considéré la pagination avec un champ timestamp?
Lorsque vous interrogez /foos vous obtenez 100 résultats. Votre API devrait ensuite revenir à quelque chose comme ceci (en supposant que JSON, mais si elle a besoin de XML les mêmes principes peuvent être suivies):
Juste une remarque, en n'utilisant qu'un timestamp repose sur un accord implicite de "limite" dans vos résultats. Vous souhaiterez peut-être ajouter une limite explicite ou également utiliser un
until
propriété.L'horodatage peut être déterminée de façon dynamique en utilisant le dernier élément de données dans la liste. Cela semble être plus ou moins comment Facebook pagine dans son L'API graphique (faites défiler vers le bas pour voir la pagination des liens dans le format que j'ai donné ci-dessus).
Un problème peut-être si vous ajoutez un élément de données, mais d'après votre description, il semble comme ils le seraient ajoutés à la fin (si non, laissez-moi savoir et je vais voir si je peux améliorer sur ce point).
rowversion
est un synonyme, pas un remplacement. Généralement, sur un Débordement de Pile, ce type d'information serait utile, dans un projet de modifier, plutôt que d'être présentée comme une contradiction.Vous avez plusieurs problèmes.
Tout d'abord, vous avez l'exemple que vous avez cité.
Vous avez aussi un problème similaire, si l'insertion de lignes, mais dans ce cas, l'utilisateur d'obtenir un double des données (sans doute plus facile à gérer que des données manquantes, mais toujours un problème).
Si vous n'êtes pas instantanés de l'ensemble de données original, alors c'est juste un fait de vie.
Vous pouvez demandez à l'utilisateur de prendre explicitement instantané:
Dont les résultats:
Vous pouvez ensuite la page qui tout au long de la journée, car il est maintenant statique. Cela peut être raisonnablement la lumière du poids, car vous pouvez simplement capturer le document touches plutôt que de l'ensemble des lignes.
Si le cas est tout simplement que vos utilisateurs veulent (et besoin) de toutes les données, alors vous pouvez simplement donner:
et juste envoyer l'ensemble du kit.
Si vous avez la pagination vous aussi de trier les données par certains grands. Pourquoi ne pas laisser l'API clients comprennent la clé du dernier élément de la collection retournée dans l'URL et ajouter un
WHERE
clause à votre requête SQL (ou quelque chose d'équivalent, si vous n'êtes pas à l'aide de SQL) pour qu'il renvoie uniquement les éléments pour lesquels la clé est supérieure à cette valeur?Il peut y avoir deux approches en fonction de votre côté serveur logique.
Approche 1: Lorsque le serveur n'est pas assez intelligent pour gérer les états d'objet.
Vous pouvez envoyer toutes les mises en cache d'enregistrement unique id de serveur, par exemple ["id1","id2","id3","id4","id5","id6","id7","id8","id9","id10"] et un paramètre booléen pour savoir si vous faites une demande de nouveaux records(tirer pour rafraîchir) ou d'anciens enregistrements(charge plus).
Votre serveur doit responsables de retour de nouveaux enregistrements(charge de plusieurs documents ou de nouveaux enregistrements via tirer pour rafraîchir) ainsi que des id des enregistrements supprimés à partir de ["id1","id2","id3","id4","id5","id6","id7","id8","id9","id10"].
Exemple:-
Si vous demandez charge plus de votre requête devrait ressembler à quelque chose comme ceci:-
Maintenant, supposons que vous êtes demandeur d'anciens enregistrements(charge plus) et supposons que "id2" enregistrement est mis à jour par quelqu'un et "id5" et "id8" dossiers est supprimé à partir du serveur de votre serveur de réponse devrait ressembler à quelque chose comme ceci:-
Mais dans ce cas, si vous avez beaucoup de locaux mis en cache des dossiers supposons que 500, alors votre demande chaîne sera trop long comme ceci:-
Approche 2: Lorsque le serveur est assez intelligent pour gérer les états d'objet en fonction de la date.
Vous pouvez envoyer l'id de l'enregistrement premier et le dernier enregistrement et de la demande antérieure époque. De cette façon, votre demande est toujours faible, même si vous avez une grande quantité d'enregistrements mis en cache
Exemple:-
Si vous demandez charge plus de votre requête devrait ressembler à quelque chose comme ceci:-
Votre serveur est responsable de retourner les id des enregistrements supprimés qui est supprimé après la last_request_time, ainsi que l'enregistrement mis à jour après last_request_time entre "id1" et "id10" .
Canapé Pour Se Rafraîchir:-
Charge Plus
Il peut être difficile de trouver de meilleures pratiques depuis la plupart des systèmes avec les Api ne pas tenir compte de ce scénario, parce que c'est un extrême, soit ils ne le sont généralement pas supprimer des enregistrements (Facebook, Twitter). Facebook dit à chaque "page" ne peut pas avoir le nombre de résultats demandés en raison de filtrage fait après la pagination.
https://developers.facebook.com/blog/post/478/
Si vous avez vraiment besoin pour tenir compte de ce cas limite, vous avez besoin de "se souvenir" où vous l'avez laissé. jandjorgensen suggestion est sur le spot, mais je voudrais utiliser un champ unique comme la clé primaire. Vous pouvez avoir besoin d'utiliser plus d'un domaine.
Suivantes Facebook de flux, vous pouvez (et devez) mettre en cache les pages déjà demandé et il suffit de retourner ceux avec des lignes supprimées filtrée si ils demande une page qu'elle avait déjà demandé.
La Pagination est généralement un "utilisateur" de l'opération et afin d'éviter une surcharge à la fois sur les ordinateurs et le cerveau humain vous donnent généralement un sous-ensemble. Cependant, plutôt que de penser que nous n'avons pas l'ensemble de la liste, il peut être mieux de demander importe-t-il?
Si un précis en direct, vue défilement est nécessaire, Api REST qui sont de requête/réponse en nature ne sont pas bien adaptés à cette fin. Pour cela, vous devez considérer les WebSockets HTML5 ou Server-Sent Events de laisser votre frontal savoir lors de la modification.
Maintenant, si il y a un besoin pour obtenir un instantané des données, je voudrais juste donner un appel API qui fournit toutes les données dans une requête sans pagination. Rappelez-vous, vous auriez besoin de quelque chose qui pourrait faire du streaming de la sortie sans temporairement de le charger dans la mémoire si vous avez un grand ensemble de données.
Pour mon cas, j'ai implicitement désigner certains appels d'API pour permettre d'obtenir l'ensemble des informations (principalement de la table de référence des données). Vous pouvez également sécuriser ces Api afin de ne pas nuire à votre système.
Option A: jeu de clés de la Pagination avec un Timestamp
Pour éviter les inconvénients de compenser la pagination vous l'avez mentionné, vous pouvez utiliser le clavier en fonction de la pagination. Généralement, les entités ont un horodatage que les membres de leur création ou de la modification du temps. Cet horodatage peut être utilisé pour la pagination: il suffit de passer le timestamp du dernier élément que le paramètre de requête pour la prochaine requête. Le serveur, à son tour, utilise le timestamp comme un critère de filtre (par exemple,
WHERE modificationDate >= receivedTimestampParameter
)De cette façon, vous ne manquerez pas de n'importe quel élément. Cette approche devrait être assez bon pour de nombreux cas d'utilisation. Cependant, gardez les points suivants à l'esprit:
Vous pouvez faire ces inconvénients en moins probable par l'augmentation de la taille de la page et en utilisant des horodatages avec précision à la milliseconde.
Option B: Étendue jeu de clés de la Pagination avec un Jeton de Continuation
Pour gérer les inconvénients mentionnés de la normale de jeu de clés de la pagination, vous pouvez ajouter un offset à l'horodatage et l'utilisation d'un soi-disant "Jeton de Continuation" ou de "Curseur". Le décalage est la position de l'élément par rapport au premier élément à la même heure. Généralement, le jeton a un format comme
Timestamp_Offset
. Il est transmis au client dans la réponse et peuvent être envoyés au serveur pour récupérer la page suivante.Le jeton "1512757072_2" pointe sur le dernier élément de la page et précise que "le client a déjà obtenu le deuxième élément avec le timestamp 1512757072". De cette façon, le serveur ne sait où pour continuer.
Veuillez garder à l'esprit que vous avez à gérer les cas où les éléments ont été changés entre deux demandes. Cela se fait généralement par l'ajout d'une somme de contrôle pour le jeton. Cette somme de contrôle est calculée sur les Identifiants de tous les éléments avec ce timestamp. On se retrouve donc avec un jeton format comme ceci:
Timestamp_Offset_Checksum
.Pour plus d'informations sur cette approche découvrez le blog "L'API Web de la Pagination à la Poursuite des Jetons". Un inconvénient de cette approche est la plus délicate de la mise en œuvre comme il y a beaucoup de cas particuliers qui doivent être pris en compte. C'est pourquoi les bibliothèques comme continuation-jeton peut être à portée de main (si vous utilisez Java/une JVM de la langue). Avertissement: je suis l'auteur du post et un co-auteur de la bibliothèque.
Je pense qu'actuellement votre api est en fait de répondre comme il le devrait. Les 100 premiers enregistrements sur la page dans l'ensemble de l'ordre des objets, vous êtes à la maintenance. Votre explication indique que vous êtes en utilisant un certain type de commande id pour définir l'ordre de vos objets pour la pagination.
Maintenant, dans le cas où vous souhaitez que la page 2 doit toujours commencer à partir de 101 et à la fin, à 200, alors vous devez faire le nombre d'entrées sur la page en tant que variable, puisqu'elles sont soumises à la suppression.
Vous devriez faire quelque chose comme le pseudo-code ci-dessous:
Juste pour ajouter à cette réponse par Kamilk : https://www.stackoverflow.com/a/13905589
J'ai longuement réfléchi à ce sujet et finalement la solution que je vais décrire ci-dessous. C'est vraiment un grand pas vers le haut dans la complexité, mais si vous faites cette étape, vous vous retrouverez avec ce que vous êtes vraiment après, ce qui est déterministe résultats pour de futures demandes.
Votre exemple d'un élément supprimé est seulement la pointe de l'iceberg. Que faire si vous êtes le filtrage par
color=blue
mais quelqu'un change d'élément de couleurs entre les demandes? L'extraction de tous les éléments dans un paginé de manière fiable est impossible... à moins que... nous mettons en œuvre révision de l'histoire.J'ai mis en place et c'est en fait moins difficile que ce que j'attendais. Voici ce que j'ai fait:
changelogs
avec un auto-incrément colonne IDid
champ, mais ce n'est pas la clé primairechangeId
champ qui est à la fois la clé primaire ainsi que d'une clé étrangère à modifications.changelogs
, saisit l'id et l'attribue à un nouveau version de l'entité, à laquelle il insère ensuite dans la DBchangeId
représente un instantané unique des données sous-jacentes au moment où le changement a été créé.changeId
en eux pour toujours. Les résultats n'expirera jamais parce qu'ils ne changeront jamais.