Skip to content
This repository has been archived by the owner on Dec 25, 2022. It is now read-only.

Commit

Permalink
added zend_search adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
wachterjohannes committed May 22, 2017
1 parent 3371b15 commit eaf727d
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 5 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@

"elasticsearch/elasticsearch": "^2.0",
"ramsey/uuid": "^3.5",
"doctrine/dbal": "^2.5"
"doctrine/dbal": "^2.5",
"zendframework/zendsearch": "2.0.0rc6"
},
"require-dev": {
"doctrine/doctrine-bundle": "^1.6",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function getConfigTreeBuilder()
$treeBuilder = new TreeBuilder();
$treeBuilder->root('pucene')
->children()
->enumNode('adapter')->isRequired()->values(['elasticsearch', 'pucene'])->end()
->enumNode('adapter')->isRequired()->values(['elasticsearch', 'pucene', 'zend_search'])->end()
->append($this->getAdaptersNode())
->append($this->getIndexNode())
->end();
Expand All @@ -39,6 +39,11 @@ private function getAdaptersNode()
->scalarNode('doctrine_dbal_connection')->defaultValue('doctrine.dbal.default_connection')->end()
->end()
->end()
->arrayNode('zend_search')
->children()
->scalarNode('directory')->defaultValue('%kernel.root_dir%/../var/indexes')->end()
->end()
->end()
->arrayNode('elasticsearch')
->addDefaultsIfNotSet()
->children()
Expand Down
16 changes: 16 additions & 0 deletions src/Bundle/PuceneBundle/DependencyInjection/PuceneExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public function load(array $configs, ContainerBuilder $container)
$this->loadPucene($config, $container);
} elseif ($adapter === 'elasticsearch') {
$this->loadElasticsearch($config, $container);
} elseif ($adapter === 'zend_search') {
$this->loadZendSearch($config, $container);
}

$container->setAlias('pucene.client', 'pucene.' . $adapter . '.client');
Expand Down Expand Up @@ -95,4 +97,18 @@ private function loadElasticsearch($config, $container)
$pass = new CollectorCompilerPass('pucene.elasticsearch.visitor', 'pucene.elasticsearch.visitor_pool', 'query');
$pass->process($container);
}

/**
* Load specific configuration for zend_search.
*
* @param array $config
* @param ContainerBuilder $container
*/
private function loadZendSearch($config, $container)
{
$container->setParameter(
'pucene.adapter_config.zend_search.directory',
$config['adapters']['zend_search']['directory']
);
}
}
13 changes: 13 additions & 0 deletions src/Bundle/PuceneBundle/Resources/config/zend_search/services.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="pucene.zend_search.client.filesystem" class="Symfony\Component\Filesystem\Filesystem"/>

<service id="pucene.zend_search.client" class="Pucene\Component\ZendSearch\ZendSearchClient">
<argument type="string">%pucene.adapter_config.zend_search.directory%</argument>
<argument type="service" id="pucene.zend_search.client.filesystem"/>
</service>
</services>
</container>
56 changes: 56 additions & 0 deletions src/Component/ZendSearch/ZendSearchClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Pucene\Component\ZendSearch;

use Pucene\Component\Client\ClientInterface;
use Symfony\Component\Filesystem\Filesystem;
use ZendSearch\Lucene\Index;

class ZendSearchClient implements ClientInterface
{
/**
* @var string
*/
private $directory;

/**
* @var Filesystem
*/
private $filesystem;

/**
* @param string $directory
* @param Filesystem $filesystem
*/
public function __construct($directory, Filesystem $filesystem)
{
$this->directory = $directory;
$this->filesystem = $filesystem;
}

/**
* {@inheritdoc}
*/
public function get($name)
{
return new ZendSearchIndex($name, new Index($this->directory . DIRECTORY_SEPARATOR . $name));
}

/**
* {@inheritdoc}
*/
public function create($name, array $parameters)
{
$this->filesystem->mkdir($this->directory . DIRECTORY_SEPARATOR . $name);

return new ZendSearchIndex(new Index($this->directory . DIRECTORY_SEPARATOR . $name, true));
}

/**
* {@inheritdoc}
*/
public function delete($name)
{
$this->filesystem->remove($this->directory . DIRECTORY_SEPARATOR . $name);
}
}
132 changes: 132 additions & 0 deletions src/Component/ZendSearch/ZendSearchIndex.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

namespace Pucene\Component\ZendSearch;

use Pucene\Component\Client\IndexInterface;
use Pucene\Component\QueryBuilder\Search;
use Ramsey\Uuid\Uuid;
use ZendSearch\Lucene\Analysis\Analyzer\Analyzer;
use ZendSearch\Lucene\Analysis\Analyzer\Common\Utf8\CaseInsensitive;
use ZendSearch\Lucene\Document;
use ZendSearch\Lucene\Index;
use ZendSearch\Lucene\Search\Query\Term;
use ZendSearch\Lucene\Search\QueryHit;
use ZendSearch\Lucene\Search\QueryParser;

class ZendSearchIndex implements IndexInterface
{
const ID_FIELD = '_id';
const TYPE_FIELD = '_type';
const SOURCE_FIELD = '_source';

/**
* @var string
*/
private $name;

/**
* @var Index
*/
private $index;

/**
* @param string $name
* @param Index $index
*/
public function __construct(string $name, Index $index)
{
$this->name = $name;
$this->index = $index;

QueryParser::setDefaultOperator(QueryParser::B_AND);
Analyzer::setDefault(new CaseInsensitive());
}

/**
* {@inheritdoc}
*/
public function index(array $document, $type, $id = null)
{
if ($id) {
$this->delete($type, $id);
}

$zendDocument = new Document();
$zendDocument->addField(Document\Field::keyword(self::ID_FIELD, $id ?: Uuid::uuid4()->toString()));
$zendDocument->addField(Document\Field::text(self::TYPE_FIELD, $type));
$zendDocument->addField(Document\Field::unIndexed(self::SOURCE_FIELD, serialize($document)));

foreach ($document as $key => $value) {
$zendDocument->addField(Document\Field::text($key, $value));
}

$this->index->addDocument($zendDocument);
}

/**
* {@inheritdoc}
*/
public function delete($type, $id)
{
$hit = $this->getHit($type, $id);
if (!$hit) {
return;
}

$this->index->delete($hit->id);
}

/**
* {@inheritdoc}
*/
public function search(Search $search, $type)
{
$hits = $this->index->find(
new Term(new Index\Term($search->getQuery()->getTerm(), $search->getQuery()->getField()))
);

$documents = [];
foreach ($hits as $hit) {
/** @var Document $document */
$document = $hit->getDocument();
$documents[] = [
'_id' => $document->getFieldValue(self::ID_FIELD),
'_type' => $document->getFieldValue(self::TYPE_FIELD),
'_index' => $this->name,
'_score' => $hit->score,
'_source' => unserialize($document->getFieldValue(self::SOURCE_FIELD)),
];
}

return ['hits' => $documents];
}

/**
* {@inheritdoc}
*/
public function get($type, $id)
{
$document = $this->getHit($type, $id)->getDocument();
if (!$document) {
return;
}

return unserialize($document->getFieldValue(self::SOURCE_FIELD));
}

/**
* @param string $type
* @param string $id
*
* @return QueryHit
*/
private function getHit(string $type, string $id)
{
$hits = $this->index->find(self::ID_FIELD . ':' . $id . ' AND ' . self::TYPE_FIELD . ':' . $type);
if (count($hits) === 0) {
return;
}

return $hits[0];
}
}
16 changes: 16 additions & 0 deletions tests/src/Functional/Comparison/ComparisonTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Pucene\Component\Client\IndexInterface;
use Pucene\Component\QueryBuilder\Search;
use Pucene\Component\ZendSearch\ZendSearchIndex;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

abstract class ComparisonTestCase extends KernelTestCase
Expand All @@ -18,6 +19,11 @@ abstract class ComparisonTestCase extends KernelTestCase
*/
protected $elasticsearchIndex;

/**
* @var ZendSearchIndex
*/
protected $zendSearchIndex;

/**
* {@inheritdoc}
*/
Expand All @@ -30,6 +36,9 @@ protected function setUp()

$elasticsearch = $this->get('pucene.elasticsearch.client');
$this->elasticsearchIndex = $elasticsearch->get('my_index');

$zendSearch = $this->get('pucene.zend_search.client');
$this->zendSearchIndex = $zendSearch->get('my_index');
}

/**
Expand Down Expand Up @@ -74,15 +83,22 @@ protected function assertSearch(Search $search)
{
$elasticsearchResult = $this->elasticsearchIndex->search($search, 'my_type');
$puceneResult = $this->puceneIndex->search($search, 'my_type');
$zendSearchResult = $this->zendSearchIndex->search($search, 'my_type');

$this->assertEquals(count($elasticsearchResult['hits']), count($puceneResult['hits']));
$this->assertEquals(count($elasticsearchResult['hits']), count($zendSearchResult['hits']));

$elasticsearchHits = $this->normalize($elasticsearchResult['hits']);
$puceneHits = $this->normalize($puceneResult['hits']);
$zendSearchHits = $this->normalize($zendSearchResult['hits']);

// zend-search scoring is not comparable with elasticsearch/pucene scoring

foreach ($puceneHits as $id => $puceneHit) {
$this->assertArrayHasKey($id, $elasticsearchHits, $id);
$this->assertArrayHasKey($id, $zendSearchHits, $id);
$this->assertEquals($elasticsearchHits[$id]['document'], $puceneHit['document']);
$this->assertEquals($zendSearchHits[$id]['document'], $puceneHit['document']);

// if position matches: OK
// else score has to be equals
Expand Down
6 changes: 6 additions & 0 deletions tests/src/TestBundle/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ private function getAdaptersNode()
->scalarNode('doctrine_dbal_connection')->defaultValue('doctrine.dbal.default_connection')->end()
->end()
->end()
->arrayNode('zend_search')
->addDefaultsIfNotSet()
->children()
->scalarNode('directory')->defaultValue('%kernel.root_dir%/../var/indexes')->end()
->end()
->end()
->arrayNode('elasticsearch')
->addDefaultsIfNotSet()
->children()
Expand Down
11 changes: 10 additions & 1 deletion tests/src/TestBundle/DependencyInjection/TestExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ public function load(array $configs, ContainerBuilder $container)
);

$loader = new DelegatingLoader($loaderResolver);
foreach (['pucene', 'elasticsearch'] as $adapter) {
foreach (['pucene', 'elasticsearch', 'zend_search'] as $adapter) {
$loader->import($fileLocator->locate($adapter . '/'));
}

$this->loadPucene($config, $container);
$this->loadZendSearch($config, $container);
}

private function loadPucene(array $config, ContainerBuilder $container)
Expand All @@ -66,4 +67,12 @@ private function loadPucene(array $config, ContainerBuilder $container)

$container->getDefinition('pucene.pucene.storage_factory')->replaceArgument(1, $serviceIds);
}

private function loadZendSearch(array $config, ContainerBuilder $container)
{
$container->setParameter(
'pucene.adapter_config.zend_search.directory',
$config['adapters']['zend_search']['directory']
);
}
}
22 changes: 20 additions & 2 deletions tests/src/TestBundle/Resources/config/commands.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,37 @@

<tag name="console.command"/>
</service>
<service id="pucene.zend_search.commands.create_indices"
class="Pucene\Bundle\PuceneBundle\Command\CreateIndicesCommand">
<argument type="string">zend_search:indices:create</argument>
<argument>%pucene.indices%</argument>
<argument type="service" id="pucene.zend_search.client"/>

<tag name="console.command"/>
</service>

<service id="pucene.pucene.commands.delete_indices" class="Pucene\Bundle\PuceneBundle\Command\DeleteIndicesCommand">
<service id="pucene.pucene.commands.delete_indices"
class="Pucene\Bundle\PuceneBundle\Command\DeleteIndicesCommand">
<argument type="string">pucene:indices:delete</argument>
<argument>%pucene.indices%</argument>
<argument type="service" id="pucene.pucene.client"/>

<tag name="console.command"/>
</service>
<service id="pucene.elasticsearch.commands.delete_indices" class="Pucene\Bundle\PuceneBundle\Command\DeleteIndicesCommand">
<service id="pucene.elasticsearch.commands.delete_indices"
class="Pucene\Bundle\PuceneBundle\Command\DeleteIndicesCommand">
<argument type="string">elasticsearch:indices:delete</argument>
<argument>%pucene.indices%</argument>
<argument type="service" id="pucene.elasticsearch.client"/>

<tag name="console.command"/>
</service>
<service id="pucene.zend_search.commands.delete_indices"
class="Pucene\Bundle\PuceneBundle\Command\DeleteIndicesCommand">
<argument type="string">zend_search:indices:delete</argument>
<argument>%pucene.indices%</argument>
<argument type="service" id="pucene.zend_search.client"/>

<tag name="console.command"/>
</service>
</services>
Expand Down

0 comments on commit eaf727d

Please sign in to comment.