Pull to refresh

DoctrineSolrBundle — поиск по Doctrine entity на базе Solr в Symfony2/3

Reading time 4 min
Views 3.2K

DoctrineSolrBundle


Добрый день, хочу представить свой symfony 2 бандл для автоматической синхронизации Doctrine entity в Solr и последующим поиском. Бандл предназначен для работы с Solr на уровне Doctrine entity и позволяет избежать написания низкоуровневых запросов в solr. Процесс установки и подробную документацию можно посмотреть на github.


Возможности


Реализованы основные (не все) возможности поиска стандартного парсера запросов Solr :



Также реализована поддержка SuggestComponent


Пример конфигурации


После установки для начала работы требуется настроить бандл в config.yml. Пример минимальной конфигурации бандла:


mdiyakov_doctrine_solr:
    indexed_entities:
        page:
            class:  AppBundle\Entity\Page
            schema: page
            config:
                - { name: type, value: page }
    schemes:
        page:
            document_unique_field: { name: 'uid' }
            config_entity_fields:
                - {  config_field_name: 'type', document_field_name: 'type', discriminator: true }
            fields:
                - {  entity_field_name: 'id', document_field_name: 'entity_id', field_type: int, entity_primary_key: true }
                - {  entity_field_name: 'textField', document_field_name: 'text', priority: 100, suggester: true }

Как результат после каждого создания, обновления "AppBundle\Entity\Page" сущности, будут прондексированы поля "id" и "textField", а также конфигурационное поле "type". В случае удаления экземпляра сущности соответствущий solr документ будет удален.


В "schemes" секции описываютс схемы индексации. В схему индексации входит описание полей сущности (fields), конфигурационных полей (config_entity_fields) которые должны быть проиндексированы в solr. А также "document_unique_field" указывающее уникальное поле для schemes.xml в solr. Помимо этого есть необязательное поле client в случае если надо использовать несколько solr core для разных схем индексации (подробнее об этом тут). По сути каждая схема в schemes отражает конкретное solr core.


Конфигурационное поле это поле которое задается в секции "indexed_entities" в рамках конфига сущности. Его можно использовать для индексации параметров заданных в parameters.yml, например:


....            
class:  AppBundle\Entity\Page
schema: page
config:
        - { name: app_version, value: %app_version% }
        - { name: host, value: %host% }
       ...
....

Для каждой индексируемой сущности должно быть задано как минимум одно конфигурационное поле с уникальным значением относительно всех индексируемых сущностей обозначенное как discriminator: true в schemes. Например:


mdiyakov_doctrine_solr:
    indexed_entities:
        page:
            class:  AppBundle\Entity\Article
            schema: page
            config:
                - { name: type, value: article }
    news:
            class:  AppBundle\Entity\News
            schema: page
            config:
                - { name: type, value: news }
  schemes:         
        page:
            ...
            config_entity_fields:
                - {  config_field_name: 'type', document_field_name: 'discriminator', discriminator: true }

Т.к. и AppBundle\Entity\Article и AppBundle\Entity\News использует одну и ту же схему "page" то соотв. уникальность их primary key теряется т.к. могут существовать News и Article с одинаковым id. Чтобы избежать неопределенности задается конфигурационное поле используемое как дискриминатор значение которого добавляется к primary key сущности и результат записывается в уникальное поле документа.


Также есть возможность задать фильтры для индексируемых сущностей применяемых перед тем как сущность будет проиндексирована в solr. В зависимости от результата фильтра сущность может быть проиндексирована, удалена или пропущена во время индексации. Пример:


indexed_entities:
    page:
        class:  AppBundle\Entity\Page
        ...
        filters: [ big_id, published, ... ]
    news:
        class:  AppBundle\Entity\News
        ...
        filters: [ published, ... ]        
schemes:
       ....
filters:    
    fields:
        big_id: { entity_field_name: "id", entity_field_value: 3, operator: ">=" }
        published: { entity_field_name: "published", entity_field_value: true, operator: "=" }

Соотв. если после того как Page или News были созданы и например поле "published" = false то индексация будет пропущена и в solr ничего не будет записано.


Если же Page или News были обновлены и например поле "published" = false то solr документ соответствующий этому экземпляру сущности будет удален в solr


То же самое и для фильтра big_id в случае если значение поле id < 3. "big_id" и "published" это произвольные названия для фильтров, могут быть какими угодно. Также есть возможность задачть symfony service как фильтр который применяется к экземпляру сущности а не к отдельному полю, подробности тут
Для успешной индексации должны быть выполнены условия для всех фильтров.


Индексация


Индексация сущности запускается каждый раз когда выполняется $em->flush и реализуется через https://symfony.com/doc/current/bundles/DoctrineBundle/entity-listeners.html


В случае когда надо выполнить первичную индексацию для всех сущностей в базе, реализована консольная команда:


app/console doctrine-solr:index

Подробности ее работы и аргументы можно найти на github.


Пример поиска:


После того как конфигурация задана и индексация выполнена можно искать как в рамках отдельной сущности так и в рамках схемы. Пример:


// MyController
//...
// @var \Mdiyakov\DoctrineSolrBundle\Finder\ClassFinder $finder 
$finder = $this->get('ds.finder')->getClassFinder(Article::class);

/** @var Article[] $searchResults */
$searchResults = $finder->findSearchTermByFields($searchTerm, ['title']);

Результатом будет массив состоящий только из Article::class.


Если схема используется несколькими сущностями, например:


  indexed_entities:
        page:
            class:  AppBundle\Entity\Article
            schema: page
           ...
    news:
            class:  AppBundle\Entity\News
            schema: page
            ...
...

то можно искать по всем сущностям использующим эту схему:


$schemaFinder = $this->get('ds.finder')->getSchemaFinder('page');
$schemaFinder->addSelectClass(Article::class);
$schemaFinder->addSelectClass(News::class);

/** @var object[] **/
$result = $schemaFinder->findSearchTermByFields($searchTerm, ['title', 'category']);

Результатом будет массив состоящий из Article и News экземпляров отсортированных в соотв. с релевантностью.


Подробнее про методы поиска тут


Заключение


Это вводная статься не описывающая полностью все возможности. Если вас заинтересовал бандл привожу пару ссылок для быстрого перехода по остальным возможностям бандла:



UPD: Добавлена поддержка Symfony 3 и PHP 7

Tags:
Hubs:
+10
Comments 23
Comments Comments 23

Articles