Heim  >  Artikel  >  Web-Frontend  >  GraphQL: So aktivieren Sie die Filterung beliebiger Listen mit Sift.js.

GraphQL: So aktivieren Sie die Filterung beliebiger Listen mit Sift.js.

王林
王林Original
2024-08-05 20:07:20333Durchsuche

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

Originalartikel

Sehr oft gibt es in Ihrem GraphQL-Schema einen Listendatentyp, und eine häufige Anforderung besteht darin, die Liste basierend auf einigen Eingabevariablen zu filtern. Filterung ist eine entscheidende Funktion, die es Benutzern ermöglicht, nur die Daten abzurufen, die sie benötigen, wodurch Anwendungen effizienter und benutzerfreundlicher werden.

Es stehen viele Bibliotheken und Ressourcen zum Filtern zur Verfügung, wenn eine externe Datenquelle verwendet wird, die Abfragen unterstützt, z. B. Prisma vor einer Datenbank. Wenn Sie jedoch Ihre eigenen Resolver schreiben, die eine Liste von GraphQL-Objekten zurückgeben, wäre es von Vorteil, die Filterlogik zu abstrahieren und sie in Ihrem Schema wiederverwendbar zu machen.

Betrachten wir ein einfaches GraphQL-Schema für eine Liste von Büchern:

type Book {
  title: String
  price: Float
}

type Query {
  books: [Book]
}

Und der folgende Resolver, der eine Liste von Büchern aus einer einfachen Liste zurückgibt. Dies kann eine beliebige Datenquelle sein.

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

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

Nehmen wir für unser Beispiel an, dass Benutzer Bücher anhand der folgenden Kriterien filtern müssen:

  • Womit der Titel beginnt

  • Der Preis liegt innerhalb einer Spanne von weniger als und mehr als

So definieren Sie individuelle Filter und Logik in GraphQL

Eine Möglichkeit, Filter zu implementieren, besteht darin, jeden einzeln zu definieren. Dazu müssen Änderungen an den Eingabetypen des GraphQL-Schemas vorgenommen und die Filter in der Resolver-Logik implementiert werden.

Sie könnten Ihr Schema so aktualisieren, dass es diese neuen Eingabevariablen enthält, sodass Sie die zulässigen Filter und die für ihre Verwendung erforderlichen Parameter ausdrücken können:

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

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

Ein aktualisierter Resolver könnte so aussehen:

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;
      });
    },
  },
};

Abfragen mit dieser Syntax sind recht einfach zu verstehen. Sie würden dem GraphQL-Resolver ein Filterargument bereitstellen und bei Bedarf Werte für diese Filtereingabefelder bereitstellen.

Vorteile dieses Ansatzes

Nur ​​die Filter, die Sie dem Benutzer erlauben möchten, werden unterstützt.

Dies wird durch das GraphQL-Typvalidierungssystem unterstützt, das keine Filterung außerhalb der zulässigen Grenzen zulässt. Der Resolver-Code im Backend selbst unterstützt nicht einmal Filter, die nicht erlaubt sind.

Nachteile dieses Ansatzes

Sie müssen jeden Filter einzeln im GraphQL-Schema und in der Implementierung im Code definieren.

Sie können diesen Code nicht einfach zwischen verschiedenen GraphQL-Objekten teilen. Wenn Sie auch Videos hätten und diese filtern möchten, wäre ein neuer Filtereingabetyp für Videos erforderlich. (Sie könnten auf eine Filtereingabe verallgemeinern, aber dann können sich Buch und Video nicht unterscheiden.)

Wenn ein neuer Filter erforderlich ist, ist eine Codeänderung erforderlich, um den Eingabefiltertyp zu ergänzen und den Resolver-Code zu aktualisieren, um ihn zu unterstützen.

Wenn Sie beispielsweise Titel filtern möchten, die an einer beliebigen Stelle und nicht nur am Anfang eine Teilzeichenfolge enthalten, ist dies eine neue Filtereingabe und eine neue Implementierung in Ihren Resolvern.

Beliebige Filterung durch Akzeptieren der Sift-Abfragesprache als Filtereingabe

Eine interessante Bibliothek, Sift, die ich gefunden habe, ermöglicht die Verwendung der MongoDB-Abfragesyntax zum einfachen Filtern beliebiger Datenlisten in JavaScript. Ich finde das wirklich cool und kann beliebige Filterung in GraphQL ermöglichen. Das Headless-CMS Strapi nutzte zuvor Sift, bevor es zu einer individuelleren Lösung überging, um GraphQL-Abfragen zu ermöglichen!

Das hat mich am meisten gefreut, weil es eine Möglichkeit zu sein schien, die nützliche automatische Filterung, die einige ORMs und Anbieter in ihre GraphQL-Dienste integriert haben, einigermaßen zu reproduzieren. Dabei spielt es keine Rolle, ob die Daten nicht aus einer bestimmten Datenbank stammen.

Sie könnten das obige Schema wie folgt umschreiben:

input SiftQueryInput {
  field: String
  filter: String
}

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

Und der Resolver zu:

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));
    },
  },
};

Wie funktioniert das? Angenommen, Sie möchten alle Bücher abfragen, die mit „The“ beginnen. Sie könnten diese Abfrage ausführen:

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

Mit diesen Variablen:

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

Und wie erwartet wurde die Liste nur nach „Der große Gatsby“ gefiltert!

Ein weiteres Beispiel: Wenn Sie nach Büchern filtern möchten, die den Buchstaben „i“ enthalten und deren Preis über 10 liegt, würden Sie die folgenden Variablen angeben:

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

Und du bekommst das Buch „Wer die Nachtigall stört“ zurück!

Beachten Sie, dass wir nichts an der Abfrage, dem Schema oder den Resolvern ändern mussten! Mit der Sift-Abfragesyntax konnten wir völlig neue Filter ausdrücken, für die bei einem anderen Ansatz neue Filtereingaben erforderlich gewesen wären, und zwar einfach in den Variablen!

Vorteile dieses Ansatzes

Jede von Sift unterstützte Filterlogik kann jetzt in Ihren Abfragen ausgedrückt werden. Wenn neue Anforderungen für verschiedene Filter eintreten, ist keine Aktualisierung mit neuen Eingabetypen und Resolver-Logik erforderlich.

Die gleiche Filtermethode kann für alle Ihre Typen verwendet werden! Das bloße Akzeptieren einer Liste von SiftQueryInputs und der Backend-Implementierung zur Verarbeitung dieser Sift-Eingaben und deren Anwendung auf eine Liste von Objekten bleibt vom Typ der Liste unverändert.

Dadurch werden Objekte problemlos unterstützt, wenn sich ihre Formen ändern oder verschachtelt werden. Das SiftQueryInput.field ist vom Typ String, da Sie mit einer Punktsyntax auf verschachtelte Eigenschaften des Objekts zugreifen können.

Z. B. ist das Filtern durch die Einbeziehung dieser Sift-Abfrage möglich: { field: 'author.name.last', filter: JSON.stringify({ $eq: "Orwell" }) }

Nachteile und Vorbehalte

Natürlich werden hier Zeichenfolgen verwendet, um die Sift-Abfragesprache auszudrücken, die fehleranfällig ist – daher wäre eine sorgfältige Validierung und Fehlerbehandlung erforderlich, um diesen Ansatz zu verwenden.

Durch die Verwendung eines generischen SiftQueryInput-Typs zum Sammeln der Filter des Benutzers verlieren Sie die Typsicherheit von GraphQL – es gibt keine Möglichkeit zu überprüfen, ob das Feld vorhanden ist oder hier von Ihrem Filter korrekt verwendet wird.

Die Daten der Liste müssen zum Zeitpunkt der Ausführung des Filter-Resolvers vollständig aufgelöst sein. Es kann nicht auf Felder weiter unten in der Abfrage zugegriffen werden, die noch nicht aufgelöst wurden. Aber in Situationen, in denen die Daten nicht aus einer Datenbank mit eigener Abfrage stammen, vielleicht aus einer JSON-Datei oder einer REST-API-Antwort, ist dies wahrscheinlich sowieso der Fall.

Zukünftige Verbesserungen

Ich finde es in diesem Fall schade, die GraphQL-Sicherheit zu verlieren. Es könnte möglich sein, die möglichen Sift-Abfrageoptionen zum Zeitpunkt der Erstellung in ein GraphQL-Schema zu kompilieren, sodass die Syntax die tatsächliche Sift-Funktion ähnlicher widerspiegelt, ohne auf Zeichenfolgen angewiesen zu sein.

Abschluss

Zusammenfassend lässt sich sagen, dass die Verwendung von Sift.js in GraphQL eine flexible und leistungsstarke Möglichkeit bietet, beliebige Filterung zu implementieren. Es bringt die Vorteile der automatischen Abfrage, die normalerweise ORMs und bestimmten GraphQL-Anbietern vorbehalten sind, auf einfache JavaScript-Objekte in einer Liste, unabhängig von ihrer Quelle.

Durch die Bereitstellung einer generischen Filter-„Engine“ im GraphQL-Server mit einer flexiblen Abfragesprache, die auf jeden Typ angewendet werden kann, wird die Filterlogik auf den GraphQL-Client verlagert. Dies ermöglicht eine viel schnellere Iteration von Filtern und ermöglicht einen viel größeren Ausdrucksgrad in Filtern.

Ich würde gerne Ihre Gedanken und Erfahrungen mit der Implementierung der Filterung in GraphQL hören – teilen Sie sie in den Kommentaren unten!

Das obige ist der detaillierte Inhalt vonGraphQL: So aktivieren Sie die Filterung beliebiger Listen mit Sift.js.. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn