[Provider] Change provider construction (possible BC break)
* Created AbstractProvider class (for all DB services), which handles the default batch_size option. * The logger Closure is now optional for populate(). * Removed unused Elastica_Type argument from Provider constructors. * Added unit tests for Doctrine's AbstractProvider class. * The extra argument (ManagerRegistry) for Doctrine providers is now an appended constructor argument, so the extension no longer needs to use different replacement indexes for Propel/Doctrine providers.
This commit is contained in:
parent
89a368ae35
commit
e09225eb09
|
@ -281,26 +281,14 @@ class FOQElasticaExtension extends Extension
|
||||||
if (isset($typeConfig['provider']['service'])) {
|
if (isset($typeConfig['provider']['service'])) {
|
||||||
return $typeConfig['provider']['service'];
|
return $typeConfig['provider']['service'];
|
||||||
}
|
}
|
||||||
$abstractProviderId = sprintf('foq_elastica.provider.prototype.%s', $typeConfig['driver']);
|
|
||||||
$providerId = sprintf('foq_elastica.provider.%s.%s', $indexName, $typeName);
|
$providerId = sprintf('foq_elastica.provider.%s.%s', $indexName, $typeName);
|
||||||
$providerDef = new DefinitionDecorator($abstractProviderId);
|
$providerDef = new DefinitionDecorator('foq_elastica.provider.prototype.' . $typeConfig['driver']);
|
||||||
$providerDef->addTag('foq_elastica.provider', array('index' => $indexName, 'type' => $typeName));
|
$providerDef->addTag('foq_elastica.provider', array('index' => $indexName, 'type' => $typeName));
|
||||||
$providerDef->replaceArgument(0, $typeDef);
|
$providerDef->replaceArgument(0, new Reference($objectPersisterId));
|
||||||
|
$providerDef->replaceArgument(1, $typeConfig['model']);
|
||||||
// Doctrine has a mandatory service as second argument
|
// Propel provider can simply ignore Doctrine-specific options
|
||||||
$argPos = ('propel' === $typeConfig['driver']) ? 1 : 2;
|
$providerDef->replaceArgument(2, array_diff_key($typeConfig['provider'], array('service' => 1)));
|
||||||
|
|
||||||
$providerDef->replaceArgument($argPos, new Reference($objectPersisterId));
|
|
||||||
$providerDef->replaceArgument($argPos + 1, $typeConfig['model']);
|
|
||||||
|
|
||||||
$options = array('batch_size' => $typeConfig['provider']['batch_size']);
|
|
||||||
|
|
||||||
if ('propel' !== $typeConfig['driver']) {
|
|
||||||
$options['query_builder_method'] = $typeConfig['provider']['query_builder_method'];
|
|
||||||
$options['clear_object_manager'] = $typeConfig['provider']['clear_object_manager'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$providerDef->replaceArgument($argPos + 2, $options);
|
|
||||||
$container->setDefinition($providerId, $providerDef);
|
$container->setDefinition($providerId, $providerDef);
|
||||||
|
|
||||||
return $providerId;
|
return $providerId;
|
||||||
|
|
|
@ -2,106 +2,85 @@
|
||||||
|
|
||||||
namespace FOQ\ElasticaBundle\Doctrine;
|
namespace FOQ\ElasticaBundle\Doctrine;
|
||||||
|
|
||||||
use FOQ\ElasticaBundle\Provider\ProviderInterface;
|
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||||
use FOQ\ElasticaBundle\Persister\ObjectPersisterInterface;
|
use FOQ\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||||
use Elastica_Type;
|
use FOQ\ElasticaBundle\Provider\AbstractProvider as BaseAbstractProvider;
|
||||||
use Elastica_Document;
|
|
||||||
use Closure;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
|
|
||||||
abstract class AbstractProvider implements ProviderInterface
|
abstract class AbstractProvider extends BaseAbstractProvider
|
||||||
{
|
{
|
||||||
/**
|
protected $managerRegistry;
|
||||||
* Elastica type
|
|
||||||
*
|
|
||||||
* @var Elastica_Type
|
|
||||||
*/
|
|
||||||
protected $type;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manager registry
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @var object
|
* @param ObjectPersisterInterface $objectPersister
|
||||||
|
* @param string $objectClass
|
||||||
|
* @param array $options
|
||||||
|
* @param ManagerRegistry $managerRegistry
|
||||||
*/
|
*/
|
||||||
protected $registry;
|
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $options, $managerRegistry)
|
||||||
|
|
||||||
/**
|
|
||||||
* Object persister
|
|
||||||
*
|
|
||||||
* @var ObjectPersisterInterface
|
|
||||||
*/
|
|
||||||
protected $objectPersister;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provider options
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $options = array(
|
|
||||||
'batch_size' => 100,
|
|
||||||
'clear_object_manager' => true,
|
|
||||||
'query_builder_method' => 'createQueryBuilder'
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(Elastica_Type $type, $registry, ObjectPersisterInterface $objectPersister, $objectClass, array $options = array())
|
|
||||||
{
|
{
|
||||||
$this->type = $type;
|
parent::__construct($objectPersister, $objectClass, array_merge(array(
|
||||||
$this->registry = $registry;
|
'clear_object_manager' => true,
|
||||||
$this->objectClass = $objectClass;
|
'query_builder_method' => 'createQueryBuilder',
|
||||||
$this->objectPersister = $objectPersister;
|
), $options));
|
||||||
$this->options = array_merge($this->options, $options);
|
|
||||||
|
$this->managerRegistry = $managerRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert the repository objects in the type index
|
* @see FOQ\ElasticaBundle\Provider\ProviderInterface::populate()
|
||||||
*
|
|
||||||
* @param Closure $loggerClosure
|
|
||||||
*/
|
*/
|
||||||
public function populate(Closure $loggerClosure)
|
public function populate(\Closure $loggerClosure = null)
|
||||||
{
|
{
|
||||||
$queryBuilder = $this->createQueryBuilder();
|
$queryBuilder = $this->createQueryBuilder();
|
||||||
$nbObjects = $this->countObjects($queryBuilder);
|
$nbObjects = $this->countObjects($queryBuilder);
|
||||||
|
|
||||||
for ($offset = 0; $offset < $nbObjects; $offset += $this->options['batch_size']) {
|
for ($offset = 0; $offset < $nbObjects; $offset += $this->options['batch_size']) {
|
||||||
|
if ($loggerClosure) {
|
||||||
|
$stepStartTime = microtime(true);
|
||||||
|
}
|
||||||
|
|
||||||
$stepStartTime = microtime(true);
|
|
||||||
$objects = $this->fetchSlice($queryBuilder, $this->options['batch_size'], $offset);
|
$objects = $this->fetchSlice($queryBuilder, $this->options['batch_size'], $offset);
|
||||||
|
|
||||||
$this->objectPersister->insertMany($objects);
|
$this->objectPersister->insertMany($objects);
|
||||||
|
|
||||||
if ($this->options['clear_object_manager']) {
|
if ($this->options['clear_object_manager']) {
|
||||||
$this->registry->getManagerForClass($this->objectClass)->clear();
|
$this->managerRegistry->getManagerForClass($this->objectClass)->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
$stepNbObjects = count($objects);
|
if ($loggerClosure) {
|
||||||
$stepCount = $stepNbObjects+$offset;
|
$stepNbObjects = count($objects);
|
||||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
$stepCount = $stepNbObjects + $offset;
|
||||||
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s', 100*$stepCount/$nbObjects, $stepCount, $nbObjects, $objectsPerSecond));
|
$percentComplete = 100 * $stepCount / $nbObjects;
|
||||||
|
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
||||||
|
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts the objects of a query builder
|
* Counts objects that would be indexed using the query builder.
|
||||||
*
|
*
|
||||||
* @param queryBuilder
|
* @param object $queryBuilder
|
||||||
* @return int
|
* @return integer
|
||||||
**/
|
*/
|
||||||
protected abstract function countObjects($queryBuilder);
|
protected abstract function countObjects($queryBuilder);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a slice of objects
|
* Fetches a slice of objects using the query builder.
|
||||||
*
|
*
|
||||||
* @param queryBuilder
|
* @param object $queryBuilder
|
||||||
* @param int limit
|
* @param integer $limit
|
||||||
* @param int offset
|
* @param integer $offset
|
||||||
* @return array of objects
|
* @return array
|
||||||
**/
|
*/
|
||||||
protected abstract function fetchSlice($queryBuilder, $limit, $offset);
|
protected abstract function fetchSlice($queryBuilder, $limit, $offset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the query builder used to fetch the documents to index
|
* Creates the query builder, which will be used to fetch objects to index.
|
||||||
*
|
*
|
||||||
* @return query builder
|
* @return object
|
||||||
**/
|
*/
|
||||||
protected abstract function createQueryBuilder();
|
protected abstract function createQueryBuilder();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,42 +2,49 @@
|
||||||
|
|
||||||
namespace FOQ\ElasticaBundle\Doctrine\MongoDB;
|
namespace FOQ\ElasticaBundle\Doctrine\MongoDB;
|
||||||
|
|
||||||
|
use Doctrine\ODM\MongoDB\Query\Builder;
|
||||||
use FOQ\ElasticaBundle\Doctrine\AbstractProvider;
|
use FOQ\ElasticaBundle\Doctrine\AbstractProvider;
|
||||||
|
use FOQ\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||||
|
|
||||||
class Provider extends AbstractProvider
|
class Provider extends AbstractProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Counts the objects of a query builder
|
* @see FOQ\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
||||||
*
|
*/
|
||||||
* @param queryBuilder
|
|
||||||
* @return int
|
|
||||||
**/
|
|
||||||
protected function countObjects($queryBuilder)
|
protected function countObjects($queryBuilder)
|
||||||
{
|
{
|
||||||
return $queryBuilder->getQuery()->count();
|
if (!$queryBuilder instanceof Builder) {
|
||||||
|
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ODM\MongoDB\Query\Builder');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $queryBuilder
|
||||||
|
->getQuery()
|
||||||
|
->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a slice of objects
|
* @see FOQ\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
|
||||||
*
|
*/
|
||||||
* @param queryBuilder
|
|
||||||
* @param int limit
|
|
||||||
* @param int offset
|
|
||||||
* @return array of objects
|
|
||||||
**/
|
|
||||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||||
{
|
{
|
||||||
return $queryBuilder->limit($limit)->skip($offset)->getQuery()->execute()->toArray();
|
if (!$queryBuilder instanceof Builder) {
|
||||||
|
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ODM\MongoDB\Query\Builder');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $queryBuilder
|
||||||
|
->limit($limit)
|
||||||
|
->skip($offset)
|
||||||
|
->getQuery()
|
||||||
|
->execute()
|
||||||
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the query builder used to fetch the documents to index
|
* @see FOQ\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
||||||
*
|
*/
|
||||||
* @return query builder
|
|
||||||
**/
|
|
||||||
protected function createQueryBuilder()
|
protected function createQueryBuilder()
|
||||||
{
|
{
|
||||||
return $this->registry
|
return $this->managerRegistry
|
||||||
->getManagerForClass($this->objectClass)
|
->getManagerForClass($this->objectClass)
|
||||||
->getRepository($this->objectClass)
|
->getRepository($this->objectClass)
|
||||||
->{$this->options['query_builder_method']}();
|
->{$this->options['query_builder_method']}();
|
||||||
|
|
|
@ -2,51 +2,59 @@
|
||||||
|
|
||||||
namespace FOQ\ElasticaBundle\Doctrine\ORM;
|
namespace FOQ\ElasticaBundle\Doctrine\ORM;
|
||||||
|
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use FOQ\ElasticaBundle\Doctrine\AbstractProvider;
|
use FOQ\ElasticaBundle\Doctrine\AbstractProvider;
|
||||||
|
use FOQ\ElasticaBundle\Exception\InvalidArgumentTypeException;
|
||||||
|
|
||||||
class Provider extends AbstractProvider
|
class Provider extends AbstractProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Counts the objects of a query builder
|
* @see FOQ\ElasticaBundle\Doctrine\AbstractProvider::countObjects()
|
||||||
*
|
*/
|
||||||
* @param queryBuilder
|
|
||||||
* @return int
|
|
||||||
**/
|
|
||||||
protected function countObjects($queryBuilder)
|
protected function countObjects($queryBuilder)
|
||||||
{
|
{
|
||||||
$qb = clone $queryBuilder;
|
if (!$queryBuilder instanceof QueryBuilder) {
|
||||||
$qb->select($qb->expr()->count($queryBuilder->getRootAlias()))
|
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||||
->resetDQLPart('orderBy'); // no need to order the query. It does not change the count and make the query less efficient.
|
}
|
||||||
|
|
||||||
return $qb->getQuery()->getSingleScalarResult();
|
/* Clone the query builder before altering its field selection and DQL,
|
||||||
|
* lest we leave the query builder in a bad state for fetchSlice().
|
||||||
|
*/
|
||||||
|
$qb = clone $queryBuilder;
|
||||||
|
|
||||||
|
return $qb
|
||||||
|
->select($qb->expr()->count($queryBuilder->getRootAlias()))
|
||||||
|
// Remove ordering for efficiency; it doesn't affect the count
|
||||||
|
->resetDQLPart('orderBy')
|
||||||
|
->getQuery()
|
||||||
|
->getSingleScalarResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a slice of objects
|
* @see FOQ\ElasticaBundle\Doctrine\AbstractProvider::fetchSlice()
|
||||||
*
|
*/
|
||||||
* @param queryBuilder
|
|
||||||
* @param int limit
|
|
||||||
* @param int offset
|
|
||||||
* @return array of objects
|
|
||||||
**/
|
|
||||||
protected function fetchSlice($queryBuilder, $limit, $offset)
|
protected function fetchSlice($queryBuilder, $limit, $offset)
|
||||||
{
|
{
|
||||||
$queryBuilder->setFirstResult($offset);
|
if (!$queryBuilder instanceof QueryBuilder) {
|
||||||
$queryBuilder->setMaxResults($limit);
|
throw new InvalidArgumentTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||||
|
}
|
||||||
|
|
||||||
return $queryBuilder->getQuery()->getResult();
|
return $queryBuilder
|
||||||
|
->setFirstResult($offset)
|
||||||
|
->setMaxResults($limit)
|
||||||
|
->getQuery()
|
||||||
|
->getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the query builder used to fetch the documents to index
|
* @see FOQ\ElasticaBundle\Doctrine\AbstractProvider::createQueryBuilder()
|
||||||
*
|
*/
|
||||||
* @return query builder
|
|
||||||
**/
|
|
||||||
protected function createQueryBuilder()
|
protected function createQueryBuilder()
|
||||||
{
|
{
|
||||||
return $this->registry
|
return $this->managerRegistry
|
||||||
->getManagerForClass($this->objectClass)
|
->getManagerForClass($this->objectClass)
|
||||||
->getRepository($this->objectClass)
|
->getRepository($this->objectClass)
|
||||||
|
// ORM query builders require an alias argument
|
||||||
->{$this->options['query_builder_method']}('a');
|
->{$this->options['query_builder_method']}('a');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
Exception/InvalidArgumentTypeException.php
Normal file
11
Exception/InvalidArgumentTypeException.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FOQ\ElasticaBundle\Exception;
|
||||||
|
|
||||||
|
class InvalidArgumentTypeException extends \InvalidArgumentException
|
||||||
|
{
|
||||||
|
public function __construct($value, $expectedType)
|
||||||
|
{
|
||||||
|
parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,64 +2,28 @@
|
||||||
|
|
||||||
namespace FOQ\ElasticaBundle\Propel;
|
namespace FOQ\ElasticaBundle\Propel;
|
||||||
|
|
||||||
use FOQ\ElasticaBundle\Provider\ProviderInterface;
|
use FOQ\ElasticaBundle\Provider\AbstractProvider;
|
||||||
use FOQ\ElasticaBundle\Persister\ObjectPersisterInterface;
|
|
||||||
use Elastica_Type;
|
|
||||||
use Elastica_Document;
|
|
||||||
use Closure;
|
|
||||||
use InvalidArgumentException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Propel provider
|
* Propel provider
|
||||||
*
|
*
|
||||||
* @author William Durand <william.durand1@gmail.com>
|
* @author William Durand <william.durand1@gmail.com>
|
||||||
*/
|
*/
|
||||||
class Provider implements ProviderInterface
|
class Provider extends AbstractProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Elastica type
|
* @see FOQ\ElasticaBundle\Provider\ProviderInterface::populate()
|
||||||
*
|
|
||||||
* @var Elastica_Type
|
|
||||||
*/
|
*/
|
||||||
protected $type;
|
public function populate(\Closure $loggerClosure = null)
|
||||||
|
|
||||||
/**
|
|
||||||
* Object persister
|
|
||||||
*
|
|
||||||
* @var ObjectPersisterInterface
|
|
||||||
*/
|
|
||||||
protected $objectPersister;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provider options
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $options = array(
|
|
||||||
'batch_size' => 100,
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(Elastica_Type $type, ObjectPersisterInterface $objectPersister, $objectClass, array $options = array())
|
|
||||||
{
|
|
||||||
$this->type = $type;
|
|
||||||
$this->objectClass = $objectClass;
|
|
||||||
$this->objectPersister = $objectPersister;
|
|
||||||
$this->options = array_merge($this->options, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert the repository objects in the type index
|
|
||||||
*
|
|
||||||
* @param Closure $loggerClosure
|
|
||||||
*/
|
|
||||||
public function populate(Closure $loggerClosure)
|
|
||||||
{
|
{
|
||||||
$queryClass = $this->objectClass . 'Query';
|
$queryClass = $this->objectClass . 'Query';
|
||||||
$nbObjects = $queryClass::create()->count();
|
$nbObjects = $queryClass::create()->count();
|
||||||
|
|
||||||
for ($offset = 0; $offset < $nbObjects; $offset += $this->options['batch_size']) {
|
for ($offset = 0; $offset < $nbObjects; $offset += $this->options['batch_size']) {
|
||||||
|
if ($loggerClosure) {
|
||||||
|
$stepStartTime = microtime(true);
|
||||||
|
}
|
||||||
|
|
||||||
$stepStartTime = microtime(true);
|
|
||||||
$objects = $queryClass::create()
|
$objects = $queryClass::create()
|
||||||
->limit($this->options['batch_size'])
|
->limit($this->options['batch_size'])
|
||||||
->offset($offset)
|
->offset($offset)
|
||||||
|
@ -67,10 +31,13 @@ class Provider implements ProviderInterface
|
||||||
|
|
||||||
$this->objectPersister->insertMany($objects->getArrayCopy());
|
$this->objectPersister->insertMany($objects->getArrayCopy());
|
||||||
|
|
||||||
$stepNbObjects = count($objects);
|
if ($loggerClosure) {
|
||||||
$stepCount = $stepNbObjects+$offset;
|
$stepNbObjects = count($objects);
|
||||||
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
$stepCount = $stepNbObjects + $offset;
|
||||||
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s', 100*$stepCount/$nbObjects, $stepCount, $nbObjects, $objectsPerSecond));
|
$percentComplete = 100 * $stepCount / $nbObjects;
|
||||||
|
$objectsPerSecond = $stepNbObjects / (microtime(true) - $stepStartTime);
|
||||||
|
$loggerClosure(sprintf('%0.1f%% (%d/%d), %d objects/s', $percentComplete, $stepCount, $nbObjects, $objectsPerSecond));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
Provider/AbstractProvider.php
Normal file
30
Provider/AbstractProvider.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FOQ\ElasticaBundle\Provider;
|
||||||
|
|
||||||
|
use FOQ\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||||
|
use FOQ\ElasticaBundle\Provider\ProviderInterface;
|
||||||
|
|
||||||
|
abstract class AbstractProvider implements ProviderInterface
|
||||||
|
{
|
||||||
|
protected $objectClass;
|
||||||
|
protected $objectPersister;
|
||||||
|
protected $options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param ObjectPersisterInterface $objectPersister
|
||||||
|
* @param string $objectClass
|
||||||
|
* @param array $options
|
||||||
|
*/
|
||||||
|
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $options = array())
|
||||||
|
{
|
||||||
|
$this->objectPersister = $objectPersister;
|
||||||
|
$this->objectClass = $objectClass;
|
||||||
|
|
||||||
|
$this->options = array_merge(array(
|
||||||
|
'batch_size' => 100,
|
||||||
|
), $options);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace FOQ\ElasticaBundle\Provider;
|
namespace FOQ\ElasticaBundle\Provider;
|
||||||
|
|
||||||
use Closure;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert application domain objects into elastica types
|
* Insert application domain objects into elastica types
|
||||||
*
|
*
|
||||||
|
@ -12,9 +10,9 @@ use Closure;
|
||||||
interface ProviderInterface
|
interface ProviderInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Add all domain objects of a repository to the elastica type
|
* Persists all domain objects to ElasticSearch for this provider.
|
||||||
*
|
*
|
||||||
* @param Closure $loggerClosure
|
* @param Closure $loggerClosure
|
||||||
*/
|
*/
|
||||||
function populate(Closure $loggerClosure);
|
function populate(\Closure $loggerClosure = null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,10 @@
|
||||||
<services>
|
<services>
|
||||||
|
|
||||||
<service id="foq_elastica.provider.prototype.mongodb" class="FOQ\ElasticaBundle\Doctrine\MongoDB\Provider" public="false" abstract="true">
|
<service id="foq_elastica.provider.prototype.mongodb" class="FOQ\ElasticaBundle\Doctrine\MongoDB\Provider" public="false" abstract="true">
|
||||||
<argument /> <!-- type -->
|
|
||||||
<argument type="service" id="doctrine.odm.mongodb" />
|
|
||||||
<argument /> <!-- object persister -->
|
<argument /> <!-- object persister -->
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
|
<argument type="service" id="doctrine.odm.mongodb" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="foq_elastica.listener.prototype.mongodb" class="FOQ\ElasticaBundle\Doctrine\MongoDB\Listener" public="false" abstract="true">
|
<service id="foq_elastica.listener.prototype.mongodb" class="FOQ\ElasticaBundle\Doctrine\MongoDB\Listener" public="false" abstract="true">
|
||||||
|
|
|
@ -7,11 +7,10 @@
|
||||||
<services>
|
<services>
|
||||||
|
|
||||||
<service id="foq_elastica.provider.prototype.orm" class="FOQ\ElasticaBundle\Doctrine\ORM\Provider" public="false" abstract="true">
|
<service id="foq_elastica.provider.prototype.orm" class="FOQ\ElasticaBundle\Doctrine\ORM\Provider" public="false" abstract="true">
|
||||||
<argument /> <!-- type -->
|
|
||||||
<argument type="service" id="doctrine" />
|
|
||||||
<argument /> <!-- object persister -->
|
<argument /> <!-- object persister -->
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
|
<argument type="service" id="doctrine" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="foq_elastica.listener.prototype.orm" class="FOQ\ElasticaBundle\Doctrine\ORM\Listener" public="false" abstract="true">
|
<service id="foq_elastica.listener.prototype.orm" class="FOQ\ElasticaBundle\Doctrine\ORM\Listener" public="false" abstract="true">
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<services>
|
<services>
|
||||||
|
|
||||||
<service id="foq_elastica.provider.prototype.propel" class="FOQ\ElasticaBundle\Propel\Provider" public="false" abstract="true">
|
<service id="foq_elastica.provider.prototype.propel" class="FOQ\ElasticaBundle\Propel\Provider" public="false" abstract="true">
|
||||||
<argument /> <!-- type -->
|
|
||||||
<argument /> <!-- object persister -->
|
<argument /> <!-- object persister -->
|
||||||
<argument /> <!-- model -->
|
<argument /> <!-- model -->
|
||||||
<argument type="collection" /> <!-- options -->
|
<argument type="collection" /> <!-- options -->
|
||||||
|
|
183
Tests/Doctrine/AbstractProviderTest.php
Normal file
183
Tests/Doctrine/AbstractProviderTest.php
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace FOQ\ElasticaBundle\Tests\Doctrine;
|
||||||
|
|
||||||
|
class AbstractProviderTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
private $objectClass;
|
||||||
|
private $objectManager;
|
||||||
|
private $objectPersister;
|
||||||
|
private $options;
|
||||||
|
private $managerRegistry;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
|
||||||
|
$this->markTestSkipped('Doctrine Common is not available.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->objectClass = 'objectClass';
|
||||||
|
$this->options = array();
|
||||||
|
|
||||||
|
$this->objectPersister = $this->getMockObjectPersister();
|
||||||
|
$this->managerRegistry = $this->getMockManagerRegistry();
|
||||||
|
$this->objectManager = $this->getMockObjectManager();
|
||||||
|
|
||||||
|
$this->managerRegistry->expects($this->any())
|
||||||
|
->method('getManagerForClass')
|
||||||
|
->with($this->objectClass)
|
||||||
|
->will($this->returnValue($this->objectManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providePopulateIterations
|
||||||
|
*/
|
||||||
|
public function testPopulateIterations($nbObjects, $objectsByIteration, $batchSize)
|
||||||
|
{
|
||||||
|
$this->options['batch_size'] = $batchSize;
|
||||||
|
|
||||||
|
$provider = $this->getMockAbstractProvider();
|
||||||
|
|
||||||
|
$queryBuilder = new \stdClass();
|
||||||
|
|
||||||
|
$provider->expects($this->once())
|
||||||
|
->method('createQueryBuilder')
|
||||||
|
->will($this->returnValue($queryBuilder));
|
||||||
|
|
||||||
|
$provider->expects($this->once())
|
||||||
|
->method('countObjects')
|
||||||
|
->with($queryBuilder)
|
||||||
|
->will($this->returnValue($nbObjects));
|
||||||
|
|
||||||
|
$providerInvocationOffset = 2;
|
||||||
|
|
||||||
|
foreach ($objectsByIteration as $i => $objects) {
|
||||||
|
$offset = $objects[0] - 1;
|
||||||
|
|
||||||
|
$provider->expects($this->at($providerInvocationOffset + $i))
|
||||||
|
->method('fetchSlice')
|
||||||
|
->with($queryBuilder, $batchSize, $offset)
|
||||||
|
->will($this->returnValue($objects));
|
||||||
|
|
||||||
|
$this->objectPersister->expects($this->at($i))
|
||||||
|
->method('insertMany')
|
||||||
|
->with($objects);
|
||||||
|
|
||||||
|
$this->objectManager->expects($this->at($i))
|
||||||
|
->method('clear');
|
||||||
|
}
|
||||||
|
|
||||||
|
$provider->populate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providePopulateIterations()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
100,
|
||||||
|
array(range(1,100)),
|
||||||
|
100,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
105,
|
||||||
|
array(range(1, 50), range(51, 100), range(101, 105)),
|
||||||
|
50,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPopulateShouldNotClearsObjectManager()
|
||||||
|
{
|
||||||
|
$nbObjects = 1;
|
||||||
|
$objects = array(1);
|
||||||
|
$this->options['clear_object_manager'] = false;
|
||||||
|
|
||||||
|
$provider = $this->getMockAbstractProvider();
|
||||||
|
|
||||||
|
$provider->expects($this->any())
|
||||||
|
->method('countObjects')
|
||||||
|
->will($this->returnValue($nbObjects));
|
||||||
|
|
||||||
|
$provider->expects($this->any())
|
||||||
|
->method('fetchSlice')
|
||||||
|
->will($this->returnValue($objects));
|
||||||
|
|
||||||
|
$this->objectManager->expects($this->never())
|
||||||
|
->method('clear');
|
||||||
|
|
||||||
|
$provider->populate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPopulateInvokesLoggerClosure()
|
||||||
|
{
|
||||||
|
$nbObjects = 1;
|
||||||
|
$objects = array(1);
|
||||||
|
|
||||||
|
$provider = $this->getMockAbstractProvider();
|
||||||
|
|
||||||
|
$provider->expects($this->any())
|
||||||
|
->method('countObjects')
|
||||||
|
->will($this->returnValue($nbObjects));
|
||||||
|
|
||||||
|
$provider->expects($this->any())
|
||||||
|
->method('fetchSlice')
|
||||||
|
->will($this->returnValue($objects));
|
||||||
|
|
||||||
|
$loggerClosureInvoked = false;
|
||||||
|
$loggerClosure = function () use (&$loggerClosureInvoked) {
|
||||||
|
$loggerClosureInvoked = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
$provider->populate();
|
||||||
|
$this->assertFalse($loggerClosureInvoked);
|
||||||
|
|
||||||
|
$provider->populate($loggerClosure);
|
||||||
|
$this->assertTrue($loggerClosureInvoked);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FOQ\ElasticaBundle\Doctrine\AbstractProvider
|
||||||
|
*/
|
||||||
|
private function getMockAbstractProvider()
|
||||||
|
{
|
||||||
|
return $this->getMockForAbstractClass('FOQ\ElasticaBundle\Doctrine\AbstractProvider', array(
|
||||||
|
$this->objectPersister,
|
||||||
|
$this->objectClass,
|
||||||
|
$this->options,
|
||||||
|
$this->managerRegistry,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Doctrine\Common\Persistence\ManagerRegistry
|
||||||
|
*/
|
||||||
|
private function getMockManagerRegistry()
|
||||||
|
{
|
||||||
|
return $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FOQ\ElasticaBundle\Tests\Doctrine\ObjectManager
|
||||||
|
*/
|
||||||
|
private function getMockObjectManager()
|
||||||
|
{
|
||||||
|
return $this->getMock(__NAMESPACE__ . '\ObjectManager');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FOQ\ElasticaBundle\Persister\ObjectPersisterInterface
|
||||||
|
*/
|
||||||
|
private function getMockObjectPersister()
|
||||||
|
{
|
||||||
|
return $this->getMock('FOQ\ElasticaBundle\Persister\ObjectPersisterInterface');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Doctrine\Common\Persistence\ObjectManager does not include a clear() method
|
||||||
|
* in its interface, so create a new interface for mocking.
|
||||||
|
*/
|
||||||
|
interface ObjectManager
|
||||||
|
{
|
||||||
|
function clear();
|
||||||
|
}
|
Loading…
Reference in a new issue