Elasticsearch is a useful tool that allows you to store, search and analyze huge volumes of data quickly and in real-time, providing answers in milliseconds. You can think of Elasticsearch as a server that can process JSON requests and give you back JSON data according to your parameters. Elasticsearch is extremely useful for applications that rely heavily on a search platform for the access, retrieval, and reporting of data, as well as for effective and accurate searches.
In this article, I will explain to you how to interact with your Elasticsearch server in your Symfony 5 project.
Requirements
To follow this article, we'll assume that you have already an Elasticsearch server up and running. In our case, we will use a local instance of Elasticsearch v7.12 running through Docker and available at http://localhost:9200/
.
Keeping this in mind, let's get started with the tutorial.
1. Install FOSElasticaBundle
In order to interact with your Elasticsearch server in Symfony, the best way to do it is through the FOSElasticaBundle. This bundle helps you to:
- Integrates the Elastica library into a Symfony environment (Elastica by itself is a PHP client for Elasticsearch).
- Use JmsSerializer or Symfony Serializer to convert between PHP objects and Elasticsearch data.
- Index configuration for Elasticsearch, or send data without configuration to use the dynamic mapping feature of Elasticsearch
- Listeners for Doctrine events for automatic indexing
To date, the support for Elasticsearch v7 is only available in the beta version of the FOSElasticaBundle (v6.0.0-beta3). To install this version, run the following command (note that till the date, the v6 release isn't available, if it's available, install that one instead):
composer require friendsofsymfony/elastica-bundle v6.0.0-beta3
Once the installation finishes, proceed with the configuration steps. For more information about this bundle, please visit the official repository at Github here.
2. Configure access URL
In your .env file, you will find a new property that will indicate the endpoint of Elasticsearch:
# project/.env
###> friendsofsymfony/elastica-bundle ###
ELASTICSEARCH_URL=http://localhost:9200/
###< friendsofsymfony/elastica-bundle ###
In case it isn't, define it and replace its value with the access endpoint.
3. Create example Entity
The idea of the search engine in this example is to search some articles with a given keyword. The articles are stored on a MySQL database and Doctrine is used as ORM, the entity looks like this:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Articles
*
* @ORM\Table(name="articles")
* @ORM\Entity(repositoryClass="App\Repository\ArticlesRepository")
*/
class Articles
{
/**
* @var int
*
* @ORM\Column(name="id", type="bigint", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="slug", type="string", length=255, nullable=false)
*/
private $slug;
/**
* @var string|null
*
* @ORM\Column(name="preview", type="string", length=255, nullable=true, options={"default"="NULL"})
*/
private $preview;
/**
* @var string
*
* @ORM\Column(name="content", type="text", length=0, nullable=false)
*/
private $content;
/**
* @var string|null
*
* @ORM\Column(name="tags", type="string", length=255, nullable=true, options={"default"="NULL"})
*/
private $tags;
}
The articles will be populated in Elasticsearch by indicating this entity later. Be sure to have some articles persisted in your database.
4. Configure indexes
In Elasticsearch, an index can be thought of as an optimized collection of documents and each document is a collection of fields, which are the key-value pairs that contain your data. By default, Elasticsearch indexes all data in every field and each indexed field has a dedicated, optimized data structure. In this case, we can create an index for the articles entity, that will store the defined properties in my entity, using the Articles entity as the model. You can define all of your indexes in the fos_elastica.yaml
file like this:
# app/config/packages/fos_elastica.yaml
# Read the documentation: https://github.com/FriendsOfSymfony/FOSElasticaBundle/blob/master/doc/setup.md
fos_elastica:
clients:
default: { url: '%env(ELASTICSEARCH_URL)%' }
indexes:
articles:
properties:
name: ~
slug: ~
preview: ~
content: ~
tags: ~
persistence:
driver: orm
model: App\Entity\Articles
5. Create indexes
Now, you need to create the indexes in Elasticsearch. This can be easily done running the following command:
php bin/console fos:elastica:create
6. Populate data
Finally, you can populate the information of the table in Elasticsearch running the following command:
php bin/console fos:elastica:populate
This will generate an output similar to the following one:
Resetting articles
0/1480 [>---------------------------] 0%
200/1480 [===>------------------------] 13%
300/1480 [=====>----------------------] 20%
500/1480 [=========>------------------] 33%
600/1480 [===========>----------------] 40%
800/1480 [===============>------------] 54%
900/1480 [=================>----------] 60%
1100/1480 [====================>-------] 74%
1200/1480 [======================>-----] 81%
1400/1480 [==========================>-] 94%
1480/1480 [============================] 100%
Populating articles Refreshing articles
As you can see, in the articles table of my database, there are 1480 Articles entities that have been now indexed in Elasticsearch, so we can now run some queries on the engine within the controllers or services of the Symfony project.
7. Register finder
There are multiple ways in which you can run some queries in Elasticsearch through Elastica, you can check the detailed information about them on this page. In this example, we are going to proceed with the most basic example, so you can research more about advanced searching later.
The first thing you need to do is to register the finder for your index in your services.yaml
. Depending on your needs, you may register the finder on a single controller or service like this:
# app/config/services.yaml
App\Controller\ExampleController:
tags: [controller.service_arguments]
bind:
# replace "articles" with the name of your index
# and replace the class of the Finder
FOS\ElasticaBundle\Finder\TransformedFinder $articlesFinder: '@fos_elastica.finder.articles'
So you can obtain the service only in the specific controller:
<?php
// app/src/Controller/ExampleController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use FOS\ElasticaBundle\Finder\TransformedFinder;
use Elastica\Util;
class ExampleController extends AbstractController
{
public function someAction(TransformedFinder $articlesFinder) : Response
{
// do something with $articlesFinder
}
}
Alternatively, you may bind the finder everywhere you need it with:
# app/config/services.yaml
services:
_defaults:
# replace "articles" with the name of your index
# and replace the class of the Finder
bind:
FOS\ElasticaBundle\Finder\TransformedFinder $articlesFinder: '@fos_elastica.finder.articles'
8. Running a search
As the last step, you may now test if everything is working as expected running some queries. This example will run a query on the articles index and will return all the entities that match your search terms. In this case, it will run inside a controller:
<?php
// app/src/Controller/ExampleController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use FOS\ElasticaBundle\Finder\TransformedFinder;
use Elastica\Util;
class ExampleController extends AbstractController
{
public function someAction(TransformedFinder $articlesFinder) : Response
{
$search = Util::escapeTerm("Bleu de channel parfum");
$result = $articlesFinder->findHybrid($search, 10);
dump($result);
return $this->render("views/empty.html.twig");
}
}
The dumped content looks like this, so all the articles that match with the search "Bleu de channel parfum":
The documentation of Elasticsearch and Elastica is quite extensive, so don't forget to check out the documentation for more advanced examples of how to interact with Elasticsearch in your Symfony 5 application.
Happy coding ❤️!