Filter and Result (AWS Amplify, AppSync, GraphQL)
Tags: aws, amplify, appsync, graphql
Published: 2022-07-18 04:37:08

In this post, we'll try to understand how to search/filter and result work in AWS Amplify and AppSync.

## Prerequisites: 

1/ GraphQL [Learn](https://graphql.org/learn/)
2/ Data modeling 
If you don't know about data modeling check out my previous blog about [data-modeling](https://dev.to/arifulhasantotul/data-modeling-aws-amplify-appsync-graphql-4l0n)

If you've no idea about any prerequisites, I recommend learning them highly.

# Directives
`@modal @auth @hasOne @hasMany @manyToMany @searchable @primaryKey @index`

> `@searchable`

- Needs to setup ElasticSearch (We'll not do this in this post)
- Active search and sorting at the same time.

> `other directives`

- I've briefly described other directives in the data-modeling blog. If you don't know about other directives [click here](https://dev.to/arifulhasantotul/data-modeling-aws-amplify-appsync-graphql-4l0n) to learn.

# Data Types

> 1. Scalar type: id, name, date_of_birth, got_job, gpa etc. (which has scalar value like ID, String, Float, Int, Boolean, AWSDate etc.)
> 2. Object type: Status, Student ( which is a javascript object)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l45arsmzyosjk19fj4tv.png)

# Supported Search Operations

| Data Type | Supported operations           |
| ------------- |:-------------:|
| String | ne, eq, match, matchPhrase, matchPhrasePrefix, multiMatch, exists, wildcard, regexp |
| Int | ne, gt, lt, gte, lte, eq, range |
| Float | ne, gt, lt, gte, lte, eq, range |
| Boolean | ne, eq |
| Enum | ne, eq, match, matchPhrase, matchPhrasePrefix, multiMatch, exists, wildcard, regexp |
| Object | not supported |

> ne (not equal), eq (equal), gt (greater than), lt (lesser than), gte (greater than or equal), lte (lesser than or equal)

# Without `@searchable` directive

We can do filter with or without `@searchable` directive.We'll get **filter** argument for every list resolvers like (listStudents, listStatuses etc.)

> - Go to the DOCS or Documentation Explorer, and now search for the list resolver name you want to apply the filter. Click on any list resolver you want to apply filter. Here we're clicking on the **liststudents** resolver to apply the filter.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z2ms59gxbuh71lys3gof.png)


> - Inside **listStudents** we get the **filter argument**. Click on the filter argument to see what conditions we can apply to filter out data.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1paagnfgnwmv4zql8web.png)


> - Inside **filter argument**, we can see multiple fields that can use to filter out data. If we pay attention to argument fields, we see that all the filter options are available only for the **scalar field**. No object fields are available like **status**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8al11wqe7vxkwjnqxi8s.png)

# Dummy data for testing:

We've created some data to test filtering. If you don't know how to add a document/data in database or how hasOne, hasMany, manyToMany relation works, I suggest you to check out the [data-modeling](https://dev.to/arifulhasantotul/data-modeling-aws-amplify-appsync-graphql-4l0n) blog. Now we'll apply filter on this below data

```
{
  "data": {
    "listStudents": {
      "items": [
        {
          "id": "9f928a7a-f096-4ad8-b0cc-5b354dde6b18",
          "name": "Raisa",
          "email": "raisa@gmail.com",
          "gpa": 3.7,
          "got_job": true,
          "statusID": "b3a4a939-50ed-4d3c-a21e-8048834391f5",
          "status": {
            "is_enrolled": true,
            "is_passed": false
          },
          "date_of_birth": "1998-07-17",
          "exam_completed": 2
        },
        {
          "id": "31092e63-e421-4bbc-afe8-f2092e309442",
          "name": "Ashim",
          "email": "ashim@gmail.com",
          "gpa": 3.2,
          "got_job": false,
          "statusID": "2cc614a4-0bd1-4a8d-80a8-bc0c8372788b",
          "status": {
            "is_enrolled": false,
            "is_passed": true
          },
          "date_of_birth": "1998-04-15",
          "exam_completed": 3
        },
        {
          "id": "9a3a92df-e4be-4258-ae02-502e0bfd3745",
          "name": "Hasan",
          "email": "hasan@gmail.com",
          "gpa": 3.4,
          "got_job": false,
          "statusID": "840a91da-75ce-4cd5-8a41-58081763a933",
          "status": {
            "is_enrolled": true,
            "is_passed": false
          },
          "date_of_birth": "1998-03-22",
          "exam_completed": 1
        },
        {
          "id": "ff173ed6-5749-4a54-903b-025675f828e5",
          "name": "Minhaj",
          "email": "minhaj@gmail.com",
          "gpa": 3.7,
          "got_job": true,
          "statusID": "e1f9ccc1-132f-443a-9f37-821ed634cfc1",
          "status": {
            "is_enrolled": true,
            "is_passed": false
          },
          "date_of_birth": "1998-04-21",
          "exam_completed": 3
        },
        {
          "id": "bc70feab-da91-4ba5-a626-ee99c9f4c4a0",
          "name": "Totul",
          "email": "totul@gmail.com",
          "gpa": 3.4,
          "got_job": false,
          "statusID": "ca9064ff-5c2f-4634-8dca-d12f10acd1db",
          "status": {
            "is_enrolled": false,
            "is_passed": true
          },
          "date_of_birth": "1998-03-29",
          "exam_completed": 1
        },
        {
          "id": "514b1115-7c95-4037-884f-4d5eae58dc4b",
          "name": "Mahadhi",
          "email": "mahadhi@gmail.com",
          "gpa": 3.6,
          "got_job": true,
          "statusID": "ec2d4c20-4022-4084-9d52-19ec2cd18503",
          "status": {
            "is_enrolled": false,
            "is_passed": true
          },
          "date_of_birth": "1993-07-21",
          "exam_completed": 4
        },
        {
          "id": "7054f363-3f3c-4343-9dff-f7e4491a797f",
          "name": "Shuvo",
          "email": "shuvo@gmail.com",
          "gpa": 2.5,
          "got_job": false,
          "statusID": "1958dea4-59b4-4766-b725-476747b8b4c3",
          "status": {
            "is_enrolled": false,
            "is_passed": true
          },
          "date_of_birth": "1998-02-15",
          "exam_completed": 2
        },
        {
          "id": "bf74457b-a1e4-4715-880f-1425bedaaa09",
          "name": "Ayesha",
          "email": "ayesha@gmail.com",
          "gpa": 3.5,
          "got_job": false,
          "statusID": "3a86016e-b0d6-479f-a1d1-1effa0846457",
          "status": {
            "is_enrolled": true,
            "is_passed": false
          },
          "date_of_birth": "1998-07-17",
          "exam_completed": 2
        },
        {
          "id": "a2e5e97d-5f8d-493e-ba78-1701ca08d58e",
          "name": "Arif",
          "email": "arif@gmail.com",
          "gpa": 3.51,
          "got_job": true,
          "statusID": "840a91da-75ce-4cd5-8a41-58081763a933",
          "status": {
            "is_enrolled": true,
            "is_passed": false
          },
          "date_of_birth": "1998-03-27",
          "exam_completed": 1
        },
        {
          "id": "485b3f5a-8709-4902-82a6-4997c6a0839a",
          "name": "Navin",
          "email": "navin@gmail.com",
          "gpa": 3.4,
          "got_job": false,
          "statusID": "0e437cee-92f6-418a-bf90-af74a67a9943",
          "status": {
            "is_enrolled": true,
            "is_passed": false
          },
          "date_of_birth": "1993-06-24",
          "exam_completed": 3
        }
      ]
    }
  }
}
```

## Using of filter argument: Filtering students by got_job

> 1. **got_job{ eq: $job }: Go to the documentation explorer and click on the **got_job** field, we'll see some conditions with types like (eq: Boolean, ne: Boolean, etc.) 

> 2. **$job: Boolean**: Specify the input value type because we can only match with boolean data according to **eq: Boolean**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hmrf52nmuwsawk4elkhd.png)

## Using of filter argument: Filtering students by gpa

> 1. **gpa{ ge: $point }**: Go to the documentation explorer and click on the **gpa** field, we'll see some conditions with types like (ne: Float, ge: Float, eq: Float, etc.) We're using **ge** condition and its value is **Float**. That's why we've to take the float value

> 2. **$point: Float**: Specify the input value type because we can only match with float number according to **ge: Float**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7vwfrhui95ppja313mm3.png)

## Using of nested filter argument: Filtering students by gpa and got_job
> Here we can use **and / or / not** operations. By default, **and** operation is applied on the query.

> **got_job : {eq: $job}, {exam_completed: {le: $exam}}**: Data will show when got_job and exam_completed both satisfy conditions
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gxmybcgbys7q94w19h6s.png)

## Using of nested filter argument: Filtering students by got_job and exam_completed/email
> **got_job : {eq: $job}, or: [{exam_completed: {gt: $exam}}, {email: {eq: $email}}]**: Data will show when got_job and exam_completed/email both satisfy conditions.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pn05m44cyyndpu65ziqs.png)


## Using of limit & nextToken argument: Limiting data for pagination

> **$limit: Int**: 
1. The limit argument takes an Integer value and gives a certain amount of data. 
2. Here we're providing the limit value 2. So the query will return a maximum of 2 documents/data.

> **$token: String**: 
1. The nextToken argument takes a String value and gives the upcoming documents/data. 
2. Here we're providing an empty string in the argument because we need all data available in the first. 
3. We're also getting a nextToken string from the query field that will provide us with the upcoming data. If the nextToken value is **null**, that means we've no data left.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mzwcbjhmbq6ezbo72xki.png)

> **$limit: Int**: 
1. Here we're providing the limit value 2. So the query will return a maximum of 2 documents/data.

> **$token: String**: 
1. Get the nextToken string from the previous query and provide it in the nextToken argument field to get upcoming documents/data.
2. We're also getting a new nextToken string from the query field that will provide us with the upcoming data.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w72e22r256j5acrd5b9b.png)

## Using of filter & limit & nextToken arguments:
> We've already described all the conditions briefly, so now we will only visualize/understand the process.
> **Step-1| nextToken: ""**:
If the nextToken is an empty string, the limit process will start from 1st item/element.

> **Step-2| limit: 2**:
The limit process will select 1st & 2nd item/element.

> **Step-3| filter: condition**:
The filter process will check the 1st & 2nd item/element. If the filter condition satisfy, we will get the matched element. Here the condition matches with one element.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4hunxyxiktwl5envwr6h.png)

> **Step-1| nextToken: "<tokenString>"**:
If we provide a nextToken which we got from the previous query. The limit process will start from the upcoming item. Here the upcoming item starts from 3rd item/element.

> **Step-2| limit: 2**:
The limit process will select 3rd & 4th item/element.

> **Step-3| filter: condition**:
The filter process will check the 3rd & 4th item/element. If the filter condition satisfy, we will get the matched element. Here the condition matches with both elements.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gfafikp6zcle6yqr6nuc.png)

### Que: Here we are filtering data based on the student scalar field. What if we want to filter data based on student status (student object field)?

### Solution:

- Step-1: The connection between the Student model and the Status model is hasOne relation. We've to create a bi-directional relationship between the Student model and the Status model to get the Student data inside the Status model. If you don't understand relations between different models, check out this [blog](https://dev.to/arifulhasantotul/data-modeling-aws-amplify-appsync-graphql-4l0n) or feel free to comment below.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rlv472j5xf2r0z9xdue1.png)

- Step-2: We've created a bi-directional relationship between both models. But if we don't update documents one by one, we'll get a null value. To get the student data inside the status table, we've to update every status providing status id in $id field and student id in $student_id field.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5sqis2aqt3s5pj54zzq5.png)

- Step-3: Get filtered data of enrolled students.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vovcqqh66iwwtl5aem0s.png)

## With `@searchable` directive

By adding @searchable we get some extra operations
- aggregation values, such as sum, average, min, max terms
- retrieve total search result count
- sort the search results across one or multiple fields

To work the @searchable directive properly, we've to setup OpenSearch, and that's for another blog

That's it for now. Thanks for your patience. If this blog help you please give a react.