Elasticsearch in Vapor: Getting Started
In this tutorial, you’ll set up a Vapor server to interact with an Elasticsearch server running locally with Docker to store and retrieve recipe documents. By Christian Weinberger.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Elasticsearch in Vapor: Getting Started
30 mins
- Getting Started
- Running Elasticsearch in Docker
- Using Elasticsearch
- Understanding Indexes and Documents
- Comparing Elasticsearch to Relational Databases
- Implementing CRUD
- Creating a Recipe
- Connecting Elasticsearch to Your API
- Reading a Recipe
- Updating a Recipe
- Getting All the Recipes
- Deleting a Recipe
- Searching Recipes
- Testing Search
- Adding Weighted Search
- Cleaning Up
- Where to Go From Here?
Testing Search
Build and run. Before testing search, run this command in Terminal to add another recipe:
curl -X "POST" "http://localhost:8080/api/recipes" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{
"name": "Banana Pie",
"ingredients": [
"Banana",
"Sugar",
"Milk",
"Vanilla",
"Egg yolks",
"Baked pastry shell"
],
"description": "If you like bananas, this is for you",
"instructions": "Mix all the ingredients. Enjoy!"
}'
Your Vapor server returns your new recipe, along with its Elasticsearch id
:
{"ingredients":["Banana","Sugar","Milk","Vanilla","Egg yolks","Baked pastry shell"],"id":"3ZQafnEBLVx2dJNClFTE","name":"Banana Pie","description":"If you like bananas, this is for you","instructions":"Mix all the ingredients. Enjoy!"}
Now that you have two recipes in your database, run this command to test if a partial search results in a tasty apple cake:
curl "http://localhost:8080/api/recipes/search?term=app"
Here’s what this search finds:
[{"ingredients":["Apple","Cake"],"id":"3JT0fXEBLVx2dJNCr1QE","name":"Apple Cake","description":"As easy as apple pie.","instructions":"Mix apple with cake."}]
Great, it found your apple cake! But there’s a reason why you added a banana pie recipe.
Run this command to search for pies:
curl "http://localhost:8080/api/recipes/search?term=pie"
The search returns both recipes:
[{"ingredients":["Apple","Cake"],"id":"3JT0fXEBLVx2dJNCr1QE","name":"Apple Cake","description":"As easy as apple pie.","instructions":"Mix apple with cake."},
{"ingredients":["Banana","Sugar","Milk","Vanilla","Egg yolks","Baked pastry shell"],"id":"3ZQafnEBLVx2dJNClFTE","name":"Banana Pie","description":"If you like bananas, this is for you","instructions":"Mix all the ingredients. Enjoy!"}]
As you can see, it returns two results: the apple cake in the first position and the banana pie in the second. This happens because both recipes contain the term pie in their data. It’s in the apple cake’s description
and in the banana pie’s name
and description
.
As you see, it’s a good idea to prioritize hits in the name
and ingredients
fields over matching terms in the description
field.
Adding Weighted Search
To make results in name
and ingredients
more relevant, your next step is to add the boost operator, ^, to your searchTerm
.
In RecipeFinderController.swift, replace let searchTerm = "(name:*\(term)* OR description:*\(term)* OR ingredients:*\(term)*)"
in searchHandler(_:)
with the following:
let searchTerm = "(name:*\(term)*^5 OR description:*\(term)* OR ingredients:*\(term)*^2)"
This boosts any matches in name
by five and in ingredients
by two.
Now build and run again, then retry the search for pie:
curl "http://localhost:8080/api/recipes/search?term=pie"
This time, the results put the banana pie recipe first, because the search term in the name
banana pie weighs more than the search term in the description
, “As easy as apple pie“.
[{"ingredients":["Banana","Sugar","Milk","Vanilla","Egg yolks","Baked pastry shell"],"id":"3ZQafnEBLVx2dJNClFTE","name":"Banana Pie","description":"If you like bananas, this is for you","instructions":"Mix all the ingredients. Enjoy!"},
{"ingredients":["Apple","Cake"],"id":"3JT0fXEBLVx2dJNCr1QE","name":"Apple Cake","description":"As easy as apple pie.","instructions":"Mix apple with cake."}]
Nice work — you did it! But before you go off to enjoy some delicious recipes, take a moment to clean up.
Cleaning Up
Stop your Xcode project to stop the Vapor server.
To stop the Elasticsearch container and remove it from your system, run these commands in Terminal:
docker stop elasticsearch
docker rm $(docker ps -a -q -f status=exited)
You named the container when you started it, which makes it easier to stop it — you don’t have to search for its ID
. The second command finds all exited
container processes and removes them.
To remove the Elasticsearch image, first find its IMAGE ID
:
docker images
Your output will look similar to this:
docker.elastic.co/elasticsearch/elasticsearch 7.6.2 f29a1ee41030 ...
Then use its IMAGE ID
to remove it. Be sure to replace the IMAGE ID
with your own:
docker rmi f29a1ee41030
Congratulations! You’ve learned how to set up a search with Elasicsearch in Vapor and how to clean it up when you’re done.
Where to Go From Here?
You can download the completed Recipe Finder Web API project using the Download Materials button at the top or bottom of this page.
In the completed project, you’ll find some implementations that this tutorial doesn’t cover. Instructions are in the project’s README.md file:
- DeleteIndexCommand: Useful to delete your index.
- ImportRecipesCommand: An example of how to import data into Elasticsearch via a command. If you deal with a lot of data in production, you should have a look at the Elasticsearch Bulk API instead.
Learn more about Elasticsearch by reviewing its comprehensive documentation at Elasticsearch Reference.
I hope you enjoyed this tutorial. If you have any questions or feedback, please join the forum discussion below.