Ionic 4 + AppSync: Add ElasticSearch to GraphQL API - Part 6
In this tutorial we're going to configure Amazon ElasticSearch Service to index the cards in our app so we can perform searches on them through our GraphQL API.
As you've seen in previous parts, adding AWS backend resources to our GraphQL API is very easy and it's no different for ElasticSearch!
This tutorial is split up into these parts
Part 1 - Introduction to GraphQL & AWS AppSync
Part 2 - Create a GraphQL API
Part 3 - Add Ionic pages and GraphQL queries
Part 4 - Use GraphQL mutations in Ionic
Part 5 - Set up Authentication with Amazon Cognito
Part 6 - Add ElasticSearch to GraphQL API (this post)
What is ElasticSearch?
ElasticSearch is an open-source search and analytics engine which enables you to do different types of searches on your data. It's built to handle big data and is blazing fast!
In Part 4 of this tutorial I showed you how to search for cards through the GraphQL API. The query that we used to search for specific cards is just scanning the entire DynamoDB table, retrieving all data and then filtering by our keyword.
Now imagine this table has millions of items... Ooops! 😱 But don't worry, we can send all the card data to ElasticSearch where it will be indexed for searching.
Amazon has a managed service implementation for ElasticSearch, so we don't have to worry about maintaining ElasticSearch ourselves. All we have to do is configure our AWS backend to stream the data from the DynamoDB table to ElasticSearch where it will be indexed.
By now you probably won't be surprised to find out that it's very easy to do this with Amplify, so let's go ahead and see how that's done!
Side note: Amazon ElasticSearch Service is free to get started with. Check the pricing.
Configure ElasticSearch
All we need to do to configure ElasticSearch is add the @searchable directive to the Card type in our GraphQL schema.
type Card @model @searchable @auth(rules: [{ allow: owner }]) {
id: ID!
question: String!
answer: String
deck: Deck! @connection(name: "DeckCards")
}
The @searchable directive is responsible for streaming the data of an @model object type to ElasticSearch and configuring search resolvers for searching that data.
Now we need to push this change to AWS so it can set up the DynamoDB stream to send data to ElasticSearch.
$ amplify push
The CLI will ask you some questions about code generation, you can choose as below.
? Do you want to update code for your updated GraphQL API Yes
? Do you want to generate GraphQL statements (queries, mutations and subscription) based on your schema types? This will overwrite your current graphql queries, mutations and subscriptions Yes
It might take a while for the backend to create the resources, but when it's done, you should have a new query on your GraphQL API.
You can check the queries in this file: src/graphql/queries.graphql. It should now contain the SearchCards
query.
query SearchCards(
$filter: SearchableCardFilterInput
$sort: SearchableCardSortInput
$limit: Int
$nextToken: Int
) {
searchCards(
filter: $filter
sort: $sort
limit: $limit
nextToken: $nextToken
) {
items {
id
question
answer
deck {
id
name
}
}
nextToken
}
}
We can now use this query in our Ionic app to search for cards.
Modify Home Page
Let's use an Ionic Alert to prompt our users to input search keywords when they want to start the quiz.
In home.page.ts we are going to import Ionic's AlertController
and add it to the constructor.
import { ModalController, AlertController } from '@ionic/angular';
constructor(private modalController: ModalController,
private router: Router,
private alertController: AlertController) {
}
Next, add the method promptQuiz
to ask the user to input search keywords to filter the cards for the quiz.
async promptQuiz() {
const alert = await this.alertController.create({
header: 'Which cards?',
inputs: [
{
name: 'filter',
type: 'text',
placeholder: 'Type a word to filter the cards'
}
],
buttons: [
{
text: 'Cancel',
role: 'cancel',
cssClass: 'secondary'
},
{
text: 'Start Quiz',
handler: async data => {
await this.startQuiz(data.filter);
}
}
]
});
await alert.present();
}
We need to modify startQuiz
to send the filter
to the modal.
async startQuiz(filter) {
const modal = await this.modalController.create({
component: QuizPage,
componentProps: { filter }
});
return await modal.present();
}
And finally, modify home.page.html to call promptQuiz
when the Quiz Me button is clicked.
<ion-button fill="outline" color="light" (click)="promptQuiz()">
Quiz Me
<ion-icon slot="end" name="albums"></ion-icon>
</ion-button>
Modify Quiz Page
Add an import for NavParams
and add it to the constructor.
import { APIService } from "../api.service";
constructor(
public modalController: ModalController,
private navParams: NavParams,
public apiService: APIService
) { }
Next, change the call to apiService
to call the new SearchCards
method and pass in the filter
.
ngOnInit() {
this.apiService
.SearchCards({
question: {
match: this.navParams.get('filter')
}
})
.then(query => this.cards = query.items);
}
That's it, we are now using ElasticSearch to search through our cards.
Test app
Start the app, add some new cards to it and test if the filtering works.
$ ionic serve
What's next?
I haven't decided yet what to include in the next tutorial, so if you have anything specific you'd like me to look at, let me know in the comments.
If you'd like to know more about databases on AWS and which one to use when, I highly recommend you watch these talks from the recent AWS re:Invent conference: