Let's go !
Here you will find the steps you have to follow to use ElasticSearch in your Symfony project.
I am using an Ubuntu system so the following console commands will be Linux based, so I apologize for non-Linux users.
1 - Installing Java
Installing ElasticSearch requires Java on your computer, so if you haven't installed it yet follow those steps.
Open a new terminal and type :
sudo apt-get update
Now you can install OpenJDK
sudo apt-get install openjdk-7-jre
Verify that your JRE is correctly installed with this command
java -version
You should see something like
Output of java -version
java version "1.7.0_79"
OpenJDK Runtime Environment (IcedTea 2.5.6) (7u79-2.5.6-0ubuntu1.14.04.1)
OpenJDK 64-Bit Server VM (build 24.79-b02, mixed mode)
2 - Downloading and installing Elasticsearch
In your terminal, type the following command to download the version 1.7.2 of ElasticSearch.
wget https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.7.2.deb
Then, install it by using the dpkg
command like this
sudo dpkg -i elasticsearch-1.7.2.deb
That's it ! ElasticSearch is now installed. But you must configure it and secure it correctly before using it. I recommend you to follow the Digital Ocean tutorial about ElasticSearch to getting started.
3 - Downloading and installing FOSElasticaBundle
Open a new terminal and in your project directory type the following command to dowload the lastest stable version :
composer require friendsofsymfony/elastica-bundle
In your AppKernel.php
file, enable the bundle by adding this line
<?php
//app/AppKernel.php
//...
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
//...
new FOS\ElasticaBundle\FOSElasticaBundle(),
);
//...
}
}
You must now configure the bundle. In your app/config/config.yml
add this basic configuration for one client with one single index.
#app/config/config.yml
fos_elastica:
clients:
default: { host: locahost, port: 9200 }
indexes:
app: ~
Here, an Elastica index called app
is available as a service with the key fos_elastica.index.app
Now we have to define our types.
By default, FOSElasticaBundle requires each type to be mapped. Each type defined is available as a service with the key fos_elastica.index.my_index.my_type
.
FOSElasticaBundle also requires a provider for each type that will notify when an object that maps to a type has been modified.
Here is a config example with Doctrine ORM :
#app/config/config.yml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
app:
types:
user:
mappings:
username: ~
firstname: ~
lastname: ~
email: ~
persistence:
driver: orm
model: Acme\AppBundle\Entity\User
provider: ~
listener: ~
finder: ~
Once your mapping is done, you have to populate your new index with the command
php app/console fos:elastica:populate
And... that's all ;)
You are now reasy to go with ElasticSearch on your Symfony Project !
4 - Simple search with FOSElasticaBundle
To make simple searches with ElasticSearch, we will need to create a search repository for our User entity and a search object that will help us to search more easily.
Let's start with our search object, UserModel.php
, which will contain properties from our app
index.
<?php
namespace Acme\AppBundle\Model;
class UserModel
{
protected $username;
protected $firstname;
protected $lastname;
protected $email;
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
return $this;
}
public function getFirstname()
{
return $this->firstname;
}
public function setFirstname($firstname)
{
$this->firstname = $firstname;
return $this;
}
public function getLastname()
{
return $this->lastname;
}
public function setLastname($lastname)
{
$this->lastname = $lastname;
return $this;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
}
Like all searches, we need a form to collect the informations that we want to search in our index.
NB : In our example, we will create a form with one field by indexed property, but you can also do a single input form which will search in all the indexed informations.
<?php
namespace Acme\AppBundle\Form;
use Acme\AppBundle\Model\UserModel;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
class UserSearchType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', TextType::class, ['required' => false])
->add('firstname', TextType::class, ['required' => false])
->add('lastname', TextType::class, ['required' => false])
->add('email', EmailType::class, ['required' => false])
->add('submit', SubmitType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => false,
'data_class' => 'Acme\AppBundle\Model\UserModel'
]);
}
}
And now our repository that will use our model :
<?php
namespace Acme\AppBundle\Entity\SearchRepository;
use FOS\ElasticaBundle\Repository;
use Acme\AppBundle\Model\UserModel;
use Elastica\Query\BoolQuery;
use Elastica\Query\Terms;
use Elastica\Query;
class UserRepository extends Repository
{
$query = new BoolQuery();
// This searchUser function will build the elasticsearch query to get a list of users that match our criterias
public function searchUser(UserModel $search)
{
if ($search->getUsername() != null && $search->getUsername() != '') {
$query->addMust(new Terms('username', [$search->getUsername()]));
}
if ($search->getFirstname() != null && $search->getFirstname() != '') {
$query->addMust(new Terms('firstname', [$search->getFirstname()]));
}
if ($search->getLastname() != null && $search->getLastname() != '') {
$query->addMust(new Terms('lastname', [$search->getLastname()]));
}
if ($search->getEmail() != null && $search->getEmail() != '') {
$query->addMust(new Terms('email', [$search->getEmail()]));
}
$query = Query::create($query);
return $this->find($query);
}
}
Important : In your User entity, you must specify that the above repository will be used for our elasticsearch searches. To do this, you must add the following annotation in your entity.
//src/Acme/UserBundle/Entity/User.php
use FOS\ElasticaBundle\Configuration\Search;
/**
* @ORM\Table()
* @Search(repositoryClass="Acme\UserBundle\Entity\SearchRepository\UserRepository")
*
*/
class User
{
// your entity...
}
And now the controller, here it is :
<?php
namespace Acme\UserBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\UserBundle\Model\UserModel;
class UserController extends Controller
{
public function indexAction(Request $request)
{
$userSearch = new UserModel();
$form = $this->createForm(UserSearchType::class, $userSearch);
$form->handleRequest($request);
$userSearch = $form->getData();
$elasticaManager = $this->get('fos_elastica.manager');
$results = $elasticaManager->getRepository('AcmeUserBundle:User')->searchUser($userSearch);
return $this->render('AcmeUserBundle:User:list.html.twig', [
'form' => $form->createView(),
'users' => $results
]);
}
}
You now have a working elasticsearch user search form which will return an user list based on your search criterias. It may be a bit complicated at first sight but with a some practice you'll be able to use Elasticsearch for all your searches.
Thanks for reading, I hope this article interested you !
As always if you have any questions do not hesitate to use the comment form.
Hugo.