Use identifiers for bulk delete rather than cloning objects

This commit is contained in:
nurikabe 2014-03-29 18:36:06 -04:00
parent 361d80a720
commit 0de48d2190
4 changed files with 63 additions and 18 deletions

View file

@ -9,7 +9,12 @@ use FOS\ElasticaBundle\Persister\ObjectPersister;
use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\SyntaxError; use Symfony\Component\ExpressionLanguage\SyntaxError;
use Symfony\Component\PropertyAccess\PropertyAccess;
/**
* Automatically update ElasticSearch based on changes to the Doctrine source
* data. One listener is generated for each Doctrine entity / ElasticSearch type.
*/
class Listener implements EventSubscriber class Listener implements EventSubscriber
{ {
/** /**
@ -48,10 +53,14 @@ class Listener implements EventSubscriber
protected $isIndexableCallback; protected $isIndexableCallback;
/** /**
* Objects scheduled for insertion, replacement, or removal * Objects scheduled for insertion and replacement
*/ */
public $scheduledForInsertion = array(); public $scheduledForInsertion = array();
public $scheduledForUpdate = array(); public $scheduledForUpdate = array();
/**
* IDs of objects scheduled for removal
*/
public $scheduledForDeletion = array(); public $scheduledForDeletion = array();
/** /**
@ -61,6 +70,13 @@ class Listener implements EventSubscriber
*/ */
protected $expressionLanguage; protected $expressionLanguage;
/**
* PropertyAccessor instance
*
* @var PropertyAccessorInterface
*/
protected $propertyAccessor;
/** /**
* Constructor. * Constructor.
* *
@ -75,6 +91,8 @@ class Listener implements EventSubscriber
$this->objectClass = $objectClass; $this->objectClass = $objectClass;
$this->events = $events; $this->events = $events;
$this->esIdentifierField = $esIdentifierField; $this->esIdentifierField = $esIdentifierField;
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
} }
/** /**
@ -195,20 +213,21 @@ class Listener implements EventSubscriber
$this->scheduledForUpdate[] = $entity; $this->scheduledForUpdate[] = $entity;
} else { } else {
// Delete if no longer indexable // Delete if no longer indexable
$this->scheduledForDeletion[] = clone $entity; $this->scheduleForDeletion($entity);
} }
} }
} }
/** /**
* Delete objects preRemove instead of postRemove so that we have access to the id * Delete objects preRemove instead of postRemove so that we have access to the id. Because this is called
* preRemove, first check that the entity is managed by Doctrine
*/ */
public function preRemove(EventArgs $eventArgs) public function preRemove(EventArgs $eventArgs)
{ {
$entity = $eventArgs->getEntity(); $entity = $eventArgs->getEntity();
if ($entity instanceof $this->objectClass) { if ($entity instanceof $this->objectClass) {
$this->scheduledForDeletion[] = clone $entity; $this->scheduleForDeletion($entity);
} }
} }
@ -224,7 +243,7 @@ class Listener implements EventSubscriber
$this->objectPersister->replaceMany($this->scheduledForUpdate); $this->objectPersister->replaceMany($this->scheduledForUpdate);
} }
if (count($this->scheduledForDeletion)) { if (count($this->scheduledForDeletion)) {
$this->objectPersister->deleteMany($this->scheduledForDeletion); $this->objectPersister->deleteManyByIdentifiers($this->scheduledForDeletion);
} }
} }
@ -245,4 +264,16 @@ class Listener implements EventSubscriber
{ {
$this->persistScheduled(); $this->persistScheduled();
} }
/**
* Record the specified identifier to delete. Do not need to entire object.
* @param mixed $object
* @return mixed
*/
protected function scheduleForDeletion($object)
{
if ($identifierValue = $this->propertyAccessor->getValue($object, $this->esIdentifierField)) {
$this->scheduledForDeletion[] = $identifierValue;
}
}
} }

View file

@ -126,6 +126,16 @@ class ObjectPersister implements ObjectPersisterInterface
$this->type->deleteDocuments($documents); $this->type->deleteDocuments($documents);
} }
/**
* Bulk deletes records from an array of identifiers
*
* @param array $identifiers array of domain model object identifiers
*/
public function deleteManyByIdentifiers(array $identifiers)
{
$this->type->getIndex()->getClient()->deleteIds($identifiers, $this->type->getIndex(), $this->type);
}
/** /**
* Transforms an object to an elastica document * Transforms an object to an elastica document
* *

View file

@ -61,4 +61,11 @@ interface ObjectPersisterInterface
* @param array $objects array of domain model objects * @param array $objects array of domain model objects
*/ */
function deleteMany(array $objects); function deleteMany(array $objects);
/**
* Bulk deletes records from an array of identifiers
*
* @param array $identifiers array of domain model object identifiers
*/
public function deleteManyByIdentifiers(array $identifiers);
} }

View file

@ -100,13 +100,13 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
$listener->postUpdate($eventArgs); $listener->postUpdate($eventArgs);
$this->assertEmpty($listener->scheduledForUpdate); $this->assertEmpty($listener->scheduledForUpdate);
$this->assertEquals($entity, current($listener->scheduledForDeletion)); $this->assertEquals($entity->getId(), current($listener->scheduledForDeletion));
$persister->expects($this->never()) $persister->expects($this->never())
->method('replaceOne'); ->method('replaceOne');
$persister->expects($this->once()) $persister->expects($this->once())
->method('deleteMany') ->method('deleteManyByIdentifiers')
->with(array($entity)); ->with(array($entity->getId()));
$listener->postFlush($eventArgs); $listener->postFlush($eventArgs);
} }
@ -133,13 +133,11 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
$listener = $this->createListener($persister, get_class($entity), array()); $listener = $this->createListener($persister, get_class($entity), array());
$listener->preRemove($eventArgs); $listener->preRemove($eventArgs);
$scheduledClone = current($listener->scheduledForDeletion); $this->assertEquals($entity->getId(), current($listener->scheduledForDeletion));
$this->assertEquals($entity, $scheduledClone);
$this->assertNotSame($entity, $scheduledClone);
$persister->expects($this->once()) $persister->expects($this->once())
->method('deleteMany') ->method('deleteManyByIdentifiers')
->with(array($entity)); ->with(array($entity->getId()));
$listener->postFlush($eventArgs); $listener->postFlush($eventArgs);
} }
@ -151,6 +149,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
$persister = $this->getMockPersister(); $persister = $this->getMockPersister();
$entity = new Listener\Entity(1); $entity = new Listener\Entity(1);
$entity->identifier = 'foo';
$eventArgs = $this->createLifecycleEventArgs($entity, $objectManager); $eventArgs = $this->createLifecycleEventArgs($entity, $objectManager);
$objectManager->expects($this->any()) $objectManager->expects($this->any())
@ -166,13 +165,11 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
$listener = $this->createListener($persister, get_class($entity), array(), 'identifier'); $listener = $this->createListener($persister, get_class($entity), array(), 'identifier');
$listener->preRemove($eventArgs); $listener->preRemove($eventArgs);
$scheduledClone = current($listener->scheduledForDeletion); $this->assertEquals($entity->identifier, current($listener->scheduledForDeletion));
$this->assertEquals($entity, $scheduledClone);
$this->assertNotSame($entity, $scheduledClone);
$persister->expects($this->once()) $persister->expects($this->once())
->method('deleteMany') ->method('deleteManyByIdentifiers')
->with(array($entity)); ->with(array($entity->identifier));
$listener->postFlush($eventArgs); $listener->postFlush($eventArgs);
} }