Maison  >  Article  >  interface Web  >  GraphQL : Comment activer le filtrage de liste arbitraire avec Sift.js.

GraphQL : Comment activer le filtrage de liste arbitraire avec Sift.js.

王林
王林original
2024-08-05 20:07:20331parcourir

GraphQL: How to Enable Arbitrary List Filtering with Sift.js.

Article original

Très souvent, il existe un type de données de liste dans votre schéma GraphQL, et une exigence courante est de filtrer la liste en fonction de certaines variables d'entrée. Le filtrage est une fonctionnalité cruciale qui permet aux utilisateurs de récupérer uniquement les données dont ils ont besoin, rendant ainsi les applications plus efficaces et plus conviviales.

Il existe de nombreuses bibliothèques et ressources disponibles pour le filtrage lors de l'utilisation d'une source de données externe prenant en charge les requêtes, comme Prisma devant une base de données. Cependant, lorsque vous écrivez vos propres résolveurs qui renvoient une liste d'objets GraphQL, il serait avantageux de supprimer la logique de filtrage et de la rendre réutilisable dans votre schéma.

Considérons un schéma GraphQL simple pour une liste de livres :

type Book {
  title: String
  price: Float
}

type Query {
  books: [Book]
}

Et le résolveur suivant qui renvoie une liste de livres à partir d'une simple liste. Il peut s'agir de n'importe quelle source de données.

const books = [
  { title: 'The Great Gatsby', price: 10.99 },
  { title: 'To Kill a Mockingbird', price: 12.99 },
  // more books
];

const resolvers = {
  Query: {
    books: () => books,
  },
};

Pour notre exemple, supposons que les utilisateurs doivent filtrer les livres en fonction des critères suivants :

  • Par quoi commence le titre

  • Le prix étant compris dans une fourchette, inférieure à et supérieure à

Comment définir des filtres individuels et une logique dans GraphQL

Une façon d'implémenter des filtres est de définir chacun individuellement. Cela implique d'apporter des modifications aux types d'entrée du schéma GraphQL et d'implémenter les filtres dans la logique du résolveur.

Vous pouvez mettre à jour votre schéma pour inclure ces nouvelles variables d'entrée, vous permettant d'exprimer les filtres autorisés et les paramètres nécessaires pour les utiliser :

input BookFilter {
  titleStartsWith: String
  priceLessThan: Float
  priceGreaterThan: Float
}

type Query {
  books(filter: BookFilter): [Book]
}

Un résolveur mis à jour pourrait ressembler à ceci :

const resolvers = {
  Query: {
    books: (_, { filter }) => {
      return books.filter(book => {
        if (filter.titleStartsWith && !book.title.startsWith(filter.titleStartsWith)) {
          return false;
        }
        if (filter.priceLessThan !== undefined && book.price >= filter.priceLessThan) {
          return false;
        }
        if (filter.priceGreaterThan !== undefined && book.price <= filter.priceGreaterThan) {
          return false;
        }
        return true;
      });
    },
  },
};

Faire des requêtes avec cette syntaxe est raisonnablement facile à comprendre. Vous fourniriez un argument de filtre au résolveur GraphQL, en fournissant des valeurs pour ces champs de saisie de filtre si nécessaire.

Avantages de cette approche

Seuls les filtres que vous souhaitez autoriser l'utilisateur à utiliser sont pris en charge.

Ceci est soutenu par le système de validation de type GraphQL, qui ne permettra pas de filtrer en dehors de ce qui est autorisé. Le code du résolveur dans le backend lui-même ne prend même pas en charge les filtres qui ne sont pas autorisés.

Inconvénients de cette approche

Vous devez définir chaque filtre individuellement dans le schéma GraphQL et dans l'implémentation dans le code.

Vous ne pouvez pas facilement partager ce code entre différents objets GraphQL. Si vous aviez également des vidéos et que vous souhaitiez les filtrer, vous aurez besoin d'un nouveau type d'entrée de filtrage pour les vidéos. (Vous pouvez généraliser à une entrée de filtre, mais le livre et la vidéo ne peuvent alors pas différer.)

S'il existe une exigence pour un nouveau filtre, il nécessite un changement de code pour l'ajouter au type de filtre d'entrée et pour mettre à jour le code du résolveur pour le prendre en charge.

Par exemple, si vous souhaitez filtrer les titres qui incluent une sous-chaîne n'importe où, pas seulement au début, il s'agit d'une nouvelle entrée de filtre et d'une nouvelle implémentation dans vos résolveurs.

Filtrage arbitraire en acceptant le langage de requête Sift comme entrée de filtre

Une bibliothèque intéressante que j'ai trouvée, tamisée, permet d'utiliser la syntaxe de requête MongoDB pour filtrer facilement des listes arbitraires de données en JavaScript. Je pense que c'est vraiment cool et peut permettre un filtrage arbitraire dans GraphQL. Le CMS sans tête Strapi utilisait auparavant Sift avant de passer à une solution plus personnalisée pour activer ses requêtes GraphQL !

J'étais très enthousiasmé par cela car cela semblait être un moyen de reproduire quelque peu le filtrage automatique utile que certains ORM et fournisseurs ont intégré à leurs services GraphQL. Et cela n'a même pas d'importance si les données ne proviennent pas d'une certaine base de données.

Vous pouvez réécrire le schéma ci-dessus comme suit :

input SiftQueryInput {
  field: String
  filter: String
}

type Query {
  books(filter: [SiftQueryInput]): [Book]
}

Et le résolveur pour :

const sift = require('sift').default;

const resolvers = {
  Query: {
    books: (_, { filter }) => {
      const siftQuery = filter.reduce((acc, { field, filter }) => {
        acc[field] = JSON.parse(filter);
        return acc;
      }, {});
      return books.filter(sift(siftQuery));
    },
  },
};

Alors, comment ça marche ? Supposons que vous souhaitiez interroger tous les livres commençant par « The ». Vous pouvez exécuter cette requête :

query {
  books(filter: [{ field: "title", filter: "{\"$regex\": \"^The\"}" }]) {
    title
    price
  }
}

Avec ces variables :

{
  "filter": [
    { "field": "title", "filter": "{\"$regex\": \"^The\"}" }
  ]
}

Et comme prévu, vous récupéreriez la liste filtrée uniquement sur « The Great Gatsby » !

Autre exemple, si vous souhaitez filtrer les livres qui incluent la lettre « i » et dont le prix est supérieur à 10, vous fournirez les variables suivantes :

{
  "filter": [
    { "field": "title", "filter": "{\"$regex\": \"i\"}" },
    { "field": "price", "filter": "{\"$gt\": 10}" }
  ]
}

Et vous récupérez le livre 'To Kill a Mockingbird' !

Remarquez que nous n'avons rien eu à changer dans la requête, le schéma ou les résolveurs ! Nous avons pu exprimer des filtres entièrement nouveaux qui auraient nécessité de nouvelles entrées de filtre dans l'autre approche, uniquement dans les variables en utilisant la syntaxe de requête Sift !

Avantages de cette approche

Toute logique de filtrage prise en charge par Sift peut désormais être exprimée dans vos requêtes. Si de nouvelles exigences surviennent pour différents filtres, cela ne nécessite pas de mise à jour avec de nouveaux types d'entrée et une logique de résolveur.

La même méthode de filtrage peut être utilisée pour tous vos types ! Il suffit d'accepter une liste de SiftQueryInputs, et l'implémentation backend pour gérer ces entrées Sift et les appliquer à une liste d'objets reste inchangée selon le type de liste.

Cela prend facilement en charge les objets si leurs formes changent ou s'imbriquent. Le SiftQueryInput.field est de type String car vous pouvez accéder aux propriétés imbriquées sur l'objet avec une syntaxe par points.

Par exemple, filtrer en incluant cette requête Sift est possible : { field : 'author.name.last', filter: JSON.stringify({ $eq: "Orwell" }) }

Inconvénients et mises en garde

Bien sûr, cela utilise des chaînes pour exprimer le langage de requête Sift, qui est sujet aux erreurs. Une validation et une gestion des erreurs minutieuses seraient donc nécessaires pour utiliser cette approche.

En utilisant un type SiftQueryInput générique pour collecter les filtres de l'utilisateur, vous perdez la sécurité des types de GraphQL : il n'a aucun moyen de vérifier que le champ existe ou est utilisé correctement ici par votre filtre.

Les données de la liste doivent être entièrement résolues au moment où le résolveur de filtrage s'exécute. Il ne peut pas accéder aux champs situés plus loin dans la requête qui n'ont pas encore été résolus. Mais pour les situations où les données ne proviennent pas d'une base de données avec sa propre requête, peut-être d'un fichier JSON ou d'une réponse API REST, cela est probable de toute façon.

Améliorations futures

Je pense que perdre la sécurité de GraphQL est dommage dans ce cas. Il pourrait être possible de compiler les options possibles de Sift Query dans un schéma GraphQL au moment de la construction, de sorte que la syntaxe reflète la réalité de Sift de manière plus similaire, sans s'appuyer sur des chaînes.

Conclusion

En conclusion, l'utilisation de Sift.js dans GraphQL fournit un moyen flexible et puissant d'implémenter un filtrage arbitraire. Il apporte les avantages des requêtes automatiques généralement réservées aux ORM et à certains fournisseurs GraphQL pour insérer des objets JavaScript dans une liste, quelle que soit leur source.

En fournissant un « moteur » de filtrage générique dans le serveur GraphQL, avec un langage de requête flexible qui peut être appliqué à n'importe quel type, la logique du filtrage est déplacée vers le client GraphQL. Cela permet une itération beaucoup plus rapide sur les filtres et permet un degré d'expression beaucoup plus élevé dans les filtres.

J'aimerais connaître vos réflexions et vos expériences concernant la mise en œuvre du filtrage dans GraphQL : partagez-les dans les commentaires ci-dessous !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn