Skip to content

Search bundle

Introduction

The main purpose of the search bundle is to index data and search through them. You can index any entity, while the structure of the data will be saved very different. The advantage is a better search results, because the search engine can analyze data and could perform fuzzy search if needed. You can also weight your data, to tell what strings may be more important than other to find the specific resource.

Installation

bash
$ composer require enhavo/search-bundle
bash
$ yarn add @enhavo/search

To initialize the engine you have to fire the enhavo:search:init command.

bash
$ bin/console enhavo:search:init

Use the --force option, if you make changes that affect the index settings. Force will drop the index first and initialize, so you will have an empty index.

bash
$ bin/console enhavo:search:init --force

Search engines

Enhavo comes with some build in search engines. The database, elasticsearch and null engine. Both implements the Enhavo\Bundle\SearchBundle\Engine\SearchEngineInterface.

Database search engine

With the database search engine the content gets indexed into the database. So no extra service is needed. Therefor all words get separated from each other, simplified and then stored in a database table. In addition to that every word gets a score which determines the relevance to the other stored words.

The downside is, that if your amount of data getting bigger, this engine could get very slow.

By default, the search term has some addons. You have the possibility to get better search results by using the operators AND and OR. If you want to exclude a word you can put - in front of the word and if you want to get results with a whole phrase of words just but them into ".

Configure the database engine with:

# .env.local
SEARCH_DSN=database://null

Elastic search engine

With the elastic search engine you can index your data in an elasticsearch service and use their power and performance.

# .env.local
SEARCH_DSN=elastic://localhost:9200/elastic_search

Null engine

The null engine is for dev mode, if you want to turn off the indexing. It will always return an empty result.

# .env.local
SEARCH_DSN=null://null

Indexing

To index an entity we have to add some metadata in order to tell what we would like to index. By default, nothing will be indexed. In this example we define metadata for the entity App\Entity\Book.

yaml
enhavo_search:
    metadata:
        App\Entity\Book:
            index:
                title:
                    type: text
                    weight: 30
                    property: title
                description:
                    type: text
                    weight: 5
                    property: title
                chapters:
                    type: collection
                    property: chapters

The properties title and description are just a string, so we use the type text. The key is not enough to tell the name of the property, you have to configure it explicit with property. You can add als the weight options, to tell that title is more important then description.

Book has also oneToMany collection of chapters. So we use the type collection. Other types may be model for e.g. manyToOne relations.

You can find more types in the reference section or write your own type.

Note

You can only index root resources. They have to be defined as well.

Define root resources:

yaml
enhavo_search:
    index:
        classes:
            - App\Entity\Book
            - Enhavo\Bundle\PageBundle\Entity\Page
            - Enhavo\Bundle\ArticleBundle\Entity\Article

To index an entity, you can just inject the engine to any service and index the resource.

php
namespace App\Service;

use Enhavo\Bundle\PageBundle\Repository\PageRepository;
use Enhavo\Bundle\SearchBundle\Engine\SearchEngineInterface;

class MyService
{
    public function __construct(
        private SearchEngineInterface $searchEngine,
        private PageRepository $repository,
    ) {}
    
    public function index() 
    {
        $page = $this->repository->find(1);
        $this->searchEngine->index($page);
    }
}

If you want to index all root resources at once just execute the enhavo:search:index command.

bash
$ bin/console enhavo:search:index

On every enhavo_app.post_create, enhavo_app.post_update and enhavo_app.pre_delete the index will be automatically updated if it is a root resource.

Filter data

If you want to add additional filter data, that you will use during a search, you have to also provide metadata for it.

yaml
enhavo_search:
    metadata:
        App\Entity\Book:
        	filter:
                category:
                    type: text
                    property: category.name

Like in index, you have to use the property config explizit, the key will not be enough. Here we just use a text filter with property chain, because category itself is also an entity.

You can find more types in the reference section or write your own type.

To execute a search programmatically, you have to inject the search engine, create a filter and search. With the ResultConverter you can change the results and highlight the search term in the text.

php
namespace App\Service;

use Enhavo\Bundle\SearchBundle\Engine\SearchEngineInterface;
use Enhavo\Bundle\SearchBundle\Result\ResultConverter;
use Enhavo\Bundle\SearchBundle\Engine\Filter\Filter;

class MyService
{
    public function __construct(
        private SearchEngineInterface $searchEngine,
        private ResultConverter $resultConverter,
    ) {}
    
    public function search() 
    {
        $filter = new Filter();
        $filter->setTerm('Hello');
        $filter->setFuzzy(true);
        $filter->setLimit(10);
        
        $summary = $searchEngine->search($filter);
        
        foreach ($summary->getEntries() as $entry) {
            $entity = $entry->getSubject();
        }
        
        $results = $resultConverter->convert($summary, $term);
        
        foreach ($results as $entry) {
            $title = $entry->getTitle();
            $text = $entry->getText(); # highlighted text
            $entity = $entry->getSubject();
        }
    }
}

Auto suggest

With auto suggest, you can auto complete single words. In the background you have to perform a search and the engine will analyze the results for words that start with the search term. So a fuzzy search will be ignored here, because we search occurrences with an exact match.

php
class MyService
{
    public function suggest() 
    {
        $filter = new Filter(); 
        $filter->setTerm('Hel'); 
        $filter->setLimit(10); 
        
        $words = $searchEngine->suggest($filter); 
        
        foreach ($words as $word) { 
            
        } 
    }
}

Debugging

To check which data will be indexed you can use the debug:search:analyze command. It will need the FQCN and the id of the resource as arguments

bash
$ bin/console debug:search:analyze "Enhavo\Bundle\PageBundle\Entity\Page" 1

To check what are the results, you can use the enhavo:search command.

bash
$ bin/console enhavo:search hello

To check the auto suggest results, you use the enhavo:search:suggest

bash
$ bin/console enhavo:search:suggest hel