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 экземпляров отсортированных в соотв. с релевантностью.
Подробнее про методы поиска тут
Заключение
Это вводная статься не описывающая полностью все возможности. Если вас заинтересовал бандл привожу пару ссылок для быстрого перехода по остальным возможностям бандла:
- использование SuggestComponent
- построение своих запросов Query Building
- реализация своего ClassFinder
UPD: Добавлена поддержка Symfony 3 и PHP 7
