Browse Source

docs: Improve docs on paginated lists

Michael Bromley 2 years ago
parent
commit
8c222c1adb
1 changed files with 169 additions and 1 deletions
  1. 169 1
      docs/docs/guides/how-to/paginated-list/index.mdx

+ 169 - 1
docs/docs/guides/how-to/paginated-list/index.mdx

@@ -128,7 +128,7 @@ query {
         }
       }
       // highlight-end
-  ) {
+    }) {
     totalItems
     items {
       id
@@ -190,3 +190,171 @@ query {
 
 In the above example, we are querying the first 10 reviews, sorted by `createdAt` in descending order, and filtered to only
 include reviews with a rating between 3 and 5.
+
+## Advanced filtering
+
+Vendure v2.2.0 introduced the ability to construct complex nested filters on any PaginatedList query. For example, we could
+filter the above query to only include reviews for products with a name starting with "Smartphone":
+
+<Tabs>
+<TabItem value="Query" label="Query" default>
+
+```graphql
+query {
+  productReviews(
+    options: {
+    skip: 0
+    take: 10
+    // highlight-start
+    filter: {
+      _and: [
+        { text: { startsWith: "phone" } },
+        {
+          _or: [
+            { rating: { gte: 4 } },
+            { rating: { eq: 0 } }
+          ]
+        }
+      ]
+    }
+    // highlight-end
+    }) {
+    totalItems
+    items {
+      id
+      createdAt
+      product {
+        name
+      }
+      text
+      rating
+    }
+  }
+}
+```
+
+</TabItem>
+<TabItem value="Response" label="Response" >
+
+```json
+{
+  "data": {
+    "productReviews": {
+      "totalItems": 3,
+      "items": [
+        {
+          "id": "12",
+          "createdAt": "2023-08-23T12:00:00Z",
+          "product": {
+            "name": "Smartphone X"
+          },
+          "text": "The best phone I've ever had!",
+          "rating": 5
+        },
+        {
+          "id": "42",
+          "createdAt": "2023-08-22T15:30:00Z",
+          "product": {
+            "name": "Smartphone Y"
+          },
+          "text": "Not a very good phone at all.",
+          "rating": 0
+        }
+      ]
+    }
+  }
+}
+```
+
+</TabItem>
+</Tabs>
+
+In the example above, we are filtering for reviews of products with the word "phone" and a rating of 4 or more,
+or a rating of 0. The `_and` and `_or` operators can be nested to any depth, allowing for arbitrarily complex filters.
+
+## Filtering by custom properties
+
+By default, the `ListQueryBuilder` will only allow filtering by properties which are defined on the entity itself.
+So in the case of the `ProductReview`, we can filter by `rating` and `text` etc., but not by `product.name`.
+
+However, it is possible to extend your GraphQL type to allow filtering by custom properties. Let's implement filtering
+but the `product.name` property. First, we need to manually add the `productName` field to
+the `ProductReviewFilterParameter` type:
+
+```ts title="src/plugins/reviews/api/api-extensions.ts"
+import gql from 'graphql-tag';
+
+export const adminApiExtensions = gql`
+# ... existing definitions from earlier example omitted
+
+// highlight-start
+input ProductReviewFilterParameter {
+  productName: StringOperators
+}
+// highlight-end
+`;
+```
+
+Next we need to update our `ProductReviewService` to be able to handle filtering on this new field using the
+[customPropertyMap](/reference/typescript-api/data-access/list-query-builder/#custompropertymap) option:
+
+```ts title="src/plugins/reviews/services/product-review.service.ts"
+import { Injectable } from '@nestjs/common';
+import { InjectConnection } from '@nestjs/typeorm';
+import { ListQueryBuilder, ListQueryOptions, PaginatedList, RequestContext } from '@vendure/core';
+
+import { ProductReview } from '../entities/product-review.entity';
+
+@Injectable()
+export class ProductReviewService {
+    constructor(
+        private listQueryBuilder: ListQueryBuilder,
+    ) {}
+
+    findAll(ctx: RequestContext, options?: ListQueryOptions<ProductReview>): Promise<PaginatedList<ProductReview>> {
+        return this.listQueryBuilder
+            .build(ProductReview, options, {
+                // highlight-next-line
+                relations: ['product'],
+                ctx,
+                // highlight-start
+                customPropertyMap: {
+                    productName: 'product.name',
+                }
+                // highlight-end
+            })
+            .getManyAndCount()
+            .then(([items, totalItems]) => ({ items, totalItems }));
+    }
+}
+```
+
+Upon restarting your server, you should now be able to filter by `productName`:
+
+```graphql
+query {
+  productReviews(
+    options: {
+      skip: 0
+      take: 10
+      // highlight-start
+      filter: {
+        productName: {
+          contains: "phone"
+        }
+      }
+      // highlight-end
+  }) {
+    totalItems
+    items {
+      id
+      createdAt
+      product {
+        name
+      }
+      text
+      rating
+    }
+  }
+}
+```