Mugo Web main content.

Adding data to your Solr index for Ibexa DXP 4.x

By: Maxim Strukov | May 3, 2023 | Solr, Site performance, and ibexadxp

Sometimes, Apache Solr doesn’t index data in just the way you need for your custom search criteria. When you need to add data not indexed by default in the Solr search engine, the document field mapper should be created to fit your needs.

We will work through an example of how to set this up using the Matrix field type, which is not natively indexed to suit our purposes.

Solr uses documents as a unit of data that is indexed. We’re going to create the Mappers for the 2 main documents – Content and Location –  so we’ll be able to perform a search using the Ibexa Content Query or LocationQuery.

To index our additional data, we implement the ContentFieldMapper and LocationFieldMapper.

Let’s assume we have the Content Type week_record with the field daily_data of the Matrix field type. By default, Solr just adds the data of that field to the meta_content__text_t key for the full-text search. But we need a Solr field dedicated to storing the "daily_data" field content, which would be an array of the Matrix values (MultipleString type). The field mappers could then look like these.

Content Mapper:

use Ibexa\Contracts\Solr\FieldMapper\ContentFieldMapper;
use Ibexa\Contracts\Core\Persistence\Content;
use Ibexa\Contracts\Core\Repository\Repository;
use Ibexa\Contracts\Core\Search;
use Ibexa\FieldTypeMatrix\FieldType\Value as MatrixValue;
use Ibexa\FieldTypeMatrix\FieldType\Value\Row as MatrixRow;

final class DailyDataContentFieldMapper extends ContentFieldMapper
{
    private Repository $repository;

    public function __construct(Repository $repository)
    {
        $this->repository = $repository;
    }

    public function accept(Content $content): bool
    {
        // ContentType with ID 19 is Week Record
        return $content->versionInfo->contentInfo->contentTypeId == 19;
    }

    public function mapFields(Content $content): array
    {
        // Converting the Matrix data into the plain array
        $values = [];
        /* @var MatrixValue $dailyData */
        $dailyData = $this->repository->getContentService()->loadContent(
            $content->versionInfo->contentInfo->id
        )->getFieldValue('daily_data');

        /* @var MatrixRow $row */
        foreach ($serviceLanguage->getRows()->getArrayCopy() as $row) {
            if (!$row->isEmpty()) {
                $values[] = $row->getCells()['value'];
            }
        }

        return [
            new Search\Field(
                'daily_data',
                $values,
                new Search\FieldType\MultipleStringField()
            )
        ];
    }
}

 

Location Mapper:

namespace App\Search\Solr\FieldMapper;

use Ibexa\Contracts\Solr\FieldMapper\LocationFieldMapper;
use Ibexa\Contracts\Core\Persistence\Content\Location;
use Ibexa\Contracts\Core\Repository\Repository;
use Ibexa\Contracts\Core\Search;
use Ibexa\FieldTypeMatrix\FieldType\Value as MatrixValue;
use Ibexa\FieldTypeMatrix\FieldType\Value\Row as MatrixRow;

final class DailyDataLocationFieldMapper extends LocationFieldMapper
{
    private Repository $repository;

    public function __construct(Repository $repository)
    {
        $this->repository = $repository;
    }

    public function accept(Location $location): bool
    {
        $content = $this->repository->sudo(
            function () use ($location) {
                return $this->repository->getContentService()->loadContent(
                    $location->contentId
                );
            }
        );

        return $content->getContentType()->identifier === 'week_record';
    }

    public function mapFields(Location $location): array
    {
        // Converting the Matrix data into the plain array
        $values = [];
        $content = $this->repository->sudo(
            function () use ($location) {
                return $this->repository->getContentService()->loadContent(
                    $location->contentId
                );
            }
        );

        /* @var MatrixValue $serviceLanguage */
        $serviceLanguage = $content->getFieldValue('daily_data');

        /* @var MatrixRow $row */
        foreach ($serviceLanguage->getRows()->getArrayCopy() as $row) {
            if (!$row->isEmpty()) {
                $values[] = $row->getCells()['value'];
            }
        }

        return [
            new Search\Field(
                'daily_data',
                $values,
                new Search\FieldType\MultipleStringField()
            )
        ];

    }
}

 

Then we should specify the configuration for the mappers:

App\Search\Solr\FieldMapper\DailyDataContentFieldMapper:
    tags:
        - { name: ibexa.search.solr.field.mapper.content }

App\Search\Solr\FieldMapper\DailyDataLocationFieldMapper:
    tags:
        - { name: ibexa.search.solr.field.mapper.location }

! Important Note: in the Ibexa doc mentioned above, the tag name for the Content Mapper is ibexa.solr.field_mapper.content. This is obviously a mistake because the correct working one is ibexa.search.solr.field.mapper.content.

After we clear the cache and reindex the search engine:

php bin/console cache:clear
php bin/console ibexa:reindex

We can open the Solr dashboard and see the new daily_data_ms (_ms suffix means it’s the Multiple String type) index with the array of values we fetched from the daily_data Matrix field.

Now we can leverage that new index by including the Query Criterion like this when performing a search:

use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;

new Criterion\CustomField(
    'daily_data_ms', Criterion\Operator::CONTAINS, 'French'
);
loading form....
Thanks for reaching out. We will get back to you right away.