How to manually index entities in Elasticsearch using FOSElasticaBundle in Symfony 5

There will be some cases, where you will need to index your entities in Elasticsearch when you need it specifically, not every time an entity gets updated and so, as this is the default behavior of FOSElasticaBundle. To make it even clearer, imagine the following case, where you have an entity named Articles. When you create an article, the information gets indexed automatically in Elasticsearch thanks to the bundle, great! When the article gets updated, it's updated in Elasticsearch as well and when it's deleted from your application, it gets removed from Elasticsearch as well. FOSElasticaBundle it's just great! The problem comes in some scenarios when you need this behavior, but you need to set specifically in which lines of code the information should be indexed, updated, or removed. For example, if the Article entity stores the number of visits that the article has and every time a new visitor visits the article, this number gets updated, therefore the entity is updated as well and the information in Elasticsearch is updated as well unnecessarily. Ideally, the information should be reindexed when the author of the article updates the content of the article only, not when any of the fields in the entity gets updated.

With custom logic, you should be able to tell when the document in Elasticsearch should be updated and that's what I'm going to explain you in this article for Symfony 5.

1. Disable default automatic document indexing

As I mentioned previously, by default FOSElasticaBundle automatically indexes as documents your entities on the doctrine lifecycle events. So a request will be executed to Elascticsearch when you:

  • insert
  • update
  • delete

Any entity that is associated with an index in Elasticsearch. As the point of doing it manually is to control by ourselves how this is handled, we need to disable the default behavior of the automatic indexing. This can be easily done by providing an object with the enabled property set to false in the persistence.listener key of your index (in our case, our index is named articles):

# app/config/packages/fos_elastica.yaml
fos_elastica:
    clients:
        default: { url: '%env(ELASTICSEARCH_URL)%' }
    indexes:
        articles:
            properties:
                name: ~
                slug: ~
                preview: ~
                content: ~
                tags: ~
            persistence:
                driver: orm
                model: App\Entity\Articles
                # Disable the default persistence listener!
                listener: { enabled: false }

This will cause that when you try to insert, update or delete any Articles entity in your database, the Elasticsearch index won't be updated, so basically, for now, the information will be outdated if you don't update the information manually, but that's what we are going to explain in this article.

2. Wire object persister of your index

The first thing you need to do is to register the object persister of your index in your services.yaml. Depending on your needs, you may register the persister 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
            FOS\ElasticaBundle\Persister\ObjectPersister $articlesPersister: "@fos_elastica.object_persister.articles"

Alternatively, you may bind the persister everywhere you need it with:

# app/config/services.yaml
services:
    _defaults:
        bind:
            # replace "articles" with the name of your index
            FOS\ElasticaBundle\Persister\ObjectPersister $articlesPersister: "@fos_elastica.object_persister.articles"

3. Manually indexing data on Elasticsearch

The idea and goal of this tutorial are of how to index the data of your entities in Elasticsearch instead of doing it automatically as the FOSElasticaBundle does by default. The regular place to follow the same workflow but deciding where to do it by yourself is in the default CRUD of an Entity inside a controller. In this case, we will use the 3 methods of a default controller that allows to create, update and delete an Articles entity in the application. All you need to do is to import the ObjectPersister that uses the index that you need (registered in step 2) and call any of the necessary methods to create, update or remove the document in Elasticsearch:

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

// My Entity, in this Case Articles
use App\Entity\Articles;

// Import ObjectPersister
use FOS\ElasticaBundle\Persister\ObjectPersister;

class ExampleController extends AbstractController
{
    // Example #1. Creating a new Entity
    public function new(
        Request $request,
        ObjectPersister $articlesPersister
    ): Response
    {
        $article = new Articles();
        
        // Register document in Elasticsearch
        $articlesPersister->insertOne($article);
    }

    // Example #2. Updating an existing Entity
    public function edit(
        Request $request, 
        Articles $article,
        ObjectPersister $articlesPersister
    ): Response
    {
        // Update document in Elasticsearch
        // Note that to update, the document needs to exist!
        $articlesPersister->replaceOne($article);
    }
    
    // Example #3. Deleting an entity
    public function delete(
        Request $request,
        Articles $article,
        ObjectPersister $articlesPersister
    ): Response
    {
        // Delete document from Elasticsearch
        $articlesPersister->deleteOne($article);
    }
}

And that's how you can easily index your entities manually in Elasticsearch through the FOSElasticaBundle.

Happy coding ❤️!

This could interest you

Become a more social person