lekarna/search

ElasticSearch 和 Solr 实体映射库

0.5.1 2022-08-29 11:52 UTC

This package is auto-updated.

Last update: 2024-08-29 05:19:01 UTC


README

注意:该项目目前是一个原型。有关实际实现示例,请参阅 demo 文件夹。

支持的搜索引擎

特性

  • SearchManager
    • 可作为独立使用或混合配置使用
    • 可配置的搜索管理器支持聚合实体管理器
    • 支持通过搜索引擎适配器(如Elastica)进行直接API调用
    • 通过批量操作将返回的ID转换为所需的填充对象
    • 支持事件管理器监听器以进行可定制的实体处理
  • 支持通过事件监听器进行索引,通过JMS序列化器或简单的实体回调。
  • 使用ObjectManager::getClassMetadata()作为基础结构的索引和数据类型创建的注解

#使用方法#

配置

以下示例显示了如何配置搜索管理器连接。

$config = new Doctrine\Search\Configuration();
$config->setMetadataCacheImpl(new Doctrine\Common\Cache\ArrayCache());
$config->setEntitySerializer(
  new Doctrine\Search\Serializer\JMSSerializer(
    JMS\Serializer\SerializationContext::create()->setGroups('search')
  )
);

$eventManager = new Doctrine\Search\EventManager();
$eventManager->addListener($listener);

$searchManager = new Doctrine\Search\SearchManager(
  $config,
  new Doctrine\Search\ElasticSearch\Client(
    new Elastica\Client(array(
      array('host' => 'localhost', 'port' => '9200')
    )
  ),
  $eventManager
);

映射

以下示例显示了用于索引和类型生成的基本实体映射。可以使用构建脚本(需要高级设置)将映射渲染成适合自动生成索引和类型的格式。

<?php
namespace Entities;

use Doctrine\Search\Mapping\Annotations as MAP;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @MAP\ElasticSearchable(index="indexname", type="post", source=true)
 */
class Post
{
  /**
   * @ORM\Id
   * @ORM\GeneratedValue(strategy="AUTO")
   * @MAP\ElasticField(type="integer", includeInAll=false)
   */
  private $id;

  /**
   * @ORM\Column(type="string")
   * @MAP\ElasticField(type="string", includeInAll=true, boost=5.0)
   */
  private $title;

  /**
   * @ORM\Column(type="text")
   * @MAP\ElasticField(type="string", includeInAll=true)
   */
  private $content;

  /**
   * @MAP\ElasticField(name="tags", type="string", includeInAll=false, index="not_analyzed")
   */
  public function getTags() {
    return $this->tags->slice(0,3);
  }
}

索引

目前可以通过以下方式对文档进行序列化以进行索引。如果需要,可以使用ORM中的事件监听器,如示例所示。如果不需要事件监听器,则可以使用搜索管理器直接将实体持久化或删除。

<?php
namespace Entities\Listener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Entities\Behaviour\SearchableEntityInterface;

class SearchableListener implements
{
      protected function getSearchManager() {
            return $this->getDatabaseConnection('elasticsearch');
      }

      public function postPersist(LifecycleEventArgs $oArgs) {
            $oEntity = $oArgs->getEntity();
            if($oEntity instanceof SearchableEntityInterface) {
                $this->getSearchManager()->persist($oEntity);
          }
      }

    public function postRemove(LifecycleEventArgs $oArgs) {
        $oEntity = $oArgs->getEntity();
        if($oEntity instanceof SearchableEntityInterface) {
            $this->getSearchManager()->remove($oEntity);
        }
    }
}

CallbackSerializer

这种方法只期望实体上有toArray()方法,尽管可以根据需要配置此方法。示例中建议的接口可以是任何您想要的接口,只要您的事件监听器可以识别需要持久化到搜索引擎的实体(参见上面的示例)。

...
use Entities\Behaviour\SearchableEntityInterface

class Post implements SearchableEntityInterface
{
  ...
  public function toArray() {
    return array(
      'id' => $this->id,
      'title' => $this->title,
      'content' => $this->content
      ...
    );
  }
}

JMS Serializer

您还可以使用JMS Serializer的高级序列化功能来自动处理序列化,根据示例中的注解进行。如果需要,可以使用JMS序列化器来替代。

...
use JMS\Serializer\Annotation as JMS;
use Entities\Behaviour\SearchableEntityInterface

/**
 * @ORM\Entity
 * @MAP\ElasticSearchable(index="indexname", type="post", source=true)
 * @JMS\ExclusionPolicy("all")
 */
class Post implements SearchableEntityInterface
{
  ...
  /**
   * @ORM\Column(type="string")
   * @MAP\ElasticField(type="string", includeInAll=true, boost=5.0)
   * @JMS\Expose
   * @JMS\Groups({"public", "search"})
   */
  private $title;

  /**
   * @ORM\Column(type="text")
   * @MAP\ElasticField(type="string", includeInAll=true)
   * @JMS\Expose
   * @JMS\Groups({"public", "search"})
   */
  private $content;
  ...
}

AnnotationSerializer

尚不可用。

查询

可以通过以下方式通过搜索管理器执行查询,如下所示。使用结果缓存是指使用缓存进行填充查询。搜索引擎特定的适配器接口被神奇地暴露出来,因此在本例中,Elastica\Query::addSort方法与Doctrine\Search\Query方法合并。因此,支持由搜索引擎客户端库支持的任何查询复杂度。

$hydrationQuery = $entityManager->createQueryBuilder()
  ->select(array('p', 'field(p.id, :ids) as HIDDEN field'))
    ->from('Entities\Post', 'p')
    ->where('p.id IN (:ids)')
    ->orderBy('field')
    ->getQuery();

$query = $searchManager->createQuery()
  ->from('Entities\Post')
  ->searchWith(new Elastica\Query())
    ->hydrateWith($hydrationQuery)
    ->addSort('_score')
    ->setFrom(0)
    ->setLimit(10)
    ->getResult();

可以使用以下技术进行简单的存储库ID查询和Term搜索。反序列化独立于Doctrine\ORM进行,但根据注册的SerializerInterface对相同的模型进行填充。

$entity = $searchManager->getRepository('Entities\Post')->find($id);
$entity = $searchManager->getRepository('Entities\Post')->findOneBy(array($key => $term));