From a386d19910b33049900c424f7cd6b93258378888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 11 Sep 2014 19:01:55 +0200 Subject: [PATCH 01/45] Fixed consistency - added objectManager accessors instead of entityManager accessors - entityManager accessors are marked as deprecated --- lib/Doctrine/Search/Configuration.php | 28 +++++++++++++++---- lib/Doctrine/Search/SearchManager.php | 39 ++++++++++++++++++++------- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/lib/Doctrine/Search/Configuration.php b/lib/Doctrine/Search/Configuration.php index 856ba00..c13d3c2 100755 --- a/lib/Doctrine/Search/Configuration.php +++ b/lib/Doctrine/Search/Configuration.php @@ -161,20 +161,38 @@ public function getEntitySerializer() } /** - * @param ObjectManager $entityManager + * @param ObjectManager $objectManager */ - public function setEntityManager(ObjectManager $entityManager) + public function setObjectManager(ObjectManager $objectManager) { - $this->attributes['entityManager'] = $entityManager; + $this->attributes['objectManager'] = $objectManager; } /** * @return ObjectManager */ + public function getObjectManager() + { + if (isset($this->attributes['objectManager'])) { + return $this->attributes['objectManager']; + } + } + + /** + * @deprecated + */ + public function setEntityManager(ObjectManager $objectManager) + { + $this->setObjectManager($objectManager); + } + + /** + * @deprecated + */ public function getEntityManager() { - if (isset($this->attributes['entityManager'])) { - return $this->attributes['entityManager']; + if (isset($this->attributes['objectManager'])) { + return $this->attributes['objectManager']; } } } diff --git a/lib/Doctrine/Search/SearchManager.php b/lib/Doctrine/Search/SearchManager.php index c4a1bfa..b3ab874 100755 --- a/lib/Doctrine/Search/SearchManager.php +++ b/lib/Doctrine/Search/SearchManager.php @@ -53,7 +53,7 @@ class SearchManager implements ObjectManager /** * @var ObjectManager */ - private $entityManager; + private $objectManager; /** * The event manager that is the central point of the event system. @@ -95,27 +95,25 @@ public function __construct(Configuration $config, SearchClientInterface $client $this->metadataFactory->setCacheDriver($this->configuration->getMetadataCacheImpl()); $this->serializer = $this->configuration->getEntitySerializer(); - $this->entityManager = $this->configuration->getEntityManager(); + $this->objectManager = $this->configuration->getObjectManager(); $this->unitOfWork = new UnitOfWork($this); } /** - * Inject a Doctrine 2 object manager - * - * @param ObjectManager $om + * @param ObjectManager $objectManager */ - public function setEntityManager(ObjectManager $om) + public function setObjectManager(ObjectManager $objectManager) { - $this->entityManager = $om; + $this->objectManager = $objectManager; } /** - * @return ObjectManager|\Doctrine\ORM\EntityManager + * @return ObjectManager */ - public function getEntityManager() + public function getObjectManager() { - return $this->entityManager; + return $this->objectManager; } /** @@ -332,4 +330,25 @@ public function detach($object) public function refresh($object) { } + + + /** + * Inject a Doctrine 2 object manager + * + * @deprecated + * @param ObjectManager $om + */ + public function setEntityManager(ObjectManager $om) + { + $this->setObjectManager($om); + } + + /** + * @deprecated + * @return ObjectManager|\Doctrine\ORM\EntityManager + */ + public function getEntityManager() + { + return $this->getObjectManager(); + } } From 8c84e7f90b155b8b4a7b0fe14222d959ae6a4e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 11 Sep 2014 19:02:46 +0200 Subject: [PATCH 02/45] Implemented DependentMappingDriver --- .../Search/Mapping/ClassMetadataFactory.php | 9 ++++ .../Search/Mapping/DependentMappingDriver.php | 41 +++++++++++++++++++ .../Mapping/Driver/AnnotationDriver.php | 38 ++++++++++++++++- 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 lib/Doctrine/Search/Mapping/DependentMappingDriver.php diff --git a/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php b/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php index 9fca8f2..544417e 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php @@ -19,6 +19,7 @@ namespace Doctrine\Search\Mapping; +use Doctrine\Common\Persistence\ObjectManager; use Doctrine\Search\SearchManager; use Doctrine\Search\Configuration; use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; @@ -66,6 +67,14 @@ protected function initialize() $this->driver = $this->config->getMetadataDriverImpl(); $this->evm = $this->sm->getEventManager(); $this->initialized = true; + + $om = $this->sm->getObjectManager(); + if ($this->driver instanceof DependentMappingDriver && $om instanceof ObjectManager) { + $parentMetadataFactory = $om->getMetadataFactory(); + if ($parentMetadataFactory instanceof AbstractClassMetadataFactory) { + $this->driver->setParentDriver($parentMetadataFactory->getDriver()); + } + } } /** diff --git a/lib/Doctrine/Search/Mapping/DependentMappingDriver.php b/lib/Doctrine/Search/Mapping/DependentMappingDriver.php new file mode 100644 index 0000000..6133661 --- /dev/null +++ b/lib/Doctrine/Search/Mapping/DependentMappingDriver.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\Search\Mapping; + +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; + + + +/** + * If a driver implements this interface, + * it should delegate the getAllClassNames method to the parent driver. + * + * @author Filip Procházka + */ +interface DependentMappingDriver +{ + + /** + * @param MappingDriver $driver + * @return void + */ + public function setParentDriver(MappingDriver $driver); + +} diff --git a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php index 838da68..5eaed2a 100755 --- a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php @@ -20,9 +20,13 @@ namespace Doctrine\Search\Mapping\Driver; use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Search\Mapping\Annotations as Search; use Doctrine\Common\Persistence\Mapping\ClassMetadata; use Doctrine\Search\Exception\Driver as DriverException; +use Doctrine\Search\Mapping\DependentMappingDriver; + + /** * The AnnotationDriver reads the mapping metadata from docblock annotations. @@ -31,7 +35,7 @@ * @since 1.0 * @author Mike Lohmann */ -class AnnotationDriver extends AbstractAnnotationDriver +class AnnotationDriver extends AbstractAnnotationDriver implements DependentMappingDriver { /** * {@inheritDoc} @@ -59,7 +63,18 @@ class AnnotationDriver extends AbstractAnnotationDriver 'Doctrine\\Search\\Mapping\\Annotations\\SolrField', ); + /** + * @var MappingDriver + */ + private $parentDriver; + /** + * @param MappingDriver $driver + */ + public function setParentDriver(MappingDriver $driver) + { + $this->parentDriver = $driver; + } /** * @param string $className @@ -207,4 +222,25 @@ private function addValuesToMetadata(array $reflectedClassProperties, ClassMetad return $metadata; } + + public function getAllClassNames() + { + if ($this->classNames !== NULL) { + return $this->classNames; + } + + if ($this->parentDriver === NULL) { + return parent::getAllClassNames(); + } + + $classes = array(); + foreach ($this->parentDriver->getAllClassNames() as $className) { + if (!$this->isTransient($className)) { + $classes[] = $className; + } + } + + return $this->classNames = $classes; + } + } From aaac049a5b63643932fb8784ed4c5f1ede2e012e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 11 Sep 2014 19:02:46 +0200 Subject: [PATCH 03/45] Implemented DependentMappingDriver --- .../Search/Mapping/ClassMetadataFactory.php | 10 +++++ .../Search/Mapping/DependentMappingDriver.php | 41 +++++++++++++++++++ .../Mapping/Driver/AnnotationDriver.php | 38 ++++++++++++++++- 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 lib/Doctrine/Search/Mapping/DependentMappingDriver.php diff --git a/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php b/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php index 9fca8f2..3d07ac4 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php @@ -19,6 +19,7 @@ namespace Doctrine\Search\Mapping; +use Doctrine\Common\Persistence\ObjectManager; use Doctrine\Search\SearchManager; use Doctrine\Search\Configuration; use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; @@ -66,6 +67,15 @@ protected function initialize() $this->driver = $this->config->getMetadataDriverImpl(); $this->evm = $this->sm->getEventManager(); $this->initialized = true; + + $om = $this->sm->getObjectManager(); + if ($this->driver instanceof DependentMappingDriver && $om instanceof ObjectManager) { + $parentMetadataFactory = $om->getMetadataFactory(); + if ($parentMetadataFactory instanceof AbstractClassMetadataFactory) { + $parentMetadataFactory->initialize(); + $this->driver->setParentDriver($parentMetadataFactory->getDriver()); + } + } } /** diff --git a/lib/Doctrine/Search/Mapping/DependentMappingDriver.php b/lib/Doctrine/Search/Mapping/DependentMappingDriver.php new file mode 100644 index 0000000..6133661 --- /dev/null +++ b/lib/Doctrine/Search/Mapping/DependentMappingDriver.php @@ -0,0 +1,41 @@ +. + */ + +namespace Doctrine\Search\Mapping; + +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; + + + +/** + * If a driver implements this interface, + * it should delegate the getAllClassNames method to the parent driver. + * + * @author Filip Procházka + */ +interface DependentMappingDriver +{ + + /** + * @param MappingDriver $driver + * @return void + */ + public function setParentDriver(MappingDriver $driver); + +} diff --git a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php index 838da68..5eaed2a 100755 --- a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php @@ -20,9 +20,13 @@ namespace Doctrine\Search\Mapping\Driver; use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Search\Mapping\Annotations as Search; use Doctrine\Common\Persistence\Mapping\ClassMetadata; use Doctrine\Search\Exception\Driver as DriverException; +use Doctrine\Search\Mapping\DependentMappingDriver; + + /** * The AnnotationDriver reads the mapping metadata from docblock annotations. @@ -31,7 +35,7 @@ * @since 1.0 * @author Mike Lohmann */ -class AnnotationDriver extends AbstractAnnotationDriver +class AnnotationDriver extends AbstractAnnotationDriver implements DependentMappingDriver { /** * {@inheritDoc} @@ -59,7 +63,18 @@ class AnnotationDriver extends AbstractAnnotationDriver 'Doctrine\\Search\\Mapping\\Annotations\\SolrField', ); + /** + * @var MappingDriver + */ + private $parentDriver; + /** + * @param MappingDriver $driver + */ + public function setParentDriver(MappingDriver $driver) + { + $this->parentDriver = $driver; + } /** * @param string $className @@ -207,4 +222,25 @@ private function addValuesToMetadata(array $reflectedClassProperties, ClassMetad return $metadata; } + + public function getAllClassNames() + { + if ($this->classNames !== NULL) { + return $this->classNames; + } + + if ($this->parentDriver === NULL) { + return parent::getAllClassNames(); + } + + $classes = array(); + foreach ($this->parentDriver->getAllClassNames() as $className) { + if (!$this->isTransient($className)) { + $classes[] = $className; + } + } + + return $this->classNames = $classes; + } + } From 330362c919ad349b60b7b29f31d0f0cf68bcc57f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Tue, 16 Sep 2014 17:06:49 +0200 Subject: [PATCH 04/45] phpdoc --- lib/Doctrine/Search/ElasticSearch/Client.php | 6 ++++-- lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php | 2 +- lib/Doctrine/Search/Mapping/ClassMetadata.php | 4 +--- lib/Doctrine/Search/Mapping/ClassMetadataFactory.php | 2 ++ 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/Search/ElasticSearch/Client.php b/lib/Doctrine/Search/ElasticSearch/Client.php index 3088ffd..26179bb 100755 --- a/lib/Doctrine/Search/ElasticSearch/Client.php +++ b/lib/Doctrine/Search/ElasticSearch/Client.php @@ -19,6 +19,8 @@ namespace Doctrine\Search\ElasticSearch; +use Doctrine\Search\Mapping\Annotations\ElasticField; +use Doctrine\Search\Mapping\Annotations\ElasticRoot; use Doctrine\Search\SearchClientInterface; use Doctrine\Search\Mapping\ClassMetadata; use Doctrine\Search\Exception\NoResultException; @@ -251,7 +253,7 @@ public function deleteType(ClassMetadata $metadata) /** * Generates property mapping from entity annotations * - * @param array $mappings + * @param array|ElasticField[] $mappings */ protected function getMapping($mappings) { @@ -334,7 +336,7 @@ protected function getParameters($paramMapping) /** * Generates root mapping from entity annotations * - * @param array $mappings + * @param array|ElasticRoot[] $mappings */ protected function getRootMapping($mappings) { diff --git a/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php b/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php index f445a26..e576ceb 100644 --- a/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php +++ b/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php @@ -73,7 +73,7 @@ final class ElasticRoot extends Annotation public $value; /** - * @var array + * @var array|ElasticField[] */ public $mapping; } diff --git a/lib/Doctrine/Search/Mapping/ClassMetadata.php b/lib/Doctrine/Search/Mapping/ClassMetadata.php index a72455d..827ab2c 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadata.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadata.php @@ -101,8 +101,6 @@ class ClassMetadata implements ClassMetadataInterface public $className; /** - * The ReflectionProperty instances of the mapped class. - * * @var array|ElasticField[] */ public $fieldMappings = array(); @@ -131,7 +129,7 @@ class ClassMetadata implements ClassMetadataInterface /** * The ReflectionClass instance of the mapped class. * - * @var \ReflectionClass + * @var \ReflectionProperty[]|\ReflectionMethod[] */ public $reflFields; diff --git a/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php b/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php index 3d07ac4..a4c647f 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php @@ -36,6 +36,8 @@ * @link www.doctrine-project.com * @since 1.0 * @author Mike Lohmann + * + * @method ClassMetadata[] getAllMetadata() */ class ClassMetadataFactory extends AbstractClassMetadataFactory { From 5ec307d5e0c18a064bc2538b65635f80823d98f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Tue, 16 Sep 2014 17:07:19 +0200 Subject: [PATCH 05/45] Virtual properties --- lib/Doctrine/Search/ElasticSearch/Client.php | 1 + lib/Doctrine/Search/Mapping/ClassMetadata.php | 29 ++++++++++++++++--- .../Mapping/Driver/AnnotationDriver.php | 2 +- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/Search/ElasticSearch/Client.php b/lib/Doctrine/Search/ElasticSearch/Client.php index 26179bb..0519bc3 100755 --- a/lib/Doctrine/Search/ElasticSearch/Client.php +++ b/lib/Doctrine/Search/ElasticSearch/Client.php @@ -222,6 +222,7 @@ public function createType(ClassMetadata $metadata) { $type = $this->getIndex($metadata->index)->getType($metadata->type); $properties = $this->getMapping($metadata->fieldMappings); + $properties += $this->getMapping($metadata->methodMappings); $rootProperties = $this->getRootMapping($metadata->rootMappings); $mapping = new Mapping($type, $properties); diff --git a/lib/Doctrine/Search/Mapping/ClassMetadata.php b/lib/Doctrine/Search/Mapping/ClassMetadata.php index 827ab2c..74d8e8b 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadata.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadata.php @@ -105,6 +105,11 @@ class ClassMetadata implements ClassMetadataInterface */ public $fieldMappings = array(); + /** + * @var array|ElasticField[] + */ + public $methodMappings = array(); + /** * Additional root annotations of the mapped class. * @@ -254,8 +259,20 @@ public function hasField($fieldName) */ public function addFieldMapping(\Reflector $field, $mapping = array()) { - $fieldName = $field->getName(); - $this->fieldMappings[$fieldName] = $mapping; + $this->fieldMappings[$field->getName()] = $mapping; + $this->reflFields[$field->getName()] = $field; + } + + /** + * This mapping is used in the _wakeup-method to set the reflFields after _sleep. + * + * @param \ReflectionProperty $field + * @param array $mapping + */ + public function addMethodMapping(\Reflector $field, $mapping = array()) + { + $this->methodMappings[$field->getName()] = $mapping; + $this->reflFields[$field->getName()] = $field; } /** @@ -274,8 +291,7 @@ public function addRootMapping($mapping = array()) */ public function addParameterMapping(\Reflector $field, $mapping = array()) { - $fieldName = $field->getName(); - $this->parameters[$fieldName] = $mapping; + $this->parameters[$field->getName()] = $mapping; } /** @@ -418,6 +434,11 @@ public function wakeupReflection($reflService) foreach ($this->fieldMappings as $field => $mapping) { $this->reflFields[$field] = $reflService->getAccessibleProperty($this->className, $field); } + + foreach ($this->methodMappings as $field => $mapping) { + $this->reflFields[$field] = $reflService->getClass($this->className)->getMethod($field); + $this->reflFields[$field]->setAccessible(TRUE); + } } /** diff --git a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php index 5eaed2a..4f36ab4 100755 --- a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php @@ -187,7 +187,7 @@ private function extractMethodsAnnotations(array $reflMethods, ClassMetadata $me foreach ($this->reader->getMethodAnnotations($reflMethod) as $annotation) { foreach ($this->entityFieldAnnotationClasses as $fieldAnnotationClass) { if ($annotation instanceof $fieldAnnotationClass) { - $metadata->addFieldMapping($reflMethod, $annotation); + $metadata->addMethodMapping($reflMethod, $annotation); continue 2; } } From 489b9de30261a3b037c91bffd69e3e345e6b0d72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 2 Oct 2014 22:18:36 +0200 Subject: [PATCH 06/45] AnnotationDriver refactoring --- .../Mapping/Driver/AnnotationDriver.php | 88 +++++++------------ 1 file changed, 32 insertions(+), 56 deletions(-) diff --git a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php index 4f36ab4..2d6aabf 100755 --- a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php @@ -19,6 +19,7 @@ namespace Doctrine\Search\Mapping\Driver; +use Doctrine\Common\Annotations\Annotation; use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver; use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Search\Mapping\Annotations as Search; @@ -85,40 +86,31 @@ public function setParentDriver(MappingDriver $driver) public function loadMetadataForClass($className, ClassMetadata $metadata) { $reflClass = $metadata->getReflectionClass(); - - if (!$reflClass) { - $reflClass = new \ReflectionClass((string)$className); - } - - $reflProperties = $reflClass->getProperties(); - $reflMethods = $reflClass->getMethods(); - $this->extractClassAnnotations($reflClass, $metadata); - $this->extractPropertiesAnnotations($reflProperties, $metadata); - $this->extractMethodsAnnotations($reflMethods, $metadata); + $this->extractPropertiesAnnotations($reflClass, $metadata); + $this->extractMethodsAnnotations($reflClass, $metadata); } - /** - * This function extracts the class annotations for search from the given reflected class and writes - * them into metadata. + * This function extracts the class annotations for search from the given reflected class + * and writes them into metadata. * * @param \ReflectionClass $reflClass * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata * - * @return ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata - * * @throws DriverException\ClassIsNotAValidDocumentException|DriverException\PropertyDoesNotExistsInMetadataException */ private function extractClassAnnotations(\ReflectionClass $reflClass, ClassMetadata $metadata) { $documentsClassAnnotations = array(); foreach ($this->reader->getClassAnnotations($reflClass) as $annotation) { + if ($annotation instanceof $this->entityRootAnnotationClass) { + $metadata->addRootMapping($annotation); + continue; + } + foreach ($this->entityAnnotationClasses as $annotationClass => $index) { - if ($annotation instanceof $this->entityRootAnnotationClass) { - $metadata->addRootMapping($annotation); - break; - } elseif ($annotation instanceof $annotationClass) { + if ($annotation instanceof $annotationClass) { $documentsClassAnnotations[$index] = $annotation; break; } @@ -129,30 +121,20 @@ private function extractClassAnnotations(\ReflectionClass $reflClass, ClassMetad throw new DriverException\ClassIsNotAValidDocumentException($metadata->getName()); } - //choose only one (the first one) + // choose only one (the first one) $annotationClass = reset($documentsClassAnnotations); - $reflClassAnnotations = new \ReflectionClass($annotationClass); - $metadata = $this->addValuesToMetadata( - $reflClassAnnotations->getProperties(), - $metadata, - $annotationClass - ); - - return $metadata; + $this->addValuesToMetadata($metadata, $annotationClass); } /** * Extract the property annotations. * - * @param \ReflectionProperty[] $reflProperties - * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata - * - * @return ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata + * @param \ReflectionClass $class + * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata */ - private function extractPropertiesAnnotations(array $reflProperties, ClassMetadata $metadata) + private function extractPropertiesAnnotations(\ReflectionClass $class, ClassMetadata $metadata) { - $documentsFieldAnnotations = array(); - foreach ($reflProperties as $reflProperty) { + foreach ($class->getProperties() as $reflProperty) { foreach ($this->reader->getPropertyAnnotations($reflProperty) as $annotation) { foreach ($this->entityFieldAnnotationClasses as $fieldAnnotationClass) { if ($annotation instanceof $fieldAnnotationClass) { @@ -168,22 +150,19 @@ private function extractPropertiesAnnotations(array $reflProperties, ClassMetada } } } - - return $metadata; } /** * Extract the methods annotations. * - * @param \ReflectionMethod[] $reflMethods - * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata + * @param \ReflectionClass $class + * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata * * @return ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata */ - private function extractMethodsAnnotations(array $reflMethods, ClassMetadata $metadata) + private function extractMethodsAnnotations(\ReflectionClass $class, ClassMetadata $metadata) { - $documentsFieldAnnotations = array(); - foreach ($reflMethods as $reflMethod) { + foreach ($class->getMethods() as $reflMethod) { foreach ($this->reader->getMethodAnnotations($reflMethod) as $annotation) { foreach ($this->entityFieldAnnotationClasses as $fieldAnnotationClass) { if ($annotation instanceof $fieldAnnotationClass) { @@ -193,34 +172,31 @@ private function extractMethodsAnnotations(array $reflMethods, ClassMetadata $me } } } - - return $metadata; } /** - * @param \ReflectionProperty[] $reflectedClassProperties - * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata - * @param string $class + * Iterates the given annotation class * - * @return ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata + * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata + * @param Annotation $class * * @throws DriverException\PropertyDoesNotExistsInMetadataException */ - private function addValuesToMetadata(array $reflectedClassProperties, ClassMetadata $metadata, $class) + private function addValuesToMetadata(ClassMetadata $metadata, $class) { - foreach ($reflectedClassProperties as $reflectedProperty) { + $reflClassAnnotations = new \ReflectionClass($class); + + foreach ($reflClassAnnotations->getProperties() as $reflectedProperty) { $propertyName = $reflectedProperty->getName(); if (false === property_exists($metadata, $propertyName)) { throw new DriverException\PropertyDoesNotExistsInMetadataException($reflectedProperty->getName()); - } else { - if (!is_null($class->$propertyName)) { - $metadata->$propertyName = $class->$propertyName; - } } - } - return $metadata; + if (!is_null($class->$propertyName)) { + $metadata->$propertyName = $class->$propertyName; + } + } } public function getAllClassNames() From d56002015b262645b539207d5bba549beb3ec861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 2 Oct 2014 23:20:46 +0200 Subject: [PATCH 07/45] AnnotationDriver refactoring --- .../Mapping/Annotations/ElasticRoot.php | 2 +- lib/Doctrine/Search/Mapping/ClassMetadata.php | 6 +++--- .../Mapping/Driver/AnnotationDriver.php | 20 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php b/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php index e576ceb..1f7f9f8 100644 --- a/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php +++ b/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php @@ -30,7 +30,7 @@ final class ElasticRoot extends Annotation /** * @var string */ - public $name; + public $name; /** * @var string diff --git a/lib/Doctrine/Search/Mapping/ClassMetadata.php b/lib/Doctrine/Search/Mapping/ClassMetadata.php index 74d8e8b..e1302bd 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadata.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadata.php @@ -286,12 +286,12 @@ public function addRootMapping($mapping = array()) /** * This mapping is used in the _wakeup-method to set the parameters after _sleep. * - * @param \ReflectionProperty $field + * @param string $fieldName * @param array $mapping */ - public function addParameterMapping(\Reflector $field, $mapping = array()) + public function addParameterMapping($fieldName, $mapping = array()) { - $this->parameters[$field->getName()] = $mapping; + $this->parameters[$fieldName] = $mapping; } /** diff --git a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php index 2d6aabf..476d35d 100755 --- a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php @@ -47,12 +47,6 @@ class AnnotationDriver extends AbstractAnnotationDriver implements DependentMapp 'Doctrine\\Search\\Mapping\\Annotations\\ElasticRoot' => 3, ); - protected $entityRootAnnotationClass = 'Doctrine\\Search\\Mapping\\Annotations\\ElasticRoot'; - - protected $entityIdAnnotationClass = 'Doctrine\\Search\\Mapping\\Annotations\\Id'; - - protected $entityParamAnnotationClass = 'Doctrine\\Search\\Mapping\\Annotations\\Parameter'; - /** * Document fields annotation classes, ordered by precedence. */ @@ -104,7 +98,7 @@ private function extractClassAnnotations(\ReflectionClass $reflClass, ClassMetad { $documentsClassAnnotations = array(); foreach ($this->reader->getClassAnnotations($reflClass) as $annotation) { - if ($annotation instanceof $this->entityRootAnnotationClass) { + if ($annotation instanceof Search\ElasticRoot) { $metadata->addRootMapping($annotation); continue; } @@ -138,13 +132,19 @@ private function extractPropertiesAnnotations(\ReflectionClass $class, ClassMeta foreach ($this->reader->getPropertyAnnotations($reflProperty) as $annotation) { foreach ($this->entityFieldAnnotationClasses as $fieldAnnotationClass) { if ($annotation instanceof $fieldAnnotationClass) { - if ($annotation instanceof $this->entityIdAnnotationClass) { + if ($annotation instanceof Search\Id) { $metadata->setIdentifier($reflProperty->name); - } elseif ($annotation instanceof $this->entityParamAnnotationClass) { - $metadata->addParameterMapping($reflProperty, $annotation); + + } elseif ($annotation instanceof Search\Parameter) { + $metadata->addParameterMapping($reflProperty, array( + 'name' => $annotation->name, + 'type' => $annotation->type, + )); + } else { $metadata->addFieldMapping($reflProperty, $annotation); } + continue 2; } } From 57b8e8da9453bb5db0c6a0b94042ce4489ee4bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 2 Oct 2014 23:29:11 +0200 Subject: [PATCH 08/45] Fixed caching of ClassMetadata::methodMappings --- lib/Doctrine/Search/Mapping/ClassMetadata.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Doctrine/Search/Mapping/ClassMetadata.php b/lib/Doctrine/Search/Mapping/ClassMetadata.php index e1302bd..af1dc19 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadata.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadata.php @@ -169,6 +169,7 @@ public function __sleep() return array( 'boost', 'className', + 'methodMappings', 'fieldMappings', 'parameters', 'index', From 4ad5584f4272f0c48f451044e67d85846284b1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Koub=C3=ADk?= Date: Sat, 22 Nov 2014 18:03:22 +0100 Subject: [PATCH 09/45] ElasticCompletionField --- lib/Doctrine/Search/ElasticSearch/Client.php | 24 ++++++++ .../Annotations/ElasticCompletionField.php | 56 +++++++++++++++++++ .../Mapping/Annotations/ElasticField.php | 14 ++++- 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 lib/Doctrine/Search/Mapping/Annotations/ElasticCompletionField.php diff --git a/lib/Doctrine/Search/ElasticSearch/Client.php b/lib/Doctrine/Search/ElasticSearch/Client.php index 0519bc3..e1f56b5 100755 --- a/lib/Doctrine/Search/ElasticSearch/Client.php +++ b/lib/Doctrine/Search/ElasticSearch/Client.php @@ -299,6 +299,30 @@ protected function getMapping($mappings) $properties[$propertyName]['index_name'] = $fieldMapping->indexName; } + if (isset($fieldMapping->indexAnalyzer)) { + $properties[$propertyName]['index_analyzer'] = $fieldMapping->indexAnalyzer; + } + + if (isset($fieldMapping->searchAnalyzer)) { + $properties[$propertyName]['search_analyzer'] = $fieldMapping->searchAnalyzer; + } + + if (isset($fieldMapping->payloads)) { + $properties[$propertyName]['payloads'] = $fieldMapping->payloads; + } + + if (isset($fieldMapping->preserveSeparators)) { + $properties[$propertyName]['preserve_separators'] = $fieldMapping->preserveSeparators; + } + + if (isset($fieldMapping->preservePositionIncrements)) { + $properties[$propertyName]['preserve_position_increments'] = $fieldMapping->preservePositionIncrements; + } + + if (isset($fieldMapping->maxInputLength)) { + $properties[$propertyName]['max_input_length'] = $fieldMapping->maxInputLength; + } + if ($fieldMapping->type == 'attachment' && isset($fieldMapping->fields)) { $callback = function ($field) { unset($field['type']); diff --git a/lib/Doctrine/Search/Mapping/Annotations/ElasticCompletionField.php b/lib/Doctrine/Search/Mapping/Annotations/ElasticCompletionField.php new file mode 100644 index 0000000..318122a --- /dev/null +++ b/lib/Doctrine/Search/Mapping/Annotations/ElasticCompletionField.php @@ -0,0 +1,56 @@ +. + */ + +namespace Doctrine\Search\Mapping\Annotations; + +use Doctrine\Common\Annotations\Annotation; + + +/** + * @Annotation + * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) + */ +class ElasticCompletionField extends ElasticField +{ + /** + * @var string + */ + public $type = 'completion'; + + /** + * @var bool + */ + public $payloads = false; + + /** + * @var bool + */ + public $preserveSeparators = true; + + /** + * @var bool + */ + public $preservePositionIncrements = true; + + /** + * @var integer + */ + public $maxInputLength; + +} diff --git a/lib/Doctrine/Search/Mapping/Annotations/ElasticField.php b/lib/Doctrine/Search/Mapping/Annotations/ElasticField.php index af49cc0..2fa8e2f 100755 --- a/lib/Doctrine/Search/Mapping/Annotations/ElasticField.php +++ b/lib/Doctrine/Search/Mapping/Annotations/ElasticField.php @@ -25,7 +25,7 @@ * @Annotation * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) */ -final class ElasticField extends Field +class ElasticField extends Field { /** * @var boolean @@ -52,6 +52,16 @@ final class ElasticField extends Field */ public $analyzer; + /** + * @var string + */ + public $indexAnalyzer; + + /** + * @var string + */ + public $searchAnalyzer; + /** * @var string */ @@ -66,7 +76,7 @@ final class ElasticField extends Field * @var boolean */ public $store; - + /** * @var mixed */ From b63be19d3546be4a16902143a90382960f5978ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Fri, 26 Dec 2014 23:02:18 +0100 Subject: [PATCH 10/45] drop dead code --- lib/Doctrine/Search/Solr/Client.php | 97 ------------------ lib/Doctrine/Search/Solr/Configuration.php | 35 ------- lib/Doctrine/Search/Solr/Connection.php | 51 ---------- lib/Doctrine/Search/ZendLucene/Client.php | 98 ------------------- .../Search/ZendLucene/Configuration.php | 35 ------- lib/Doctrine/Search/ZendLucene/Connection.php | 51 ---------- 6 files changed, 367 deletions(-) delete mode 100755 lib/Doctrine/Search/Solr/Client.php delete mode 100755 lib/Doctrine/Search/Solr/Configuration.php delete mode 100755 lib/Doctrine/Search/Solr/Connection.php delete mode 100644 lib/Doctrine/Search/ZendLucene/Client.php delete mode 100644 lib/Doctrine/Search/ZendLucene/Configuration.php delete mode 100644 lib/Doctrine/Search/ZendLucene/Connection.php diff --git a/lib/Doctrine/Search/Solr/Client.php b/lib/Doctrine/Search/Solr/Client.php deleted file mode 100755 index 6552b6d..0000000 --- a/lib/Doctrine/Search/Solr/Client.php +++ /dev/null @@ -1,97 +0,0 @@ -. - */ - -namespace Doctrine\Search\Solr; - -use Doctrine\Search\SearchClientInterface; -use Doctrine\Search\Mapping\ClassMetadata; -use Doctrine\Common\Persistence\ObjectManager; - -/** - * SearchManager for Solr-Backend - * - * @author Mike Lohmann - */ -class Client implements SearchClientInterface -{ - - private $config; - - private $connection; - - /* - * @param Connection $conn - * @param Configuration $config - */ - public function __construct(Connection $conn = null, Configuration $config = null) - { - $this->connection = $conn; - $this->config = $config; - } - - public function find($index, $type, $query) - { - - } - - public function createIndex($index, array $data) - { - - } - - public function createType(ClassMetadata $metadata) - { - - } - - public function deleteType(ClassMetadata $metadata) - { - - } - - public function getIndex($index) - { - - } - - public function deleteIndex($index) - { - - } - - public function refreshIndex($index) - { - - } - - public function addDocuments($index, $type, array $documents) - { - - } - - public function removeDocuments($index, $type, array $documents) - { - - } - - public function removeAll($index, $type) - { - - } -} diff --git a/lib/Doctrine/Search/Solr/Configuration.php b/lib/Doctrine/Search/Solr/Configuration.php deleted file mode 100755 index 052e519..0000000 --- a/lib/Doctrine/Search/Solr/Configuration.php +++ /dev/null @@ -1,35 +0,0 @@ -. - */ - -namespace Doctrine\Search\Solr; - -/** - * Configuration handler for Solr-Backend - * - * @author Mike Lohmann - */ -class Configuration -{ - - - public function __construct() - { - - } -} diff --git a/lib/Doctrine/Search/Solr/Connection.php b/lib/Doctrine/Search/Solr/Connection.php deleted file mode 100755 index 4bbd344..0000000 --- a/lib/Doctrine/Search/Solr/Connection.php +++ /dev/null @@ -1,51 +0,0 @@ -. - */ - -namespace Doctrine\Search\Solr; - -use Doctrine\Search\Http\ClientInterface as HttpClientInterface; - -/** - * Connections handler for Solr-Backend - * - * @author Mike Lohmann - */ -class Connection -{ - private $host; - - private $port; - - private $path; - - private $httpClient; - - public function __construct(HttpClientInterface $httpClient, $host = null, $port = null, $path = null) - { - $this->host = $host; - $this->port = $port; - $this->path = $path; - $this->httpClient = $httpClient; - } - - public function initialize() - { - - } -} diff --git a/lib/Doctrine/Search/ZendLucene/Client.php b/lib/Doctrine/Search/ZendLucene/Client.php deleted file mode 100644 index db3e7e1..0000000 --- a/lib/Doctrine/Search/ZendLucene/Client.php +++ /dev/null @@ -1,98 +0,0 @@ -. - */ - -namespace Doctrine\Search\ZendLucene; - - -use Doctrine\Search\SearchClientInterface; -use Doctrine\Search\Mapping\ClassMetadata; -use Doctrine\Common\Persistence\ObjectManager; - -/** - * SearchManager for ZendLucene-Backend - * - * @author Mike Lohmann - */ -class Client implements SearchClientInterface -{ - - private $config; - - private $connection; - - /* - * @param Connection $conn - * @param Configuration $config - */ - public function __construct(Connection $conn = null, Configuration $config = null) - { - $this->connection = $conn; - $this->config = $config; - } - - public function find($index, $type, $query) - { - - } - - public function createIndex($index, array $data) - { - - } - - public function createType(ClassMetadata $metadata) - { - - } - - public function deleteType(ClassMetadata $metadata) - { - - } - - public function getIndex($index) - { - - } - - public function deleteIndex($index) - { - - } - - public function refreshIndex($index) - { - - } - - public function addDocuments($index, $type, array $documents) - { - - } - - public function removeDocuments($index, $type, array $documents) - { - - } - - public function removeAll($index, $type) - { - - } -} diff --git a/lib/Doctrine/Search/ZendLucene/Configuration.php b/lib/Doctrine/Search/ZendLucene/Configuration.php deleted file mode 100644 index 871db67..0000000 --- a/lib/Doctrine/Search/ZendLucene/Configuration.php +++ /dev/null @@ -1,35 +0,0 @@ -. - */ - -namespace Doctrine\Search\ZendLucene; - -/** - * Configuration handler for ZendLucene-Backend - * - * @author Mike Lohmann - */ -class Configuration -{ - - - public function __construct() - { - - } -} diff --git a/lib/Doctrine/Search/ZendLucene/Connection.php b/lib/Doctrine/Search/ZendLucene/Connection.php deleted file mode 100644 index 13f6184..0000000 --- a/lib/Doctrine/Search/ZendLucene/Connection.php +++ /dev/null @@ -1,51 +0,0 @@ -. - */ - -namespace Doctrine\Search\ZendLucene; - -use Doctrine\Search\Http\ClientInterface as HttpClientInterface; - -/** - * Connections handler for ZendLucene-Backend - * - * @author Mike Lohmann - */ -class Connection -{ - private $host; - - private $port; - - private $path; - - private $httpClient; - - public function __construct(HttpClientInterface $httpClient, $host = null, $port = null, $path = null) - { - $this->host = $host; - $this->port = $port; - $this->path = $path; - $this->httpClient = $httpClient; - } - - public function initialize() - { - - } -} From b4d452b659a22f6e5a221a1b45d30e8dc8885940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:37:50 +0100 Subject: [PATCH 11/45] Drop broken AnnotationDriver --- .../Annotations/ElasticCompletionField.php | 56 ----- .../Mapping/Annotations/ElasticField.php | 84 ------- .../Mapping/Annotations/ElasticRoot.php | 79 ------- .../Mapping/Annotations/ElasticSearchable.php | 65 ----- .../Search/Mapping/Annotations/Field.php | 44 ---- .../Search/Mapping/Annotations/Id.php | 30 --- .../Search/Mapping/Annotations/Parameter.php | 39 --- .../Search/Mapping/Annotations/Searchable.php | 39 --- .../Search/Mapping/Annotations/SolrField.php | 31 --- .../Mapping/Driver/AnnotationDriver.php | 222 ------------------ 10 files changed, 689 deletions(-) delete mode 100644 lib/Doctrine/Search/Mapping/Annotations/ElasticCompletionField.php delete mode 100755 lib/Doctrine/Search/Mapping/Annotations/ElasticField.php delete mode 100644 lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php delete mode 100755 lib/Doctrine/Search/Mapping/Annotations/ElasticSearchable.php delete mode 100755 lib/Doctrine/Search/Mapping/Annotations/Field.php delete mode 100644 lib/Doctrine/Search/Mapping/Annotations/Id.php delete mode 100644 lib/Doctrine/Search/Mapping/Annotations/Parameter.php delete mode 100755 lib/Doctrine/Search/Mapping/Annotations/Searchable.php delete mode 100755 lib/Doctrine/Search/Mapping/Annotations/SolrField.php delete mode 100755 lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php diff --git a/lib/Doctrine/Search/Mapping/Annotations/ElasticCompletionField.php b/lib/Doctrine/Search/Mapping/Annotations/ElasticCompletionField.php deleted file mode 100644 index 318122a..0000000 --- a/lib/Doctrine/Search/Mapping/Annotations/ElasticCompletionField.php +++ /dev/null @@ -1,56 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping\Annotations; - -use Doctrine\Common\Annotations\Annotation; - - -/** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - */ -class ElasticCompletionField extends ElasticField -{ - /** - * @var string - */ - public $type = 'completion'; - - /** - * @var bool - */ - public $payloads = false; - - /** - * @var bool - */ - public $preserveSeparators = true; - - /** - * @var bool - */ - public $preservePositionIncrements = true; - - /** - * @var integer - */ - public $maxInputLength; - -} diff --git a/lib/Doctrine/Search/Mapping/Annotations/ElasticField.php b/lib/Doctrine/Search/Mapping/Annotations/ElasticField.php deleted file mode 100755 index 2fa8e2f..0000000 --- a/lib/Doctrine/Search/Mapping/Annotations/ElasticField.php +++ /dev/null @@ -1,84 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping\Annotations; - -use Doctrine\Common\Annotations\Annotation; - -/** - * @Annotation - * @Target({"PROPERTY", "METHOD", "ANNOTATION"}) - */ -class ElasticField extends Field -{ - /** - * @var boolean - */ - public $includeInAll; - - /** - * @var string - */ - public $index; - - /** - * @var array - */ - public $fields; - - /** - * @var array - */ - public $properties; - - /** - * @var string - */ - public $analyzer; - - /** - * @var string - */ - public $indexAnalyzer; - - /** - * @var string - */ - public $searchAnalyzer; - - /** - * @var string - */ - public $path; - - /** - * @var string - */ - public $indexName; - - /** - * @var boolean - */ - public $store; - - /** - * @var mixed - */ - public $nullValue; -} diff --git a/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php b/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php deleted file mode 100644 index 1f7f9f8..0000000 --- a/lib/Doctrine/Search/Mapping/Annotations/ElasticRoot.php +++ /dev/null @@ -1,79 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping\Annotations; - -use Doctrine\Common\Annotations\Annotation; - -/** - * @Annotation - * @Target({"CLASS"}) - */ -final class ElasticRoot extends Annotation -{ - /** - * @var string - */ - public $name; - - /** - * @var string - */ - public $id; - - /** - * @var string - */ - public $match; - - /** - * @var string - */ - public $unmatch; - - /** - * @var string - */ - public $pathMatch; - - /** - * @var string - */ - public $pathUnmatch; - - /** - * @var string - */ - public $matchPattern; - - /** - * @var string - */ - public $matchMappingType; - - /** - * @var mixed - */ - public $value; - - /** - * @var array|ElasticField[] - */ - public $mapping; -} diff --git a/lib/Doctrine/Search/Mapping/Annotations/ElasticSearchable.php b/lib/Doctrine/Search/Mapping/Annotations/ElasticSearchable.php deleted file mode 100755 index 297f23d..0000000 --- a/lib/Doctrine/Search/Mapping/Annotations/ElasticSearchable.php +++ /dev/null @@ -1,65 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping\Annotations; - -use Doctrine\Common\Annotations\Annotation; - -/** - * @Annotation - * @Target("CLASS") - */ -final class ElasticSearchable extends Searchable -{ - /** - * @var int $numberOfShards - */ - public $numberOfShards; - - /** - * @var int $numnberOfReplicas - */ - public $numberOfReplicas; - - /** - * @var string $op_type - */ - public $opType; - - /** - * @var string $parent - */ - public $parent; - - /** - * TTL in milliseconds - * @var int $timeToLive - */ - public $timeToLive; - - /** - * @var float - */ - public $boost; - - /** - * @var boolean - */ - public $source; -} diff --git a/lib/Doctrine/Search/Mapping/Annotations/Field.php b/lib/Doctrine/Search/Mapping/Annotations/Field.php deleted file mode 100755 index b0b74c6..0000000 --- a/lib/Doctrine/Search/Mapping/Annotations/Field.php +++ /dev/null @@ -1,44 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping\Annotations; - -use Doctrine\Common\Annotations\Annotation; - -/** - * @Annotation - * @Target("PROPERTY") - */ -class Field extends Annotation -{ - /** - * @var float - */ - public $boost; - - /** - * @var string - */ - public $type; - - /** - * @var string - */ - public $name; -} diff --git a/lib/Doctrine/Search/Mapping/Annotations/Id.php b/lib/Doctrine/Search/Mapping/Annotations/Id.php deleted file mode 100644 index 6234014..0000000 --- a/lib/Doctrine/Search/Mapping/Annotations/Id.php +++ /dev/null @@ -1,30 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping\Annotations; - -use Doctrine\Common\Annotations\Annotation; - -/** - * @Annotation - * @Target({"PROPERTY"}) - */ -final class Id extends Annotation -{ -} diff --git a/lib/Doctrine/Search/Mapping/Annotations/Parameter.php b/lib/Doctrine/Search/Mapping/Annotations/Parameter.php deleted file mode 100644 index 9df7773..0000000 --- a/lib/Doctrine/Search/Mapping/Annotations/Parameter.php +++ /dev/null @@ -1,39 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping\Annotations; - -use Doctrine\Common\Annotations\Annotation; - -/** - * @Annotation - * @Target({"PROPERTY"}) - */ -final class Parameter extends Annotation -{ - /** - * @var string - */ - public $name; - - /** - * @var string - */ - public $type = 'string'; -} diff --git a/lib/Doctrine/Search/Mapping/Annotations/Searchable.php b/lib/Doctrine/Search/Mapping/Annotations/Searchable.php deleted file mode 100755 index 9ba42ac..0000000 --- a/lib/Doctrine/Search/Mapping/Annotations/Searchable.php +++ /dev/null @@ -1,39 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping\Annotations; - -use Doctrine\Common\Annotations\Annotation; - -/** - * @Annotation - * @Target("CLASS") - */ -class Searchable extends Annotation -{ - /** - * @var string $index - */ - public $index; - - /** - * @var string $type - */ - public $type; -} diff --git a/lib/Doctrine/Search/Mapping/Annotations/SolrField.php b/lib/Doctrine/Search/Mapping/Annotations/SolrField.php deleted file mode 100755 index 0b9734d..0000000 --- a/lib/Doctrine/Search/Mapping/Annotations/SolrField.php +++ /dev/null @@ -1,31 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping\Annotations; - -use Doctrine\Common\Annotations\Annotation; - -/** - * @Annotation - * @Target("PROPERTY") - */ -final class SolrField extends Field -{ - /* configuration */ -} diff --git a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php deleted file mode 100755 index 476d35d..0000000 --- a/lib/Doctrine/Search/Mapping/Driver/AnnotationDriver.php +++ /dev/null @@ -1,222 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping\Driver; - -use Doctrine\Common\Annotations\Annotation; -use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver as AbstractAnnotationDriver; -use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; -use Doctrine\Search\Mapping\Annotations as Search; -use Doctrine\Common\Persistence\Mapping\ClassMetadata; -use Doctrine\Search\Exception\Driver as DriverException; -use Doctrine\Search\Mapping\DependentMappingDriver; - - - -/** - * The AnnotationDriver reads the mapping metadata from docblock annotations. - * - * @link www.doctrine-project.org - * @since 1.0 - * @author Mike Lohmann - */ -class AnnotationDriver extends AbstractAnnotationDriver implements DependentMappingDriver -{ - /** - * {@inheritDoc} - */ - protected $entityAnnotationClasses = array( - 'Doctrine\\Search\\Mapping\\Annotations\\Searchable' => 1, - 'Doctrine\\Search\\Mapping\\Annotations\\ElasticSearchable' => 2, - 'Doctrine\\Search\\Mapping\\Annotations\\ElasticRoot' => 3, - ); - - /** - * Document fields annotation classes, ordered by precedence. - */ - protected $entityFieldAnnotationClasses = array( - 'Doctrine\\Search\\Mapping\\Annotations\\Id', //Only here for convenience - 'Doctrine\\Search\\Mapping\\Annotations\\Parameter', //Only here for convenience - 'Doctrine\\Search\\Mapping\\Annotations\\Field', - 'Doctrine\\Search\\Mapping\\Annotations\\ElasticField', - 'Doctrine\\Search\\Mapping\\Annotations\\SolrField', - ); - - /** - * @var MappingDriver - */ - private $parentDriver; - - /** - * @param MappingDriver $driver - */ - public function setParentDriver(MappingDriver $driver) - { - $this->parentDriver = $driver; - } - - /** - * @param string $className - * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata - * - * @throws \ReflectionException - */ - public function loadMetadataForClass($className, ClassMetadata $metadata) - { - $reflClass = $metadata->getReflectionClass(); - $this->extractClassAnnotations($reflClass, $metadata); - $this->extractPropertiesAnnotations($reflClass, $metadata); - $this->extractMethodsAnnotations($reflClass, $metadata); - } - - /** - * This function extracts the class annotations for search from the given reflected class - * and writes them into metadata. - * - * @param \ReflectionClass $reflClass - * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata - * - * @throws DriverException\ClassIsNotAValidDocumentException|DriverException\PropertyDoesNotExistsInMetadataException - */ - private function extractClassAnnotations(\ReflectionClass $reflClass, ClassMetadata $metadata) - { - $documentsClassAnnotations = array(); - foreach ($this->reader->getClassAnnotations($reflClass) as $annotation) { - if ($annotation instanceof Search\ElasticRoot) { - $metadata->addRootMapping($annotation); - continue; - } - - foreach ($this->entityAnnotationClasses as $annotationClass => $index) { - if ($annotation instanceof $annotationClass) { - $documentsClassAnnotations[$index] = $annotation; - break; - } - } - } - - if (!$documentsClassAnnotations) { - throw new DriverException\ClassIsNotAValidDocumentException($metadata->getName()); - } - - // choose only one (the first one) - $annotationClass = reset($documentsClassAnnotations); - $this->addValuesToMetadata($metadata, $annotationClass); - } - - /** - * Extract the property annotations. - * - * @param \ReflectionClass $class - * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata - */ - private function extractPropertiesAnnotations(\ReflectionClass $class, ClassMetadata $metadata) - { - foreach ($class->getProperties() as $reflProperty) { - foreach ($this->reader->getPropertyAnnotations($reflProperty) as $annotation) { - foreach ($this->entityFieldAnnotationClasses as $fieldAnnotationClass) { - if ($annotation instanceof $fieldAnnotationClass) { - if ($annotation instanceof Search\Id) { - $metadata->setIdentifier($reflProperty->name); - - } elseif ($annotation instanceof Search\Parameter) { - $metadata->addParameterMapping($reflProperty, array( - 'name' => $annotation->name, - 'type' => $annotation->type, - )); - - } else { - $metadata->addFieldMapping($reflProperty, $annotation); - } - - continue 2; - } - } - } - } - } - - /** - * Extract the methods annotations. - * - * @param \ReflectionClass $class - * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata - * - * @return ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata - */ - private function extractMethodsAnnotations(\ReflectionClass $class, ClassMetadata $metadata) - { - foreach ($class->getMethods() as $reflMethod) { - foreach ($this->reader->getMethodAnnotations($reflMethod) as $annotation) { - foreach ($this->entityFieldAnnotationClasses as $fieldAnnotationClass) { - if ($annotation instanceof $fieldAnnotationClass) { - $metadata->addMethodMapping($reflMethod, $annotation); - continue 2; - } - } - } - } - } - - /** - * Iterates the given annotation class - * - * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata - * @param Annotation $class - * - * @throws DriverException\PropertyDoesNotExistsInMetadataException - */ - private function addValuesToMetadata(ClassMetadata $metadata, $class) - { - $reflClassAnnotations = new \ReflectionClass($class); - - foreach ($reflClassAnnotations->getProperties() as $reflectedProperty) { - $propertyName = $reflectedProperty->getName(); - - if (false === property_exists($metadata, $propertyName)) { - throw new DriverException\PropertyDoesNotExistsInMetadataException($reflectedProperty->getName()); - } - - if (!is_null($class->$propertyName)) { - $metadata->$propertyName = $class->$propertyName; - } - } - } - - public function getAllClassNames() - { - if ($this->classNames !== NULL) { - return $this->classNames; - } - - if ($this->parentDriver === NULL) { - return parent::getAllClassNames(); - } - - $classes = array(); - foreach ($this->parentDriver->getAllClassNames() as $className) { - if (!$this->isTransient($className)) { - $classes[] = $className; - } - } - - return $this->classNames = $classes; - } - -} From cd2a48a0af514a8bad93842f62ab3af21fab1299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:38:20 +0100 Subject: [PATCH 12/45] refactor exceptions --- demo/index.php | 2 +- .../Exception/DoctrineSearchException.php | 24 ----- .../ClassIsNotAValidDocumentException.php | 26 ------ ...opertyDoesNotExistsInMetadataException.php | 26 ------ .../Search/Exception/NoResultException.php | 34 ------- .../Exception/UnexpectedTypeException.php | 35 ------- lib/Doctrine/Search/exceptions.php | 91 +++++++++++++++++++ 7 files changed, 92 insertions(+), 146 deletions(-) delete mode 100644 lib/Doctrine/Search/Exception/DoctrineSearchException.php delete mode 100644 lib/Doctrine/Search/Exception/Driver/ClassIsNotAValidDocumentException.php delete mode 100644 lib/Doctrine/Search/Exception/Driver/PropertyDoesNotExistsInMetadataException.php delete mode 100644 lib/Doctrine/Search/Exception/NoResultException.php delete mode 100644 lib/Doctrine/Search/Exception/UnexpectedTypeException.php create mode 100644 lib/Doctrine/Search/exceptions.php diff --git a/demo/index.php b/demo/index.php index 9b92047..266a4ca 100644 --- a/demo/index.php +++ b/demo/index.php @@ -34,7 +34,7 @@ echo PHP_EOL."*** Single lookup with no results ***".PHP_EOL; try { $user = $sm->find('Doctrine\Tests\Models\Comments\User', 'unknownid'); -} catch (Doctrine\Search\Exception\NoResultException $exception) { +} catch (\Doctrine\Search\NoResultException $exception) { print_r($exception->getMessage()); echo PHP_EOL; } diff --git a/lib/Doctrine/Search/Exception/DoctrineSearchException.php b/lib/Doctrine/Search/Exception/DoctrineSearchException.php deleted file mode 100644 index 3ed9b09..0000000 --- a/lib/Doctrine/Search/Exception/DoctrineSearchException.php +++ /dev/null @@ -1,24 +0,0 @@ -. - */ - -namespace Doctrine\Search\Exception; - -class DoctrineSearchException extends \Exception -{ -} diff --git a/lib/Doctrine/Search/Exception/Driver/ClassIsNotAValidDocumentException.php b/lib/Doctrine/Search/Exception/Driver/ClassIsNotAValidDocumentException.php deleted file mode 100644 index 1f9a10b..0000000 --- a/lib/Doctrine/Search/Exception/Driver/ClassIsNotAValidDocumentException.php +++ /dev/null @@ -1,26 +0,0 @@ -. - */ - -namespace Doctrine\Search\Exception\Driver; - -use Doctrine\Search\Exception\DoctrineSearchException; - -class ClassIsNotAValidDocumentException extends DoctrineSearchException -{ -} diff --git a/lib/Doctrine/Search/Exception/Driver/PropertyDoesNotExistsInMetadataException.php b/lib/Doctrine/Search/Exception/Driver/PropertyDoesNotExistsInMetadataException.php deleted file mode 100644 index d3a52a9..0000000 --- a/lib/Doctrine/Search/Exception/Driver/PropertyDoesNotExistsInMetadataException.php +++ /dev/null @@ -1,26 +0,0 @@ -. - */ - -namespace Doctrine\Search\Exception\Driver; - -use Doctrine\Search\Exception\DoctrineSearchException; - -class PropertyDoesNotExistsInMetadataException extends DoctrineSearchException -{ -} diff --git a/lib/Doctrine/Search/Exception/NoResultException.php b/lib/Doctrine/Search/Exception/NoResultException.php deleted file mode 100644 index 97f8519..0000000 --- a/lib/Doctrine/Search/Exception/NoResultException.php +++ /dev/null @@ -1,34 +0,0 @@ -. - */ - -namespace Doctrine\Search\Exception; - -/** - * Exception thrown when an search query unexpectedly does not return any results. - * - * @author robo - * @since 2.0 - */ -class NoResultException extends DoctrineSearchException -{ - public function __construct($message = null) - { - parent::__construct($message ?: 'No result was found for query although at least one row was expected.'); - } -} diff --git a/lib/Doctrine/Search/Exception/UnexpectedTypeException.php b/lib/Doctrine/Search/Exception/UnexpectedTypeException.php deleted file mode 100644 index 7fa82c7..0000000 --- a/lib/Doctrine/Search/Exception/UnexpectedTypeException.php +++ /dev/null @@ -1,35 +0,0 @@ -. - */ - -namespace Doctrine\Search\Exception; - -class UnexpectedTypeException extends DoctrineSearchException -{ - - public function __construct($value, $expected) - { - parent::__construct( - sprintf( - 'Expected argument of type "%s", "%s" given', - $expected, - (is_object($value) ? get_class($value) : gettype($value)) - ) - ); - } -} diff --git a/lib/Doctrine/Search/exceptions.php b/lib/Doctrine/Search/exceptions.php new file mode 100644 index 0000000..216a664 --- /dev/null +++ b/lib/Doctrine/Search/exceptions.php @@ -0,0 +1,91 @@ + Date: Mon, 29 Dec 2014 01:38:58 +0100 Subject: [PATCH 13/45] Fixed JMSSerializer --- .../Search/Serializer/JMSSerializer.php | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/lib/Doctrine/Search/Serializer/JMSSerializer.php b/lib/Doctrine/Search/Serializer/JMSSerializer.php index 8ed383f..b153ac3 100644 --- a/lib/Doctrine/Search/Serializer/JMSSerializer.php +++ b/lib/Doctrine/Search/Serializer/JMSSerializer.php @@ -20,33 +20,66 @@ namespace Doctrine\Search\Serializer; use Doctrine\Search\SerializerInterface; -use JMS\Serializer\SerializerBuilder; -use JMS\Serializer\SerializationContext; -use JMS\Serializer\Naming\SerializedNameAnnotationStrategy; use JMS\Serializer\Naming\IdenticalPropertyNamingStrategy; +use JMS\Serializer\Naming\SerializedNameAnnotationStrategy; +use JMS\Serializer\SerializationContext; +use JMS\Serializer\Serializer; +use JMS\Serializer\SerializerBuilder; + + class JMSSerializer implements SerializerInterface { + + /** + * @var Serializer + */ protected $serializer; + + /** + * @var SerializationContext + */ protected $context; - public function __construct(SerializationContext $context = null) + + + public function __construct(Serializer $serializer = NULL, SerializationContext $context = NULL) { $this->context = $context; - $this->serializer = SerializerBuilder::create() - ->setPropertyNamingStrategy(new SerializedNameAnnotationStrategy(new IdenticalPropertyNamingStrategy())) - ->addDefaultHandlers() - ->build(); + $this->serializer = $serializer; } + + public function serialize($object) { - $context = $this->context ? clone $this->context : null; - return json_decode($this->serializer->serialize($object, 'json', $context), true); + $context = $this->context ? clone $this->context : NULL; + + return json_decode($this->getSerializer()->serialize($object, 'json', $context), TRUE); } + + public function deserialize($entityName, $data) { - return $this->serializer->deserialize($data, $entityName, 'json'); + return $this->getSerializer()->deserialize($data, $entityName, 'json'); + } + + + + /** + * @return Serializer + */ + protected function getSerializer() + { + if (!$this->serializer === NULL) { + $this->serializer = SerializerBuilder::create() + ->setPropertyNamingStrategy(new SerializedNameAnnotationStrategy(new IdenticalPropertyNamingStrategy())) + ->addDefaultHandlers() + ->build(); + } + + return $this->serializer; } + } From b614546159a6c685235255727c3692456a5e5aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:39:08 +0100 Subject: [PATCH 14/45] dead code AnnotationSerializer --- .../Serializer/AnnotationSerializer.php | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 lib/Doctrine/Search/Serializer/AnnotationSerializer.php diff --git a/lib/Doctrine/Search/Serializer/AnnotationSerializer.php b/lib/Doctrine/Search/Serializer/AnnotationSerializer.php deleted file mode 100644 index 0ea6330..0000000 --- a/lib/Doctrine/Search/Serializer/AnnotationSerializer.php +++ /dev/null @@ -1,33 +0,0 @@ -. - */ - -namespace Doctrine\Search\Serializer; - -use Doctrine\Search\SerializerInterface; - -class AnnotationSerializer implements SerializerInterface -{ - public function serialize($object) - { - } - - public function deserialize($entityName, $data) - { - } -} From 2f5c8824cf772867b03b3a58a8d330c8f90abd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:39:20 +0100 Subject: [PATCH 15/45] introduced ChainSerializer --- .../Search/Serializer/ChainSerializer.php | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 lib/Doctrine/Search/Serializer/ChainSerializer.php diff --git a/lib/Doctrine/Search/Serializer/ChainSerializer.php b/lib/Doctrine/Search/Serializer/ChainSerializer.php new file mode 100644 index 0000000..d1b8281 --- /dev/null +++ b/lib/Doctrine/Search/Serializer/ChainSerializer.php @@ -0,0 +1,84 @@ + + */ +class ChainSerializer implements SerializerInterface +{ + + /** + * @var SerializerInterface[] + */ + private $serializers = []; + + /** + * @var SerializerInterface + */ + private $defaultSerializer; + + + + public function addSerializer($classType, SerializerInterface $serializer) + { + $this->serializers[strtolower($classType)] = $serializer; + } + + + + public function setDefaultSerializer(SerializerInterface $serializer) + { + $this->defaultSerializer = $serializer; + } + + + + /** + * @param object $object + * @throws DefaultSerializerNotProvidedException + * @return string + */ + public function serialize($object) + { + $lName = strtolower(ClassUtils::getClass($object)); + if (isset($this->serializers[$lName])) { + return $this->serializers[$lName]->serialize($object); + } + + if (!$this->defaultSerializer) { + throw new DefaultSerializerNotProvidedException; + } + + return $this->defaultSerializer->serialize($object); + } + + + + /** + * @param string $entityName + * @param string $data + * @throws DefaultSerializerNotProvidedException + * @return object + */ + public function deserialize($entityName, $data) + { + $lName = strtolower($entityName); + if (isset($this->serializers[$lName])) { + return $this->serializers[$lName]->deserialize($entityName, $data); + } + + if (!$this->defaultSerializer) { + throw new DefaultSerializerNotProvidedException; + } + + return $this->defaultSerializer->deserialize($entityName, $data); + } + +} From df012e2ea0cc0929ca9d356b5f14bd498880e583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:39:47 +0100 Subject: [PATCH 16/45] Added default optional SearchableListener & Searchable interface --- lib/Doctrine/Search/Searchable.php | 26 ++++++++ lib/Doctrine/Search/SearchableListener.php | 76 ++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 lib/Doctrine/Search/Searchable.php create mode 100644 lib/Doctrine/Search/SearchableListener.php diff --git a/lib/Doctrine/Search/Searchable.php b/lib/Doctrine/Search/Searchable.php new file mode 100644 index 0000000..dd6999c --- /dev/null +++ b/lib/Doctrine/Search/Searchable.php @@ -0,0 +1,26 @@ + + */ +interface Searchable +{ + +// /** +// * @return array +// */ +// public function toArray(); + + + +// /** +// * @param array $data +// * @return void +// */ +// public function fromArray($data); + +} diff --git a/lib/Doctrine/Search/SearchableListener.php b/lib/Doctrine/Search/SearchableListener.php new file mode 100644 index 0000000..1a4298e --- /dev/null +++ b/lib/Doctrine/Search/SearchableListener.php @@ -0,0 +1,76 @@ + + */ +class SearchableListener implements EventSubscriber +{ + + /** + * @var SearchManager + */ + private $sm; + + + + public function __construct(SearchManager $sm) + { + $this->sm = $sm; + } + + + + public function getSubscribedEvents() + { + return array( + Doctrine\ORM\Events::prePersist => 'prePersist', + Doctrine\ORM\Events::preUpdate => 'prePersist', + Doctrine\ORM\Events::preRemove => 'preRemove', + Doctrine\ORM\Events::postFlush => 'postFlush', + ); + } + + + + public function prePersist(LifecycleEventArgs $oArgs) + { + $oEntity = $oArgs->getEntity(); + if ($oEntity instanceof Searchable) { + $this->sm->persist($oEntity); + } + } + + + + public function preRemove(LifecycleEventArgs $oArgs) + { + $oEntity = $oArgs->getEntity(); + if ($oEntity instanceof Searchable) { + $this->sm->remove($oEntity); + } + } + + + + public function postFlush() + { + $this->sm->flush(); + } + +} From 301c7effaf0d7c9699c77bb8407e8a6abfcce2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:40:04 +0100 Subject: [PATCH 17/45] Fixed search events --- .../Search/Event/LifecycleEventArgs.php | 9 +-- .../Event/LoadClassMetadataEventArgs.php | 2 +- .../Search/Event/PostLoadEventArgs.php | 66 +++++++++++++++++++ 3 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 lib/Doctrine/Search/Event/PostLoadEventArgs.php diff --git a/lib/Doctrine/Search/Event/LifecycleEventArgs.php b/lib/Doctrine/Search/Event/LifecycleEventArgs.php index f9efa73..05914bc 100644 --- a/lib/Doctrine/Search/Event/LifecycleEventArgs.php +++ b/lib/Doctrine/Search/Event/LifecycleEventArgs.php @@ -20,6 +20,7 @@ namespace Doctrine\Search\Event; use Doctrine\Common\EventArgs; +use Doctrine\Search\Searchable; use Doctrine\Search\SearchManager; /** @@ -46,19 +47,19 @@ class LifecycleEventArgs extends EventArgs /** * Constructor * - * @param object $entity - * @param \Doctrine\Search\SearchManager $em + * @param Searchable $entity + * @param \Doctrine\Search\SearchManager $sm */ public function __construct($entity, SearchManager $sm) { $this->entity = $entity; - $this->sm = $sm; + $this->sm = $sm; } /** * Retrieve associated Entity. * - * @return object + * @return Searchable */ public function getEntity() { diff --git a/lib/Doctrine/Search/Event/LoadClassMetadataEventArgs.php b/lib/Doctrine/Search/Event/LoadClassMetadataEventArgs.php index 471af56..e932ce8 100644 --- a/lib/Doctrine/Search/Event/LoadClassMetadataEventArgs.php +++ b/lib/Doctrine/Search/Event/LoadClassMetadataEventArgs.php @@ -46,7 +46,7 @@ class LoadClassMetadataEventArgs extends EventArgs * Constructor. * * @param \Doctrine\Search\Mapping\ClassMetadata $classMetadata - * @param \Doctrine\Search\EntityManager $sm + * @param \Doctrine\Search\SearchManager $sm */ public function __construct(ClassMetadata $classMetadata, SearchManager $sm) { diff --git a/lib/Doctrine/Search/Event/PostLoadEventArgs.php b/lib/Doctrine/Search/Event/PostLoadEventArgs.php new file mode 100644 index 0000000..10b590b --- /dev/null +++ b/lib/Doctrine/Search/Event/PostLoadEventArgs.php @@ -0,0 +1,66 @@ +. + */ + +namespace Doctrine\Search\Event; + +use Doctrine\Common\EventArgs; +use Doctrine\Search\Searchable; +use Doctrine\Search\SearchManager; + +/** + * Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions + * of entities. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Roman Borschel + * @author Benjamin Eberlei + */ +class PostLoadEventArgs extends LifecycleEventArgs +{ + + /** + * @var array + */ + private $data; + + + + /** + * @param Searchable $entity + * @param array $data + * @param SearchManager $sm + */ + public function __construct($entity, $data, SearchManager $sm) + { + parent::__construct($entity, $sm); + $this->data = $data; + } + + + + /** + * @return array + */ + public function getRawData() + { + return $this->data; + } + +} From 33984fb0e2457c78cc42d5b4d1073e72bacc21dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:41:03 +0100 Subject: [PATCH 18/45] Refactored mapping; Introduced NeonDriver; Introduced decorator DependentMappingDriver --- .../ElasticSearch/Mapping/TypeMetadata.php | 41 +++ lib/Doctrine/Search/Mapping/ClassMetadata.php | 347 ++++++------------ .../Search/Mapping/ClassMetadataFactory.php | 67 +++- .../Search/Mapping/DependentMappingDriver.php | 41 --- .../Mapping/Driver/DependentMappingDriver.php | 122 ++++++ .../Search/Mapping/Driver/NeonDriver.php | 207 +++++++++++ lib/Doctrine/Search/Mapping/Helpers.php | 44 +++ lib/Doctrine/Search/Mapping/IndexMetadata.php | 43 +++ lib/Doctrine/Search/Mapping/TypeMetadata.php | 84 +++++ .../Search/Mapping/TypeMetadataFactory.php | 22 ++ ...chClientInterface.php => SearchClient.php} | 68 ++-- 11 files changed, 756 insertions(+), 330 deletions(-) create mode 100644 lib/Doctrine/Search/ElasticSearch/Mapping/TypeMetadata.php delete mode 100644 lib/Doctrine/Search/Mapping/DependentMappingDriver.php create mode 100644 lib/Doctrine/Search/Mapping/Driver/DependentMappingDriver.php create mode 100644 lib/Doctrine/Search/Mapping/Driver/NeonDriver.php create mode 100644 lib/Doctrine/Search/Mapping/Helpers.php create mode 100644 lib/Doctrine/Search/Mapping/IndexMetadata.php create mode 100644 lib/Doctrine/Search/Mapping/TypeMetadata.php create mode 100644 lib/Doctrine/Search/Mapping/TypeMetadataFactory.php rename lib/Doctrine/Search/{SearchClientInterface.php => SearchClient.php} (74%) diff --git a/lib/Doctrine/Search/ElasticSearch/Mapping/TypeMetadata.php b/lib/Doctrine/Search/ElasticSearch/Mapping/TypeMetadata.php new file mode 100644 index 0000000..7863b45 --- /dev/null +++ b/lib/Doctrine/Search/ElasticSearch/Mapping/TypeMetadata.php @@ -0,0 +1,41 @@ + + */ +class TypeMetadata extends Search\Mapping\TypeMetadata +{ + + /** + * @var array + */ + public $settings = array(); + + /** + * @var array + */ + public $properties = array(); + + + + protected function validateSettings(array $options) + { + + } + + + + protected function validateProperty($name, array $property) + { + if (!isset($property['type'])) { + throw new Search\InvalidMetadataException("Type of property is mandatory."); + } + } + +} diff --git a/lib/Doctrine/Search/Mapping/ClassMetadata.php b/lib/Doctrine/Search/Mapping/ClassMetadata.php index af1dc19..ae2f2f2 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadata.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadata.php @@ -20,8 +20,7 @@ namespace Doctrine\Search\Mapping; use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface; -use Doctrine\Search\Mapping\Annotations\ElasticField; -use Doctrine\Search\Mapping\Annotations\ElasticRoot; +use Doctrine\Common\Persistence\Mapping\ReflectionService; @@ -39,152 +38,82 @@ * get the whole class name, namespace inclusive, prepended to every property in * the serialized representation). * - * @link www.doctrine-project.com - * @since 1.0 - * @author Mike Lohmann + * @author Filip Procházka */ class ClassMetadata implements ClassMetadataInterface { /** - * @var string - */ - public $index; - - /** - * @var string + * @todo allow mapping to multiple types + * @var TypeMetadata */ public $type; /** - * @var int - */ - public $numberOfShards = 1; - - /** - * @var int - */ - public $numberOfReplicas = 0; - - /** - * @var int + * @todo allow mapping to multiple indexes + * @var IndexMetadata */ - public $opType = 1; + public $index; /** * @var string */ public $parent; - /** - * @var int - */ - public $timeToLive = 1; - - /** - * @var int - */ - public $value = 1; - - /** - * @var boolean - */ - public $source = true; - - /** - * @var float - */ - public $boost; - /** * @var string */ public $className; /** - * @var array|ElasticField[] + * @var ClassMetadataInterface|\Doctrine\ORM\Mapping\ClassMetadata */ - public $fieldMappings = array(); + private $parentMetadata; - /** - * @var array|ElasticField[] - */ - public $methodMappings = array(); - /** - * Additional root annotations of the mapped class. - * - * @var array|ElasticRoot[] - */ - public $rootMappings = array(); - /** - * The ReflectionProperty parameters of the mapped class. - * - * @var array - */ - public $parameters = array(); + public function __construct($entityName) + { + $this->className = $entityName; + } - /** - * The ReflectionClass instance of the mapped class. - * - * @var \ReflectionClass - */ - public $reflClass; - /** - * The ReflectionClass instance of the mapped class. - * - * @var \ReflectionProperty[]|\ReflectionMethod[] - */ - public $reflFields; /** - * READ-ONLY: The field names of all fields that are part of the identifier/primary key - * of the mapped entity class. - * - * @var mixed - */ - public $identifier = array(); - - - public function __construct($documentName) - { - $this->className = $documentName; - $this->reflClass = new \ReflectionClass($documentName); - } - - /** Determines which fields get serialized. - * - * It is only serialized what is necessary for best unserialization performance. - * - * Parts that are also NOT serialized because they can not be properly unserialized: - * - reflClass (ReflectionClass) - * - reflFields (ReflectionProperty array) - * * @return array The names of all the fields that should be serialized. */ public function __sleep() { // This metadata is always serialized/cached. return array( - 'boost', - 'className', - 'methodMappings', - 'fieldMappings', - 'parameters', + 'type', 'index', - 'numberOfReplicas', - 'numberOfShards', - 'opType', 'parent', - 'timeToLive', - 'type', - 'value', - 'identifier', - 'rootMappings' + 'className', ); } + + + /** + * @return string + */ + public function getIndexName() + { + return $this->index->name; + } + + + + /** + * @return string + */ + public function getTypeName() + { + return $this->type->name; + } + + + /** * Get fully-qualified class name of this persistent class. * @@ -193,32 +122,23 @@ public function __sleep() public function getName() { return $this->className; - } + + /** * Gets the mapped identifier field name. * * The returned structure is an array of the identifier field names. * - * @return array + * @return string */ public function getIdentifier() { - return $this->identifier; + return $this->parentMetadata->getSingleIdentifierFieldName(); } - /** - * INTERNAL: - * Sets the mapped identifier key field of this class. - * Mainly used by the ClassMetadataFactory to assign inherited identifiers. - * - * @param mixed $identifier - */ - public function setIdentifier($identifier) - { - $this->identifier = $identifier; - } + /** * Gets the ReflectionClass instance for this mapped class. @@ -227,117 +147,81 @@ public function setIdentifier($identifier) */ public function getReflectionClass() { - return $this->reflClass; + return $this->parentMetadata->getReflectionClass(); } + + /** * Checks if the given field name is a mapped identifier for this class. * * @param string $fieldName + * * @return boolean */ public function isIdentifier($fieldName) { - return $this->identifier === $fieldName; + return $this->parentMetadata->isIdentifier($fieldName); } + + /** * Checks if the given field is a mapped property for this class. * * @param string $fieldName + * * @return boolean */ public function hasField($fieldName) { - return false; + return $this->parentMetadata->hasField($fieldName); } - /** - * This mapping is used in the _wakeup-method to set the reflFields after _sleep. - * - * @param \ReflectionProperty $field - * @param array $mapping - */ - public function addFieldMapping(\Reflector $field, $mapping = array()) - { - $this->fieldMappings[$field->getName()] = $mapping; - $this->reflFields[$field->getName()] = $field; - } - - /** - * This mapping is used in the _wakeup-method to set the reflFields after _sleep. - * - * @param \ReflectionProperty $field - * @param array $mapping - */ - public function addMethodMapping(\Reflector $field, $mapping = array()) - { - $this->methodMappings[$field->getName()] = $mapping; - $this->reflFields[$field->getName()] = $field; - } - - /** - * @param array $mapping - */ - public function addRootMapping($mapping = array()) - { - $this->rootMappings[] = $mapping; - } - - /** - * This mapping is used in the _wakeup-method to set the parameters after _sleep. - * - * @param string $fieldName - * @param array $mapping - */ - public function addParameterMapping($fieldName, $mapping = array()) - { - $this->parameters[$fieldName] = $mapping; - } - - /** - * @param \ReflectionProperty $field - */ - /*public function addField(\ReflectionProperty $field) - { - $fieldName = $field->getName(); - $this->reflFields[] = $field; - }*/ /** * Checks if the given field is a mapped association for this class. * * @param string $fieldName + * * @return boolean */ public function hasAssociation($fieldName) { - return false; + return $this->parentMetadata->hasAssociation($fieldName); } + + /** * Checks if the given field is a mapped single valued association for this class. * * @param string $fieldName + * * @return boolean */ public function isSingleValuedAssociation($fieldName) { - return false; + return $this->parentMetadata->isSingleValuedAssociation($fieldName); } + + /** * Checks if the given field is a mapped collection valued association for this class. * * @param string $fieldName + * * @return boolean */ public function isCollectionValuedAssociation($fieldName) { - return false; + return $this->parentMetadata->isCollectionValuedAssociation($fieldName); } + + /** * A numerically indexed list of field names of this persistent class. * @@ -347,19 +231,37 @@ public function isCollectionValuedAssociation($fieldName) */ public function getFieldNames() { - return array_keys($this->reflFields); + return $this->parentMetadata->getFieldNames(); } + + /** - * Currently not necessary but needed by Interface + * Returns an array of identifier field names numerically indexed. + * + * @return array + */ + public function getIdentifierFieldNames() + { + return $this->parentMetadata->getIdentifierFieldNames(); + } + + + + /** + * Returns a numerically indexed list of association names of this persistent class. + * + * This array includes identifier associations if present on this class. * * @return array */ public function getAssociationNames() { - return array(); + return $this->parentMetadata->getAssociationNames(); } + + /** * Returns a type name of this field. * @@ -367,92 +269,77 @@ public function getAssociationNames() * integer, string, boolean, float/double, datetime. * * @param string $fieldName + * * @return string */ public function getTypeOfField($fieldName) { - //@todo: check if $field exists - return gettype($this->$fieldName); + return $this->parentMetadata->getTypeOfField($fieldName); } + + /** - * Currently not necessary but needed by Interface - * + * Returns the target class name of the given association. * * @param string $assocName + * * @return string */ public function getAssociationTargetClass($assocName) { - return ''; + return $this->parentMetadata->getAssociationTargetClass($assocName); } - public function isAssociationInverseSide($assocName) - { - return ''; - } - public function getAssociationMappedByTargetField($assocName) - { - return ''; - } /** - * Return the identifier of this object as an array with field name as key. + * Checks if the association is the inverse side of a bidirectional association. * - * Has to return an empty array if no identifier isset. + * @param string $assocName * - * @param object $object - * @return array + * @return boolean */ - public function getIdentifierValues($object) + public function isAssociationInverseSide($assocName) { - // TODO: Implement getIdentifierValues() method. + return $this->parentMetadata->isAssociationInverseSide($assocName); } + + /** - * Returns an array of identifier field names numerically indexed. + * Returns the target field of the owning side of the association. * - * @return array + * @param string $assocName + * + * @return string */ - public function getIdentifierFieldNames() + public function getAssociationMappedByTargetField($assocName) { - // TODO: Implement getIdentifierFieldNames() method. + return $this->parentMetadata->getAssociationMappedByTargetField($assocName); } + + /** - * Restores some state that can not be serialized/unserialized. + * Returns the identifier of this object as an array with field name as key. + * + * Has to return an empty array if no identifier isset. * - * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService + * @param object $object * - * @return void + * @return array */ - public function wakeupReflection($reflService) + public function getIdentifierValues($object) { - // Restore ReflectionClass and properties - $this->reflClass = $reflService->getClass($this->className); + return $this->parentMetadata->getIdentifierValues($object); + } - foreach ($this->fieldMappings as $field => $mapping) { - $this->reflFields[$field] = $reflService->getAccessibleProperty($this->className, $field); - } - foreach ($this->methodMappings as $field => $mapping) { - $this->reflFields[$field] = $reflService->getClass($this->className)->getMethod($field); - $this->reflFields[$field]->setAccessible(TRUE); - } - } - /** - * Initializes a new ClassMetadata instance that will hold the object-relational mapping - * metadata of the class with the given name. - * - * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service. - * - * @return void - */ - public function initializeReflection($reflService) + public function wakeupReflection(ReflectionService $reflService, ClassMetadataInterface $parentMetadata) { - $this->reflClass = $reflService->getClass($this->className); - $this->className = $this->reflClass->getName(); // normalize classname + $this->parentMetadata = $parentMetadata; } + } diff --git a/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php b/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php index a4c647f..91408c9 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php @@ -19,7 +19,9 @@ namespace Doctrine\Search\Mapping; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain; use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Search\Mapping\Driver\DependentMappingDriver; use Doctrine\Search\SearchManager; use Doctrine\Search\Configuration; use Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory; @@ -61,23 +63,41 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory */ private $evm; + /** + * @var AbstractClassMetadataFactory + */ + private $parentMetadataFactory; + + /** + * @var TypeMetadataFactory + */ + private $typeMetadataFactory; + /** * {@inheritDoc} */ protected function initialize() { - $this->driver = $this->config->getMetadataDriverImpl(); - $this->evm = $this->sm->getEventManager(); - $this->initialized = true; - $om = $this->sm->getObjectManager(); - if ($this->driver instanceof DependentMappingDriver && $om instanceof ObjectManager) { - $parentMetadataFactory = $om->getMetadataFactory(); - if ($parentMetadataFactory instanceof AbstractClassMetadataFactory) { - $parentMetadataFactory->initialize(); - $this->driver->setParentDriver($parentMetadataFactory->getDriver()); + $parentMetadataFactory = $om->getMetadataFactory(); + if (!$parentMetadataFactory instanceof AbstractClassMetadataFactory) { + throw new \LogicException("Parent metadata factory must be an instanceof AbstractClassMetadataFactory"); + } + + $parentMetadataFactory->initialize(); + + $driver = $this->config->getMetadataDriverImpl(); + foreach ($driver instanceof MappingDriverChain ? $driver->getDrivers() : array($driver) as $innerDriver) { + if (!$innerDriver instanceof DependentMappingDriver) { + throw new \LogicException("Driver must implement DependentMappingDriver interface"); } + $innerDriver->setParentDriver($parentMetadataFactory->getDriver()); } + + $this->driver = $driver; + $this->evm = $this->sm->getEventManager(); + $this->parentMetadataFactory = $parentMetadataFactory; + $this->initialized = TRUE; } /** @@ -90,6 +110,14 @@ public function setSearchManager(SearchManager $sm) $this->sm = $sm; } + /** + * @param TypeMetadataFactory $factory + */ + public function setTypeMetadataFactory(TypeMetadataFactory $factory) + { + $this->typeMetadataFactory = $factory; + } + /** * Sets the Configuration instance * @@ -151,7 +179,17 @@ protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonS */ protected function newClassMetadataInstance($className) { - return new ClassMetadata($className); + $metadata = new ClassMetadata($className); + + if (empty($metadata->type)) { + $metadata->type = $this->typeMetadataFactory->createTypeMetadata($className); + } + + if (empty($metadata->index)) { + $metadata->index = new IndexMetadata(); + } + + return $metadata; } /** @@ -164,7 +202,12 @@ protected function newClassMetadataInstance($className) */ protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) { - $class->wakeupReflection($reflService); + if (!$this->parentMetadataFactory) { + $om = $this->sm->getObjectManager(); + $this->parentMetadataFactory = $om->getMetadataFactory(); + } + + $class->wakeupReflection($reflService, $this->parentMetadataFactory->getMetadataFor($class->getName())); } /** @@ -176,7 +219,7 @@ protected function wakeupReflection(ClassMetadataInterface $class, ReflectionSer */ protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) { - $class->initializeReflection($reflService); + $this->wakeupReflection($class, $reflService); } /** diff --git a/lib/Doctrine/Search/Mapping/DependentMappingDriver.php b/lib/Doctrine/Search/Mapping/DependentMappingDriver.php deleted file mode 100644 index 6133661..0000000 --- a/lib/Doctrine/Search/Mapping/DependentMappingDriver.php +++ /dev/null @@ -1,41 +0,0 @@ -. - */ - -namespace Doctrine\Search\Mapping; - -use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; - - - -/** - * If a driver implements this interface, - * it should delegate the getAllClassNames method to the parent driver. - * - * @author Filip Procházka - */ -interface DependentMappingDriver -{ - - /** - * @param MappingDriver $driver - * @return void - */ - public function setParentDriver(MappingDriver $driver); - -} diff --git a/lib/Doctrine/Search/Mapping/Driver/DependentMappingDriver.php b/lib/Doctrine/Search/Mapping/Driver/DependentMappingDriver.php new file mode 100644 index 0000000..1eca94c --- /dev/null +++ b/lib/Doctrine/Search/Mapping/Driver/DependentMappingDriver.php @@ -0,0 +1,122 @@ +. + */ + +namespace Doctrine\Search\Mapping\Driver; + +use Doctrine\Common\Persistence\Mapping\ClassMetadata; +use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; + + + +/** + * If a driver implements this interface, + * it should delegate the getAllClassNames method to the parent driver. + * + * @author Filip Procházka + */ +class DependentMappingDriver implements MappingDriver +{ + + /** + * @var MappingDriver + */ + private $innerDriver; + + /** + * @var MappingDriver + */ + private $parentDriver; + + /** + * @var array + */ + private $classNames; + + + + public function __construct(MappingDriver $innerDriver) + { + $this->innerDriver = $innerDriver; + } + + + + /** + * @param MappingDriver $driver + */ + public function setParentDriver(MappingDriver $driver) + { + $this->parentDriver = $driver; + } + + + + /** + * Loads the metadata for the specified class into the provided container. + * + * @param string $className + * @param ClassMetadata $metadata + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $this->innerDriver->loadMetadataForClass($className, $metadata); + } + + + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + public function getAllClassNames() + { + if ($this->classNames !== NULL) { + return $this->classNames; + } + + if ($this->parentDriver === NULL) { + throw new \LogicException(sprintf("Setting the parent driver using %s::setParentDriver(\$driver) is mandatory.", get_called_class())); + } + + $classes = array(); + foreach ($this->parentDriver->getAllClassNames() as $className) { + if (!$this->isTransient($className)) { + $classes[] = $className; + } + } + + return $this->classNames = $classes; + } + + + + /** + * Returns whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped as an Entity or a MappedSuperclass. + * + * @param string $className + * @return boolean + */ + public function isTransient($className) + { + return $this->innerDriver->isTransient($className); + } + +} diff --git a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php new file mode 100644 index 0000000..2048567 --- /dev/null +++ b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php @@ -0,0 +1,207 @@ + + */ +class NeonDriver implements MappingDriver +{ + + /** + * @var array + */ + private $classNames; + + /** + * @var string + */ + private $metadataDirectory; + + /** + * @var array + */ + private $typesMapping; + + /** + * @var array + */ + private $indexesMapping; + + + + public function __construct($metadataDirectory) + { + $this->metadataDirectory = $metadataDirectory; + } + + + + /** + * Loads the metadata for the specified class into the provided container. + * + * @param string $className + * @param ClassMetadata|\Doctrine\Search\Mapping\ClassMetadata $metadata + * + * @return void + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $typeMapping = $this->getTypeMapping($className); + $this->loadTypeMapping($metadata->type, $typeMapping); + + $indexMapping = $this->getIndexMapping($typeMapping['index']); + $this->loadIndexMapping($metadata->index, $indexMapping); + } + + + + protected function loadTypeMapping(TypeMetadata $type, $typeMapping) + { + $type->name = $typeMapping['type']; + + if (!empty($typeMapping['properties'])) { + $type->setProperties($typeMapping['properties']); + } + + if (!empty($typeMapping['parameters'])) { + $type->setParameters($typeMapping['parameters']); + } + + $settings = $typeMapping; + unset($settings['class'], $settings['index'], $settings['type'], $settings['properties'], $settings['parameters'], $settings['serializer']); + $type->setSettings($settings); + } + + + + protected function loadIndexMapping(IndexMetadata $index, $indexMapping) + { + foreach (array('name', 'numberOfShards', 'numberOfReplicas', 'charFilter', 'filter', 'analyzer') as $key) { + if (empty($indexMapping[$key])) { + continue; + } + + $index->{$key} = $indexMapping[$key]; + } + } + + + + public function getAllClassNames() + { + if ($this->classNames !== NULL) { + return $this->classNames; + } + + $classes = array(); + foreach ($this->getTypesMapping() as $meta) { + $classes[] = $meta['class']; + } + + return $this->classNames = $classes; + } + + + + /** + * Returns whether the class with the specified name should have its metadata loaded. + * + * @param string $className + * @return boolean + */ + public function isTransient($className) + { + return (bool) $this->getTypeMapping($className); + } + + + + /** + * @param string $className + * @return array + */ + protected function getTypeMapping($className) + { + foreach ($this->getTypesMapping() as $mapping) { + if ($mapping['class'] === $className) { + return $mapping; + } + } + + return NULL; + } + + + + /** + * @return array + */ + protected function getTypesMapping() + { + if ($this->typesMapping !== NULL) { + return $this->typesMapping; + } + + $this->typesMapping = array(); + foreach (glob($this->metadataDirectory . '/*.type.neon') as $file) { + $meta = Neon::decode(file_get_contents($file)); + if (!isset($meta['class'])) { + throw new \InvalidArgumentException("The metadata file $file is missing a required field 'class' with entity name."); + } + + $meta['type'] = basename($file, '.type.neon'); + $this->typesMapping[$meta['type']] = $meta; + } + + return $this->typesMapping; + } + + + + /** + * @param string $indexName + * @return array + */ + protected function getIndexMapping($indexName) + { + foreach ($this->getIndexesMapping() as $mapping) { + if ($mapping['name'] === $indexName) { + return $mapping; + } + } + + throw new InvalidArgumentException(sprintf('Metadata of index %s not found', $indexName)); + } + + + + /** + * @return array + */ + protected function getIndexesMapping() + { + if ($this->indexesMapping !== NULL) { + return $this->indexesMapping; + } + + $this->indexesMapping = array(); + foreach (glob($this->metadataDirectory . '/*.index.neon') as $file) { + $meta = Neon::decode(file_get_contents($file)); + $meta['name'] = basename($file, '.index.neon'); + $this->indexesMapping[$meta['name']] = $meta; + } + + return $this->indexesMapping; + } + +} diff --git a/lib/Doctrine/Search/Mapping/Helpers.php b/lib/Doctrine/Search/Mapping/Helpers.php new file mode 100644 index 0000000..7f11f21 --- /dev/null +++ b/lib/Doctrine/Search/Mapping/Helpers.php @@ -0,0 +1,44 @@ + + * @see http://php.net/manual/en/function.ucwords.php#92092 + */ +class Helpers +{ + + /** + * underscored to lower-camelcase + * e.g. "this_method_name" -> "thisMethodName" + * + * @param string $string + * @return string + */ + public function camelCase($string) + { + return preg_replace('/_(.?)/e', "strtoupper('$1')", $string); + } + + + + /** + * camelcase (lower or upper) to underscored + * e.g. "thisMethodName" -> "this_method_name" + * e.g. "ThisMethodName" -> "this_method_name" + * + * @param string $string + * @return string + */ + public function underscore($string) + { + return strtolower(preg_replace('/([^A-Z])([A-Z])/', "$1_$2", $string)); + } + +} diff --git a/lib/Doctrine/Search/Mapping/IndexMetadata.php b/lib/Doctrine/Search/Mapping/IndexMetadata.php new file mode 100644 index 0000000..059655b --- /dev/null +++ b/lib/Doctrine/Search/Mapping/IndexMetadata.php @@ -0,0 +1,43 @@ + + */ +class IndexMetadata +{ + + /** + * @var string + */ + public $name; + + /** + * @var int + */ + public $numberOfShards = 1; + + /** + * @var int + */ + public $numberOfReplicas = 1; + + /** + * @var array + */ + public $charFilter = array(); + + /** + * @var array + */ + public $filter = array(); + + /** + * @var array + */ + public $analyzer = array(); + +} diff --git a/lib/Doctrine/Search/Mapping/TypeMetadata.php b/lib/Doctrine/Search/Mapping/TypeMetadata.php new file mode 100644 index 0000000..21803f4 --- /dev/null +++ b/lib/Doctrine/Search/Mapping/TypeMetadata.php @@ -0,0 +1,84 @@ + + */ +abstract class TypeMetadata +{ + + /** + * @var string + */ + public $name; + + /** + * @var bool + */ + public $source = TRUE; + + /** + * @var integer + */ + public $boost; + + /** + * @var array + */ + public $settings = array(); + + /** + * @var array + */ + public $properties = array(); + + /** + * @var array + */ + public $parameters = array(); + + + + public function __construct($className) + { + $this->className = $className; + } + + + + public function setSettings(array $options) + { + $this->validateSettings($options); + $this->settings = $options; + } + + + + public function setProperties(array $properties) + { + foreach ($properties as $name => $property) { + $this->validateProperty($name, $property); + } + + $this->properties = $properties; + } + + + + public function setParameters(array $parameters) + { + foreach ($parameters as $name => $field) { + $this->parameters[is_numeric($name) ? $field : $name] = $field; + } + } + + + + abstract protected function validateSettings(array $options); + + abstract protected function validateProperty($name, array $property); + +} diff --git a/lib/Doctrine/Search/Mapping/TypeMetadataFactory.php b/lib/Doctrine/Search/Mapping/TypeMetadataFactory.php new file mode 100644 index 0000000..d0fe7fa --- /dev/null +++ b/lib/Doctrine/Search/Mapping/TypeMetadataFactory.php @@ -0,0 +1,22 @@ + + */ +interface TypeMetadataFactory +{ + + /** + * @param string $className + * @return TypeMetadata + */ + public function createTypeMetadata($className); + +} diff --git a/lib/Doctrine/Search/SearchClientInterface.php b/lib/Doctrine/Search/SearchClient.php similarity index 74% rename from lib/Doctrine/Search/SearchClientInterface.php rename to lib/Doctrine/Search/SearchClient.php index 8042c9a..ecfee87 100755 --- a/lib/Doctrine/Search/SearchClientInterface.php +++ b/lib/Doctrine/Search/SearchClient.php @@ -20,13 +20,17 @@ namespace Doctrine\Search; use Doctrine\Search\Mapping\ClassMetadata; +use Doctrine\Search\Mapping\TypeMetadata; +use Doctrine\Search\Mapping\TypeMetadataFactory; + + /** * Interface for a Doctrine SearchManager class to implement. * * @author Mike Lohmann */ -interface SearchClientInterface +interface SearchClient extends TypeMetadataFactory { /** * Finds document by id. @@ -34,7 +38,7 @@ interface SearchClientInterface * @param ClassMetadata $class * @param mixed $id * @param array $options - * @throws \Doctrine\Search\Exception\NoResultException + * @throws \Doctrine\Search\NoResultException */ public function find(ClassMetadata $class, $id, $options = array()); @@ -44,7 +48,7 @@ public function find(ClassMetadata $class, $id, $options = array()); * @param ClassMetadata $class * @param string $field * @param mixed $value - * @throws \Doctrine\Search\Exception\NoResultException + * @throws \Doctrine\Search\NoResultException */ public function findOneBy(ClassMetadata $class, $field, $value); @@ -63,50 +67,6 @@ public function findAll(array $classes); */ public function search($query, array $classes); - /** - * Creates a document index - * - * @param string $name The name of the index. - * @param string $config The configuration of the index. - */ - public function createIndex($name, array $config = array()); - - /** - * Gets a document index reference - * - * @param string $name The name of the index. - */ - public function getIndex($name); - - /** - * Deletes an index and its types and documents - * - * @param string $index - */ - public function deleteIndex($index); - - /** - * Refresh the index to make documents available for search - * - * @param string $index - */ - public function refreshIndex($index); - - /** - * Create a document type mapping as defined in the - * class annotations - * - * @param ClassMetadata $metadata - */ - public function createType(ClassMetadata $metadata); - - /** - * Delete a document type - * - * @param ClassMetadata $metadata - */ - public function deleteType(ClassMetadata $metadata); - /** * Adds documents of a given type to the specified index * @@ -131,4 +91,18 @@ public function removeDocuments(ClassMetadata $class, array $documents); * @param object $query */ public function removeAll(ClassMetadata $class, $query = null); + + /** + * Refresh the index to make documents available for search + * + * @param string $index + */ + public function refreshIndex($index); + + /** + * @param string $className + * @return TypeMetadata + */ + public function createTypeMetadata($className); + } From 8e4addf439200be961d4af83326710817cb458e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:42:18 +0100 Subject: [PATCH 19/45] Introduce SchemaManager --- lib/Doctrine/Search/ElasticSearch/Client.php | 243 ++---------------- .../Search/ElasticSearch/SchemaManager.php | 218 ++++++++++++++++ lib/Doctrine/Search/SchemaManager.php | 79 ++++++ 3 files changed, 312 insertions(+), 228 deletions(-) create mode 100644 lib/Doctrine/Search/ElasticSearch/SchemaManager.php create mode 100644 lib/Doctrine/Search/SchemaManager.php diff --git a/lib/Doctrine/Search/ElasticSearch/Client.php b/lib/Doctrine/Search/ElasticSearch/Client.php index e1f56b5..964b878 100755 --- a/lib/Doctrine/Search/ElasticSearch/Client.php +++ b/lib/Doctrine/Search/ElasticSearch/Client.php @@ -19,21 +19,22 @@ namespace Doctrine\Search\ElasticSearch; -use Doctrine\Search\Mapping\Annotations\ElasticField; -use Doctrine\Search\Mapping\Annotations\ElasticRoot; -use Doctrine\Search\SearchClientInterface; +use Doctrine; use Doctrine\Search\Mapping\ClassMetadata; -use Doctrine\Search\Exception\NoResultException; +use Doctrine\Search\Mapping\TypeMetadata; +use Doctrine\Search\Mapping\TypeMetadataFactory; +use Doctrine\Search\NoResultException; +use Doctrine\Search\SearchClient; use Elastica\Client as ElasticaClient; -use Elastica\Type\Mapping; use Elastica\Document; +use Elastica\Exception\NotFoundException; +use Elastica\Filter\Term; use Elastica\Index; +use Elastica\Query; use Elastica\Query\MatchAll; -use Elastica\Filter\Term; -use Elastica\Exception\NotFoundException; use Elastica\Search; -use Doctrine\Common\Collections\ArrayCollection; -use Elastica\Query; + + /** * SearchManager for ElasticSearch-Backend @@ -41,7 +42,7 @@ * @author Mike Lohmann * @author Markus Bachmann */ -class Client implements SearchClientInterface +class Client implements SearchClient { /** * @var ElasticaClient @@ -181,32 +182,6 @@ public function search($query, array $classes) return $this->buildQuery($classes)->search($query); } - /** - * {@inheritDoc} - */ - public function createIndex($name, array $config = array()) - { - $index = $this->getIndex($name); - $index->create($config, true); - return $index; - } - - /** - * {@inheritDoc} - */ - public function getIndex($name) - { - return $this->client->getIndex($name); - } - - /** - * {@inheritDoc} - */ - public function deleteIndex($index) - { - $this->getIndex($index)->delete(); - } - /** * {@inheritDoc} */ @@ -216,200 +191,12 @@ public function refreshIndex($index) } /** - * {@inheritDoc} - */ - public function createType(ClassMetadata $metadata) - { - $type = $this->getIndex($metadata->index)->getType($metadata->type); - $properties = $this->getMapping($metadata->fieldMappings); - $properties += $this->getMapping($metadata->methodMappings); - $rootProperties = $this->getRootMapping($metadata->rootMappings); - - $mapping = new Mapping($type, $properties); - $mapping->disableSource($metadata->source); - if (isset($metadata->boost)) { - $mapping->setParam('_boost', array('name' => '_boost', 'null_value' => $metadata->boost)); - } - if (isset($metadata->parent)) { - $mapping->setParent($metadata->parent); - } - foreach ($rootProperties as $key => $value) { - $mapping->setParam($key, $value); - } - - $mapping->send(); - - return $type; - } - - /** - * {@inheritDoc} - */ - public function deleteType(ClassMetadata $metadata) - { - $type = $this->getIndex($metadata->index)->getType($metadata->type); - return $type->delete(); - } - - /** - * Generates property mapping from entity annotations - * - * @param array|ElasticField[] $mappings - */ - protected function getMapping($mappings) - { - $properties = array(); - - foreach ($mappings as $propertyName => $fieldMapping) { - if (isset($fieldMapping->name)) { - $propertyName = $fieldMapping->name; - } - - $properties[$propertyName]['type'] = $fieldMapping->type; - - if (isset($fieldMapping->path)) { - $properties[$propertyName]['path'] = $fieldMapping->path; - } - - if (isset($fieldMapping->includeInAll)) { - $properties[$propertyName]['include_in_all'] = $fieldMapping->includeInAll; - } - - if (isset($fieldMapping->nullValue)) { - $properties[$propertyName]['null_value'] = $fieldMapping->nullValue; - } - - if (isset($fieldMapping->store)) { - $properties[$propertyName]['store'] = $fieldMapping->store; - } - - if (isset($fieldMapping->index)) { - $properties[$propertyName]['index'] = $fieldMapping->index; - } - - if (isset($fieldMapping->boost)) { - $properties[$propertyName]['boost'] = $fieldMapping->boost; - } - - if (isset($fieldMapping->analyzer)) { - $properties[$propertyName]['analyzer'] = $fieldMapping->analyzer; - } - - if (isset($fieldMapping->indexName)) { - $properties[$propertyName]['index_name'] = $fieldMapping->indexName; - } - - if (isset($fieldMapping->indexAnalyzer)) { - $properties[$propertyName]['index_analyzer'] = $fieldMapping->indexAnalyzer; - } - - if (isset($fieldMapping->searchAnalyzer)) { - $properties[$propertyName]['search_analyzer'] = $fieldMapping->searchAnalyzer; - } - - if (isset($fieldMapping->payloads)) { - $properties[$propertyName]['payloads'] = $fieldMapping->payloads; - } - - if (isset($fieldMapping->preserveSeparators)) { - $properties[$propertyName]['preserve_separators'] = $fieldMapping->preserveSeparators; - } - - if (isset($fieldMapping->preservePositionIncrements)) { - $properties[$propertyName]['preserve_position_increments'] = $fieldMapping->preservePositionIncrements; - } - - if (isset($fieldMapping->maxInputLength)) { - $properties[$propertyName]['max_input_length'] = $fieldMapping->maxInputLength; - } - - if ($fieldMapping->type == 'attachment' && isset($fieldMapping->fields)) { - $callback = function ($field) { - unset($field['type']); - return $field; - }; - $properties[$propertyName]['fields'] = array_map($callback, $this->getMapping($fieldMapping->fields)); - } - - if ($fieldMapping->type == 'multi_field' && isset($fieldMapping->fields)) { - $properties[$propertyName]['fields'] = $this->getMapping($fieldMapping->fields); - } - - if (in_array($fieldMapping->type, array('nested', 'object')) && isset($fieldMapping->properties)) { - $properties[$propertyName]['properties'] = $this->getMapping($fieldMapping->properties); - } - } - - return $properties; - } - - /** - * Generates parameter mapping from entity annotations - * - * @param array $paramMapping + * @param string $className + * @return TypeMetadata */ - protected function getParameters($paramMapping) + public function createTypeMetadata($className) { - $parameters = array(); - foreach ($paramMapping as $propertyName => $mapping) { - $paramName = isset($mapping->name) ? $mapping->name : $propertyName; - $parameters[$paramName] = $propertyName; - } - return $parameters; + return new Mapping\TypeMetadata($className); } - /** - * Generates root mapping from entity annotations - * - * @param array|ElasticRoot[] $mappings - */ - protected function getRootMapping($mappings) - { - $properties = array(); - - foreach ($mappings as $rootMapping) { - $propertyName = $rootMapping->name; - $mapping = array(); - - if (isset($rootMapping->value)) { - $mapping = $rootMapping->value; - } - - if (isset($rootMapping->match)) { - $mapping['match'] = $rootMapping->match; - } - - if (isset($rootMapping->pathMatch)) { - $mapping['path_match'] = $rootMapping->pathMatch; - } - - if (isset($rootMapping->unmatch)) { - $mapping['unmatch'] = $rootMapping->unmatch; - } - - if (isset($rootMapping->pathUnmatch)) { - $mapping['path_unmatch'] = $rootMapping->pathUnmatch; - } - - if (isset($rootMapping->matchPattern)) { - $mapping['match_pattern'] = $rootMapping->matchPattern; - } - - if (isset($rootMapping->matchMappingType)) { - $mapping['match_mapping_type'] = $rootMapping->matchMappingType; - } - - if (isset($rootMapping->mapping)) { - $mapping['mapping'] = current($this->getMapping($rootMapping->mapping)); - } - - if (isset($rootMapping->id)) { - $properties[$propertyName][][$rootMapping->id] = $mapping; - } else { - $properties[$propertyName] = $mapping; - } - } - - return $properties; - } } diff --git a/lib/Doctrine/Search/ElasticSearch/SchemaManager.php b/lib/Doctrine/Search/ElasticSearch/SchemaManager.php new file mode 100644 index 0000000..0657dcd --- /dev/null +++ b/lib/Doctrine/Search/ElasticSearch/SchemaManager.php @@ -0,0 +1,218 @@ + + */ +class SchemaManager implements Doctrine\Search\SchemaManager +{ + + /** + * @var \Doctrine\Search\ElasticSearch\Client + */ + private $client; + + /** + * @var \Elastica\Client + */ + private $elastica; + + + + public function __construct(Client $client) + { + $this->client = $client; + $this->elastica = $client->getClient(); + } + + + + /** + * @param ClassMetadata $class + * @return \Elastica\Index + */ + protected function getIndex(ClassMetadata $class) + { + return $this->elastica + ->getIndex($class->getIndexName()); + } + + + + /** + * @param ClassMetadata $class + * @return \Elastica\Type + */ + protected function getType(ClassMetadata $class) + { + return $this->getIndex($class) + ->getType($class->getTypeName()); + } + + + + /** + * @param array|ClassMetadata[] $classes + */ + public function dropMappings(array $classes) + { + foreach ($classes as $class) { + if (!$this->hasIndex($class->getIndexName())) { + continue; + } + + if (!$this->hasType($class)) { + continue; + } + + $this->dropType($class); + } + + foreach ($classes as $class) { + if (!$this->hasIndex($class->getIndexName())) { + continue; + } + + $this->dropIndex($class->getIndexName()); + } + } + + + + /** + * @param array|ClassMetadata[] $classes + * @param bool $withAliases + * @return array + */ + public function createMappings(array $classes, $withAliases = FALSE) + { + $aliases = []; + foreach ($classes as $class) { + if ($withAliases) { + $indexAlias = $class->getIndexName() . '_' . date('YmdHis'); + $aliases[$indexAlias] = $class->getIndexName(); + + $fakeMetadata = clone $class; + $fakeMetadata->index['name'] = $indexAlias; + + $class = $fakeMetadata; + } + + if (!$this->hasIndex($class->getIndexName())) { + $this->createIndex($class); + } + + $this->createType($class); + } + + return $aliases; + } + + + + public function createAliases(array $aliases) + { + foreach ($aliases as $alias => $original) { + try { + $this->createAlias($alias, $original); + } catch (ResponseException $e) { + } + } + } + + + + public function hasIndex($index) + { + return $this->elastica->getIndex($index)->exists(); + } + + + + public function createIndex(ClassMetadata $class) + { + $index = $this->elastica->getIndex($class->getIndexName()); + $response = $index->create(array( + 'number_of_shards' => $class->index['number_of_shards'], + 'number_of_replicas' => $class->index['number_of_replicas'], + 'analysis' => array( + 'char_filter' => $class->index['char_filter'], + 'analyzer' => $class->index['analyzer'], + 'filter' => $class->index['filter'], + ), + ), TRUE); + + return $response; + } + + + + public function dropIndex($index) + { + return $this->elastica->getIndex($index)->delete(); + } + + + + public function hasType(ClassMetadata $class) + { + return $this->getType($class)->exists(); + } + + + + public function createType(ClassMetadata $class) + { + $mapping = new ElasticaTypeMapping($this->getType($class), $class->type->properties); + $mapping->disableSource($class->type->source); + + if ($class->type->boost !== NULL) { + $mapping->setParam('_boost', array('name' => '_boost', 'null_value' => $class->type->boost)); + } + + if ($class->parent !== NULL) { + $mapping->setParent($class->parent); + } + + foreach ($class->type->settings as $key => $value) { + $mapping->setParam($key, $value); + } + + return $mapping->send(); + } + + + + public function dropType(ClassMetadata $class) + { + return $this->getType($class)->delete(); + } + + + + public function createAlias($alias, $original) + { + try { + $this->elastica->request(sprintf('_all/_alias/%s', $original), Request::DELETE); + + } catch (ResponseException $e) { + if (stripos($e->getMessage(), 'AliasesMissingException') === FALSE) { + throw $e; + } + } + + $this->elastica->request(sprintf('/%s/_alias/%s', $alias, $original), Request::PUT); + } + +} diff --git a/lib/Doctrine/Search/SchemaManager.php b/lib/Doctrine/Search/SchemaManager.php new file mode 100644 index 0000000..3e79f63 --- /dev/null +++ b/lib/Doctrine/Search/SchemaManager.php @@ -0,0 +1,79 @@ + + */ +interface SchemaManager +{ + + /** + * @param array|ClassMetadata[] $classes + */ + public function dropMappings(array $classes); + + /** + * @param array|ClassMetadata[] $classes + * @param bool $withAliases + * @return array + */ + public function createMappings(array $classes, $withAliases = FALSE); + + /** + * @param array $aliases + * @return void + */ + public function createAliases(array $aliases); + + /** + * @param string $index + * @return boolean + */ + public function hasIndex($index); + + /** + * @param ClassMetadata $class + * @return boolean + */ + public function createIndex(ClassMetadata $class); + + /** + * @param string $index + * @return boolean + */ + public function dropIndex($index); + + /** + * @param ClassMetadata $class + * @return boolean + */ + public function hasType(ClassMetadata $class); + + /** + * @param ClassMetadata $class + * @return boolean + */ + public function createType(ClassMetadata $class); + + /** + * @param ClassMetadata $class + * @return boolean + */ + public function dropType(ClassMetadata $class); + + /** + * @param string $alias + * @param string $original + */ + public function createAlias($alias, $original); + +} From 699cab245172c1af0f33b256f9389f670f07f2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:43:52 +0100 Subject: [PATCH 20/45] Refactored SearchManager & UnitOfWork --- lib/Doctrine/Search/Configuration.php | 31 +--- lib/Doctrine/Search/ElasticSearch/Client.php | 65 ++++++--- lib/Doctrine/Search/SearchManager.php | 52 ++----- lib/Doctrine/Search/UnitOfWork.php | 140 ++++++++++++------- 4 files changed, 148 insertions(+), 140 deletions(-) diff --git a/lib/Doctrine/Search/Configuration.php b/lib/Doctrine/Search/Configuration.php index c13d3c2..d63366b 100755 --- a/lib/Doctrine/Search/Configuration.php +++ b/lib/Doctrine/Search/Configuration.php @@ -46,11 +46,11 @@ class Configuration */ public function getMetadataDriverImpl() { - if (!isset($this->attributes['concreteMetadataDriver'])) { - $this->attributes['concreteMetadataDriver'] = $this->newDefaultAnnotationDriver(); + if (!isset($this->attributes['metadataDriver'])) { + $this->attributes['metadataDriver'] = $this->newDefaultAnnotationDriver(); } - return $this->attributes['concreteMetadataDriver']; + return $this->attributes['metadataDriver']; } /** @@ -60,7 +60,7 @@ public function getMetadataDriverImpl() */ public function setMetadataDriverImpl(MappingDriver $concreteDriver) { - $this->attributes['concreteMetadataDriver'] = $concreteDriver; + $this->attributes['metadataDriver'] = $concreteDriver; } /** @@ -94,9 +94,9 @@ public function getMetadataCacheImpl() */ public function newDefaultAnnotationDriver(array $paths = array()) { - $reader = new \Doctrine\Common\Annotations\AnnotationReader(); - - return new \Doctrine\Search\Mapping\Driver\AnnotationDriver($reader, $paths); + throw new NotImplementedException; +// $reader = new \Doctrine\Common\Annotations\AnnotationReader(); +// return new \Doctrine\Search\Mapping\Driver\AnnotationDriver($reader, $paths); } /** @@ -178,21 +178,4 @@ public function getObjectManager() } } - /** - * @deprecated - */ - public function setEntityManager(ObjectManager $objectManager) - { - $this->setObjectManager($objectManager); - } - - /** - * @deprecated - */ - public function getEntityManager() - { - if (isset($this->attributes['objectManager'])) { - return $this->attributes['objectManager']; - } - } } diff --git a/lib/Doctrine/Search/ElasticSearch/Client.php b/lib/Doctrine/Search/ElasticSearch/Client.php index 964b878..7d2fd03 100755 --- a/lib/Doctrine/Search/ElasticSearch/Client.php +++ b/lib/Doctrine/Search/ElasticSearch/Client.php @@ -66,33 +66,53 @@ public function getClient() } /** - * {@inheritDoc} + * @param string $name + * @return Index */ - public function addDocuments(ClassMetadata $class, array $documents) + protected function getIndex($name) { - $type = $this->getIndex($class->index)->getType($class->type); + return $this->client->getIndex($name); + } - $parameters = $this->getParameters($class->parameters); + /** + * @param ClassMetadata $class + * @return \Elastica\Type + */ + protected function getType(ClassMetadata $class) + { + return $this->getIndex($class->getIndexName())->getType($class->getTypeName()); + } + /** + * {@inheritDoc} + */ + public function addDocuments(ClassMetadata $class, array $documents) + { $bulk = array(); foreach ($documents as $id => $document) { $elasticaDoc = new Document($id); - foreach ($parameters as $name => $value) { - if (isset($document[$value])) { - if (method_exists($elasticaDoc, "set{$name}")) { - $elasticaDoc->{"set{$name}"}($document[$value]); - } else { - $elasticaDoc->setParam($name, $document[$value]); - } - unset($document[$value]); + + foreach ($class->type->parameters as $name => $value) { + if (!isset($document[$value])) { + continue; + } + + if (method_exists($elasticaDoc, "set{$name}")) { + $elasticaDoc->{"set{$name}"}($document[$value]); + } else { + $elasticaDoc->setParam($name, $document[$value]); } + unset($document[$value]); } - $elasticaDoc->setData($document); - $bulk[] = $elasticaDoc; + + $bulk[] = $elasticaDoc->setData($document); } + $type = $this->getType($class); + if (count($bulk) > 1) { $type->addDocuments($bulk); + } else { $type->addDocument($bulk[0]); } @@ -103,8 +123,7 @@ public function addDocuments(ClassMetadata $class, array $documents) */ public function removeDocuments(ClassMetadata $class, array $documents) { - $type = $this->getIndex($class->index)->getType($class->type); - $type->deleteIds(array_keys($documents)); + $this->getType($class)->deleteIds(array_keys($documents)); } /** @@ -112,9 +131,8 @@ public function removeDocuments(ClassMetadata $class, array $documents) */ public function removeAll(ClassMetadata $class, $query = null) { - $type = $this->getIndex($class->index)->getType($class->type); $query = $query ?: new MatchAll(); - $type->deleteByQuery($query); + $this->getType($class)->deleteByQuery($query); } /** @@ -123,8 +141,7 @@ public function removeAll(ClassMetadata $class, $query = null) public function find(ClassMetadata $class, $id, $options = array()) { try { - $type = $this->getIndex($class->index)->getType($class->type); - $document = $type->getDocument($id, $options); + $document = $this->getType($class)->getDocument($id, $options); } catch (NotFoundException $ex) { throw new NoResultException(); } @@ -158,16 +175,20 @@ public function findAll(array $classes) return $this->buildQuery($classes)->search(); } + /** + * @param array|ClassMetadata[] $classes + * @return Search + */ protected function buildQuery(array $classes) { $searchQuery = new Search($this->client); $searchQuery->setOption(Search::OPTION_VERSION, true); foreach ($classes as $class) { if ($class->index) { - $indexObject = $this->getIndex($class->index); + $indexObject = $this->getIndex($class->getIndexName()); $searchQuery->addIndex($indexObject); if ($class->type) { - $searchQuery->addType($indexObject->getType($class->type)); + $searchQuery->addType($indexObject->getType($class->getTypeName())); } } } diff --git a/lib/Doctrine/Search/SearchManager.php b/lib/Doctrine/Search/SearchManager.php index b3ab874..32357dc 100755 --- a/lib/Doctrine/Search/SearchManager.php +++ b/lib/Doctrine/Search/SearchManager.php @@ -19,9 +19,10 @@ namespace Doctrine\Search; -use Doctrine\Common\Persistence\ObjectManager; -use Doctrine\Search\Exception\UnexpectedTypeException; use Doctrine\Common\EventManager; +use Doctrine\Common\Persistence\ObjectManager; + + /** * Interface for a Doctrine SearchManager class to implement. @@ -31,7 +32,7 @@ class SearchManager implements ObjectManager { /** - * @var SearchClientInterface + * @var SearchClient */ private $client; @@ -80,10 +81,10 @@ class SearchManager implements ObjectManager * Constructor * * @param Configuration $config - * @param SearchClientInterface $client + * @param SearchClient $client * @param EventManager $eventManager */ - public function __construct(Configuration $config, SearchClientInterface $client, EventManager $eventManager) + public function __construct(Configuration $config, SearchClient $client, EventManager $eventManager) { $this->configuration = $config; $this->client = $client; @@ -91,6 +92,7 @@ public function __construct(Configuration $config, SearchClientInterface $client $this->metadataFactory = $this->configuration->getClassMetadataFactory(); $this->metadataFactory->setSearchManager($this); + $this->metadataFactory->setTypeMetadataFactory($client); $this->metadataFactory->setConfiguration($this->configuration); $this->metadataFactory->setCacheDriver($this->configuration->getMetadataCacheImpl()); @@ -165,7 +167,7 @@ public function getClassMetadataFactory() } /** - * @return SearchClientInterface + * @return SearchClient */ public function getClient() { @@ -275,23 +277,7 @@ public function getRepository($entityName) } /** - * Gets a collection of entity repositories. - * - * @param array $entityNames The names of the entities. - * @return EntityRepositoryCollection The repository class. - */ - public function getRepositories(array $entityNames) - { - $repositoryCollection = new EntityRepositoryCollection($this); - foreach ($entityNames as $entityName) { - $repositoryCollection->addRepository($this->getRepository($entityName)); - } - return $repositoryCollection; - } - - /** - * Returns a search engine Query wrapper which can be executed - * to retrieve results. + * Returns a search engine Query wrapper which can be executed to retrieve results. * * @return Query */ @@ -331,24 +317,4 @@ public function refresh($object) { } - - /** - * Inject a Doctrine 2 object manager - * - * @deprecated - * @param ObjectManager $om - */ - public function setEntityManager(ObjectManager $om) - { - $this->setObjectManager($om); - } - - /** - * @deprecated - * @return ObjectManager|\Doctrine\ORM\EntityManager - */ - public function getEntityManager() - { - return $this->getObjectManager(); - } } diff --git a/lib/Doctrine/Search/UnitOfWork.php b/lib/Doctrine/Search/UnitOfWork.php index 0167ebe..d010792 100644 --- a/lib/Doctrine/Search/UnitOfWork.php +++ b/lib/Doctrine/Search/UnitOfWork.php @@ -19,12 +19,14 @@ namespace Doctrine\Search; -use Doctrine\Search\SearchManager; -use Doctrine\Search\Exception\DoctrineSearchException; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Search\Mapping\ClassMetadata; +use Elastica\Document; +use Elastica\Search; use Traversable; + + class UnitOfWork { /** @@ -79,7 +81,8 @@ public function persist($entity) } $oid = spl_object_hash($entity); - $this->scheduledForPersist[$oid] = $entity; + $class = get_class($entity); + $this->scheduledForPersist[$class][$oid] = $entity; if ($this->evm->hasListeners(Events::postPersist)) { $this->evm->dispatchEvent(Events::postPersist, new Event\LifecycleEventArgs($entity, $this->sm)); @@ -98,7 +101,9 @@ public function remove($entity) } $oid = spl_object_hash($entity); - $this->scheduledForDelete[$oid] = $entity; + $class = get_class($entity); + unset($this->scheduledForPersist[$class][$oid]); + $this->scheduledForDelete[$class][$oid] = $entity; if ($this->evm->hasListeners(Events::postRemove)) { $this->evm->dispatchEvent(Events::postRemove, new Event\LifecycleEventArgs($entity, $this->sm)); @@ -117,7 +122,7 @@ public function clear($entityName = null) $this->scheduledForPersist = $this->updatedIndexes = array(); } else { - //TODO: implement for named entity classes + throw new NotImplementedException; } if ($this->evm->hasListeners(Events::onClear)) { @@ -142,16 +147,20 @@ public function commit($entity = null) $this->evm->dispatchEvent(Events::preFlush, new Event\PreFlushEventArgs($this->sm)); } - //TODO: single/array entity commit handling - $this->commitRemoved(); - $this->commitPersisted(); + if (is_object($entity)) { + throw new NotImplementedException; - //Force refresh of updated indexes - if ($entity === true) { - $client = $this->sm->getClient(); - foreach (array_unique($this->updatedIndexes) as $index) { - $client->refreshIndex($index); - } + } elseif (is_array($entity)) { + throw new NotImplementedException; + + } else { + $this->commitRemoved(); + $this->commitPersisted(); + } + + $client = $this->sm->getClient(); + foreach (array_unique($this->updatedIndexes) as $index) { + $client->refreshIndex($index); } $this->clear(); @@ -171,8 +180,8 @@ private function commitPersisted() foreach ($sortedDocuments as $entityName => $documents) { $classMetadata = $this->sm->getClassMetadata($entityName); - $this->updatedIndexes[] = $classMetadata->index; $client->addDocuments($classMetadata, $documents); + $this->updatedIndexes[] = $classMetadata->getIndexName(); } } @@ -181,13 +190,13 @@ private function commitPersisted() */ private function commitRemoved() { - $documents = $this->sortObjects($this->scheduledForDelete, false); + $sortedDocuments = $this->sortObjects($this->scheduledForDelete, false); $client = $this->sm->getClient(); - foreach ($documents as $entityName => $documents) { + foreach ($sortedDocuments as $entityName => $documents) { $classMetadata = $this->sm->getClassMetadata($entityName); - $this->updatedIndexes[] = $classMetadata->index; $client->removeDocuments($classMetadata, $documents); + $this->updatedIndexes[] = $classMetadata->getIndexName(); } } @@ -195,25 +204,29 @@ private function commitRemoved() * Prepare entities for commit. Entities scheduled for deletion do not need * to be serialized. * - * @param array $objects + * @param array $scheduledObjects * @param boolean $serialize * @throws DoctrineSearchException * @return array */ - private function sortObjects(array $objects, $serialize = true) + private function sortObjects(array $scheduledObjects, $serialize = true) { $documents = array(); $serializer = $this->sm->getSerializer(); - foreach ($objects as $object) { - $document = $serialize ? $serializer->serialize($object) : $object; + foreach ($scheduledObjects as $type => $objects) { + $metadata = $this->sm->getClassMetadata($type); - $id = $object->getId(); - if (!$id) { - throw new DoctrineSearchException('Entity must have an id to be indexed'); - } + foreach ($objects as $object) { + $document = $serialize ? $serializer->serialize($object) : $object; + + if (!$metadata->getIdentifier()) { + throw new DoctrineSearchException('Entity must have an id to be indexed'); + } - $documents[get_class($object)][$id] = $document; + $id = implode('-', (array) $metadata->getIdentifierValues($object)); + $documents[$type][$id] = $document; + } } return $documents; @@ -243,7 +256,8 @@ public function load(ClassMetadata $class, $value, $options = array()) * Load and hydrate a document collection * * @param array $classes - * @param unknown $query + * @param mixed $query + * @return ArrayCollection|Searchable[] */ public function loadCollection(array $classes, $query) { @@ -254,34 +268,55 @@ public function loadCollection(array $classes, $query) /** * Construct an entity collection * - * @param array $classes - * @param Traversable $resultSet + * @param array|ClassMetadata[] $classes + * @param Traversable|Document[] $resultSet + * @return ArrayCollection|Searchable[] */ public function hydrateCollection(array $classes, Traversable $resultSet) { + $map = array(); + foreach ($classes as $class) { + $map[$class->getIndexName()][$class->getTypeName()] = $class; + } + + if ($om = $this->sm->getObjectManager()) { // preload entities by one query + $documentsByType = array(); + foreach ($resultSet as $document) { + /** @var ClassMetadata $class */ + $class = $map[$document->getIndex()][$document->getType()]; + $documentsByType[$class->className][$document->getId()] = $document; + } + + foreach ($documentsByType as $className => $documents) { + $metadata = $this->sm->getClassMetadata($className); + + $repository = $om->getRepository($className); + $repository->findBy([$metadata->getIdentifier() => array_keys($documents)]); + } + } + $collection = new ArrayCollection(); foreach ($resultSet as $document) { - foreach ($classes as $class) { - if ($document->getIndex() == $class->index && $document->getType() == $class->type) { - break; - } - } + /** @var ClassMetadata $class */ + $class = $map[$document->getIndex()][$document->getType()]; $collection[] = $this->hydrateEntity($class, $document); } return $collection; } + + /** * Construct an entity object * * @param ClassMetadata $class - * @param object $document + * @param object|Document $document + * @return Searchable */ public function hydrateEntity(ClassMetadata $class, $document) { - // TODO: add support for different result set types from different clients - // perhaps by wrapping documents in a layer of abstraction + // TODO: add support for different result set types from different clients by implementing Persisters per client $data = $document->getData(); $fields = array_merge( $document->hasFields() ? $document->getFields() : array(), @@ -289,24 +324,25 @@ public function hydrateEntity(ClassMetadata $class, $document) ); foreach ($fields as $name => $value) { - if (isset($class->parameters[$name])) { + if (isset($class->type->parameters[$name])) { $data[$name] = $value; - } else { - foreach ($class->parameters as $param => $mapping) { - if ($mapping->name == $name) { - $data[$param] = $value; - break; - } - } + + } elseif ($key = array_search($name, $class->type->parameters, TRUE)) { + $data[$key] = $value; } } $data[$class->getIdentifier()] = $document->getId(); - $entity = $this->sm->getSerializer()->deserialize($class->className, json_encode($data)); + if ($om = $this->sm->getObjectManager()) { + $entity = $om->find($class->className, $document->getId()); + + } else { + $entity = $this->sm->getSerializer()->deserialize($class->className, json_encode($data)); + } if ($this->evm->hasListeners(Events::postLoad)) { - $this->evm->dispatchEvent(Events::postLoad, new Event\LifecycleEventArgs($entity, $this->sm)); + $this->evm->dispatchEvent(Events::postLoad, new Event\PostLoadEventArgs($entity, $data, $this->sm)); } return $entity; @@ -315,13 +351,15 @@ public function hydrateEntity(ClassMetadata $class, $document) /** * Checks whether an entity is registered in the identity map of this UnitOfWork. * - * @param object $entity - * + * @param Searchable $entity * @return boolean */ public function isInIdentityMap($entity) { $oid = spl_object_hash($entity); - return isset($this->scheduledForPersist[$oid]) || isset($this->scheduledForDelete[$oid]); + $class = get_class($entity); + return isset($this->scheduledForPersist[$class][$oid]) + || isset($this->scheduledForDelete[$class][$oid]); } + } From 75e997cea7df49f14e7461a73f18ac7fee1a8708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:44:02 +0100 Subject: [PATCH 21/45] Refactored Query --- lib/Doctrine/Search/Query.php | 178 +++++++++++++++++++++++++++++----- 1 file changed, 153 insertions(+), 25 deletions(-) diff --git a/lib/Doctrine/Search/Query.php b/lib/Doctrine/Search/Query.php index 61bbf57..765e330 100644 --- a/lib/Doctrine/Search/Query.php +++ b/lib/Doctrine/Search/Query.php @@ -19,14 +19,16 @@ namespace Doctrine\Search; -use Doctrine\Search\Exception\DoctrineSearchException; +use Doctrine; use Elastica; + + class Query { const HYDRATE_BYPASS = -1; - const HYDRATE_INTERNAL = -2; + const HYDRATE_QUERY = NULL; const HYDRATION_PARAMETER = 'ids'; @@ -36,7 +38,7 @@ class Query protected $sm; /** - * @var object + * @var Elastica\Query */ protected $query; @@ -58,7 +60,7 @@ class Query /** * @var integer */ - protected $hydrationMode; + protected $hydrationMode = self::HYDRATE_INTERNAL; /** * @var boolean @@ -70,6 +72,11 @@ class Query */ protected $cacheLifetime; + /** + * @var string + */ + protected $resultCacheId; + /** * @var integer */ @@ -80,17 +87,32 @@ class Query */ protected $facets; + /** + * @var integer + */ + protected $firstResult; + + /** + * @var integer + */ + protected $maxResults; + + + public function __construct(SearchManager $sm) { $this->sm = $sm; } + + /** * Magic method to pass query building to the underlying query * object, saving the need to abstract. * * @param string $method * @param array $arguments + * @return Query */ public function __call($method, $arguments) { @@ -102,10 +124,13 @@ public function __call($method, $arguments) return $this; } + + /** * Specifies the searchable entity class to search against. * - * @param mixed $entityClasses + * @param string|array $entityClasses + * @return Query */ public function from($entityClasses) { @@ -113,27 +138,44 @@ public function from($entityClasses) return $this; } + + /** * Set the query object to be executed on the search engine * * @param mixed $query + * @return Query */ public function searchWith($query) { + $client = $this->sm->getClient(); + + if ($client instanceof ElasticSearch\Client) { + $query = Elastica\Query::create($query); + + } else { + throw new NotImplementedException; + } + $this->query = $query; return $this; } + + protected function getSearchManager() { return $this->sm; } + + /** * Set the hydration mode from the underlying query modes * or bypass and return search result directly from the client * * @param integer $mode + * @return Query */ public function setHydrationMode($mode) { @@ -141,37 +183,71 @@ public function setHydrationMode($mode) return $this; } + + /** * If hydrating with Doctrine then you can use the result cache * on the default or provided query * * @param boolean $useCache * @param integer $cacheLifetime + * @param string $resultCacheId + * @return Query */ - public function useResultCache($useCache, $cacheLifetime = null) + public function useResultCache($useCache, $cacheLifetime = null, $resultCacheId = NULL) { $this->useResultCache = $useCache; $this->cacheLifetime = $cacheLifetime; + $this->resultCacheId = $resultCacheId; return $this; } + + /** - * Return the total hit count for the given query as provided by - * the search engine. + * @return int */ - public function count() + public function getFirstResult() { - return $this->count; + return $this->firstResult; } + + /** - * @return array + * @param int $firstResult + * @return Query */ - public function getFacets() + public function setFirstResult($firstResult) { - return $this->facets; + $this->firstResult = $firstResult; + return $this; + } + + + + /** + * @return int + */ + public function getMaxResults() + { + return $this->maxResults; } + + + /** + * @param int $maxResults + * @return Query + */ + public function setMaxResults($maxResults) + { + $this->maxResults = $maxResults; + return $this; + } + + + /** * Set a custom Doctrine Query to execute in order to hydrate the search * engine results into required entities. The assumption is made the the @@ -180,36 +256,57 @@ public function getFacets() * * @param object $hydrationQuery * @param string $parameter + * @return Query */ public function hydrateWith($hydrationQuery, $parameter = null) { + if ($hydrationQuery instanceof Doctrine\ORM\QueryBuilder) { + if (!$this->entityClasses) { + $this->entityClasses = $hydrationQuery->getRootEntities(); + } + + $hydrationQuery = $hydrationQuery->getQuery(); + + } elseif ($hydrationQuery instanceof Doctrine\ORM\AbstractQuery) { + // pass + + } else { + throw new NotImplementedException(sprintf('Unsupported type of hydration query provided: %s', get_class($hydrationQuery))); + } + + $this->hydrationMode = self::HYDRATE_QUERY; $this->hydrationQuery = $hydrationQuery; if ($parameter) { $this->hydrationParameter = $parameter; } + return $this; } + + /** * Return a provided hydration query * - * @return object + * @return Doctrine\ORM\AbstractQuery */ protected function getHydrationQuery() { if (!$this->hydrationQuery) { - throw new DoctrineSearchException('A hydration query is required for hydrating results to entities.'); + throw new InvalidStateException('A hydration query is required for hydrating results to entities.'); } return $this->hydrationQuery; } + + /** * Execute search and hydrate results if required. * * @param integer $hydrationMode - * @throws DoctrineSearchException - * @return mixed + * @throws InvalidStateException + * @return array|Searchable[] */ public function getResult($hydrationMode = null) { @@ -222,35 +319,66 @@ public function getResult($hydrationMode = null) $classes[] = $this->sm->getClassMetadata($entityClass); } - $resultSet = $this->getSearchManager()->getClient()->search($this->query, $classes); + if ($this->query instanceof Elastica\Query) { + if ($this->maxResults) { + $this->query->setSize($this->maxResults); + } + if ($this->firstResult) { + $this->query->setFrom($this->firstResult); + } + } + + $client = $this->getSearchManager()->getClient(); + $resultSet = $client->search($this->query, $classes); - // TODO: abstraction of support for different result sets if ($resultSet instanceof Elastica\ResultSet) { $this->count = $resultSet->getTotalHits(); $this->facets = $resultSet->getFacets(); $results = $resultSet->getResults(); } else { - $resultClass = get_class($resultSet); - throw new DoctrineSearchException("Unexpected result set class '$resultClass'"); + throw new NotImplementedException(sprintf('Unexpected result set class \'%s\'', get_class($resultSet))); } // Return results depending on hydration mode if ($this->hydrationMode == self::HYDRATE_BYPASS) { return $resultSet; + } elseif ($this->hydrationMode == self::HYDRATE_INTERNAL) { return $this->sm->getUnitOfWork()->hydrateCollection($classes, $resultSet); } // Document ids are used to lookup dbms results - $fn = function ($result) { + $ids = array_map(function ($result) { + /** @var Elastica\Document $result */ return $result->getId(); - }; - $ids = array_map($fn, $results); + }, $results); return $this->getHydrationQuery() ->setParameter($this->hydrationParameter, $ids ?: null) - ->useResultCache($this->useResultCache, $this->cacheLifetime) + ->useResultCache($this->useResultCache, $this->cacheLifetime, $this->resultCacheId) ->getResult($this->hydrationMode); } + + + + /** + * Return the total hit count for the given query as provided by + * the search engine. + */ + public function count() + { + return $this->count; + } + + + + /** + * @return array + */ + public function getFacets() + { + return $this->facets; + } + } From c6a7b3d1688503e58fbe3862708f06c251029e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:44:18 +0100 Subject: [PATCH 22/45] Refactored EntityRepository --- lib/Doctrine/Search/EntityRepository.php | 34 +++++++++---------- .../Search/EntityRepositoryCollection.php | 9 ++--- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/lib/Doctrine/Search/EntityRepository.php b/lib/Doctrine/Search/EntityRepository.php index 6f52c80..952355c 100644 --- a/lib/Doctrine/Search/EntityRepository.php +++ b/lib/Doctrine/Search/EntityRepository.php @@ -20,32 +20,32 @@ namespace Doctrine\Search; use Doctrine\Common\Persistence\ObjectRepository; -use Doctrine\Search\SearchManager; use Doctrine\Search\Mapping\ClassMetadata; -use Doctrine\Search\Exception\DoctrineSearchException; + + class EntityRepository implements ObjectRepository { /** * @var string */ - protected $_entityName; + protected $entityName; /** * @var \Doctrine\Search\Mapping\ClassMetadata */ - private $_class; + private $class; /** * @var \Doctrine\Search\SearchManager */ - private $_sm; + private $sm; public function __construct(SearchManager $sm, ClassMetadata $class) { - $this->_sm = $sm; - $this->_entityName = $class->className; - $this->_class = $class; + $this->sm = $sm; + $this->entityName = $class->className; + $this->class = $class; } /** @@ -56,7 +56,7 @@ public function __construct(SearchManager $sm, ClassMetadata $class) */ public function find($id) { - return $this->_sm->find($this->_entityName, $id); + return $this->sm->find($this->entityName, $id); } /** @@ -66,7 +66,7 @@ public function find($id) */ public function findAll() { - throw new DoctrineSearchException('Not yet implemented.'); + throw new NotImplementedException; } /** @@ -85,7 +85,7 @@ public function findAll() */ public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) { - throw new DoctrineSearchException('Not yet implemented.'); + throw new NotImplementedException(); } /** @@ -98,7 +98,7 @@ public function findOneBy(array $criteria) { $options = array('field' => key($criteria)); $value = current($criteria); - return $this->_sm->getUnitOfWork()->load($this->_class, $value, $options); + return $this->sm->getUnitOfWork()->load($this->class, $value, $options); } /** @@ -108,7 +108,7 @@ public function findOneBy(array $criteria) */ public function search($query) { - return $this->_sm->getUnitOfWork()->loadCollection(array($this->_class), $query); + return $this->sm->getUnitOfWork()->loadCollection(array($this->class), $query); } /** @@ -118,7 +118,7 @@ public function search($query) */ public function delete($query) { - $this->_sm->getClient()->removeAll($this->_class, $query); + $this->sm->getClient()->removeAll($this->class, $query); } /** @@ -128,7 +128,7 @@ public function delete($query) */ public function getClassName() { - return $this->_entityName; + return $this->entityName; } /** @@ -138,7 +138,7 @@ public function getClassName() */ public function getClassMetadata() { - return $this->_class; + return $this->class; } /** @@ -148,6 +148,6 @@ public function getClassMetadata() */ public function getSearchManager() { - return $this->_sm; + return $this->sm; } } diff --git a/lib/Doctrine/Search/EntityRepositoryCollection.php b/lib/Doctrine/Search/EntityRepositoryCollection.php index 3d5960c..8275f7b 100644 --- a/lib/Doctrine/Search/EntityRepositoryCollection.php +++ b/lib/Doctrine/Search/EntityRepositoryCollection.php @@ -20,7 +20,8 @@ namespace Doctrine\Search; use Doctrine\Common\Persistence\ObjectRepository; -use Doctrine\Search\Exception\DoctrineSearchException; + + class EntityRepositoryCollection implements ObjectRepository { @@ -67,7 +68,7 @@ public function find($id) */ public function findAll() { - throw new DoctrineSearchException('Not yet implemented.'); + throw new NotImplementedException; } /** @@ -86,7 +87,7 @@ public function findAll() */ public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) { - throw new DoctrineSearchException('Not yet implemented.'); + throw new NotImplementedException; } /** @@ -97,7 +98,7 @@ public function findBy(array $criteria, array $orderBy = null, $limit = null, $o */ public function findOneBy(array $criteria) { - throw new DoctrineSearchException('Not yet implemented.'); + throw new NotImplementedException; } /** From 0f51c3f9d849b3d1450766c34d60d347dceef1c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 01:44:44 +0100 Subject: [PATCH 23/45] composer: require-dev includes jms/serializer, elastica and neon --- composer.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5b258fd..9a5d789 100644 --- a/composer.json +++ b/composer.json @@ -16,10 +16,14 @@ "doctrine/common": ">=2.3.0" }, "suggest": { - "ruflin/Elastica": "Elastica version correlating to your ElasticSearch installation is required." + "ruflin/Elastica": "Elastica version correlating to your ElasticSearch installation is required.", + "nette/neon": "Neon is a developer friendly format for config files, similar to YAML." }, "require-dev": { "doctrine/orm": "~2.4", + "jms/serializer": "~0.16", + "ruflin/elastica": "~1.3.0", + "nette/neon": "~2.2", "phpunit/phpunit": "~4.2" }, "autoload": { From 74f7bf6c6524435ecc21bfd061c7da15d6b5d56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 02:39:09 +0100 Subject: [PATCH 24/45] fixed exceptions autoloading --- composer.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9a5d789..fdbc8c5 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,10 @@ "autoload": { "psr-0": { "Doctrine\\Search": "lib/" - } + }, + "classmap": [ + "lib/Doctrine/Search/exceptions.php" + ] }, "autoload-dev": { "psr-0": { From 30cc63d4969f57d44678754c10be7bff91e5acd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 02:39:18 +0100 Subject: [PATCH 25/45] fixed type error --- lib/Doctrine/Search/Mapping/Driver/NeonDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php index 2048567..d7325b1 100644 --- a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php @@ -79,7 +79,7 @@ protected function loadTypeMapping(TypeMetadata $type, $typeMapping) $settings = $typeMapping; unset($settings['class'], $settings['index'], $settings['type'], $settings['properties'], $settings['parameters'], $settings['serializer']); - $type->setSettings($settings); + $type->setSettings((array) $settings); } From 456010116f740d2595ccd6c7d8fe4c988bb072d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 03:19:16 +0100 Subject: [PATCH 26/45] Fixed cannot use IndexMetadata as array --- lib/Doctrine/Search/ElasticSearch/SchemaManager.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Doctrine/Search/ElasticSearch/SchemaManager.php b/lib/Doctrine/Search/ElasticSearch/SchemaManager.php index 0657dcd..7115181 100644 --- a/lib/Doctrine/Search/ElasticSearch/SchemaManager.php +++ b/lib/Doctrine/Search/ElasticSearch/SchemaManager.php @@ -104,7 +104,7 @@ public function createMappings(array $classes, $withAliases = FALSE) $aliases[$indexAlias] = $class->getIndexName(); $fakeMetadata = clone $class; - $fakeMetadata->index['name'] = $indexAlias; + $fakeMetadata->index->name = $indexAlias; $class = $fakeMetadata; } @@ -144,12 +144,12 @@ public function createIndex(ClassMetadata $class) { $index = $this->elastica->getIndex($class->getIndexName()); $response = $index->create(array( - 'number_of_shards' => $class->index['number_of_shards'], - 'number_of_replicas' => $class->index['number_of_replicas'], + 'number_of_shards' => $class->index->numberOfShards, + 'number_of_replicas' => $class->index->numberOfReplicas, 'analysis' => array( - 'char_filter' => $class->index['char_filter'], - 'analyzer' => $class->index['analyzer'], - 'filter' => $class->index['filter'], + 'char_filter' => $class->index->charFilter, + 'analyzer' => $class->index->analyzer, + 'filter' => $class->index->filter, ), ), TRUE); From 5d1514615914b59f5c6273cb9b168e382c9601f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 03:19:35 +0100 Subject: [PATCH 27/45] Fixed loading metadata in NeonDriver --- .../Search/Mapping/Driver/NeonDriver.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php index d7325b1..6212bb3 100644 --- a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php @@ -56,7 +56,10 @@ public function __construct($metadataDirectory) */ public function loadMetadataForClass($className, ClassMetadata $metadata) { - $typeMapping = $this->getTypeMapping($className); + if (!$typeMapping = $this->getTypeMapping($className)) { + return; + } + $this->loadTypeMapping($metadata->type, $typeMapping); $indexMapping = $this->getIndexMapping($typeMapping['index']); @@ -77,9 +80,13 @@ protected function loadTypeMapping(TypeMetadata $type, $typeMapping) $type->setParameters($typeMapping['parameters']); } - $settings = $typeMapping; - unset($settings['class'], $settings['index'], $settings['type'], $settings['properties'], $settings['parameters'], $settings['serializer']); - $type->setSettings((array) $settings); + unset($typeMapping['properties'], $typeMapping['parameters']); + + $type->source = !empty($typeMapping['source']); + $type->boost = !empty($typeMapping['boost']) ? $typeMapping['boost'] : NULL; + + unset($typeMapping['class'], $typeMapping['index'], $typeMapping['source'], $typeMapping['type']); + $type->setSettings((array) $typeMapping); } @@ -121,7 +128,7 @@ public function getAllClassNames() */ public function isTransient($className) { - return (bool) $this->getTypeMapping($className); + return ! $this->getTypeMapping($className); } From 8ae9174947159c5bb49132f12eaf0a07e6438520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 03:19:50 +0100 Subject: [PATCH 28/45] Fixed sections inheritance; todo remove nette\di --- .../Search/Mapping/Driver/NeonDriver.php | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php index 6212bb3..3f5ed0d 100644 --- a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php @@ -8,6 +8,7 @@ use Doctrine\Search\Mapping\IndexMetadata; use Doctrine\Search\Mapping\TypeMetadata; use Nette\Neon\Neon; +use Nette\DI\Config; @@ -201,10 +202,33 @@ protected function getIndexesMapping() return $this->indexesMapping; } + // todo: refactor away usage of Nette\DI + $adapter = new Config\Adapters\NeonAdapter(); + $this->indexesMapping = array(); foreach (glob($this->metadataDirectory . '/*.index.neon') as $file) { - $meta = Neon::decode(file_get_contents($file)); + $meta = $adapter->load($file); $meta['name'] = basename($file, '.index.neon'); + + // $indexConfig = Config\Helpers::merge($meta, $this->indexDefaults); + unset($analysisSection); + foreach (array('charFilter', 'filter', 'analyzer') as $analysisType) { + $analysisSection = $meta[$analysisType]; + unset($setup); + foreach ($analysisSection as $name => $setup) { + if (!Config\Helpers::isInheriting($setup)) { + continue; + } + $parent = Config\Helpers::takeParent($setup); + if (!isset($analysisSection[$parent])) { + throw new \Nette\Utils\AssertionException(sprintf('The %s.%s cannot inherit undefined %s.%s', $analysisType, $name, $analysisType, $parent)); + } + $analysisSection[$name] = Config\Helpers::merge($setup, $analysisSection[$parent]); + } + + $meta[$analysisType] = $analysisSection; + } + $this->indexesMapping[$meta['name']] = $meta; } From 892155e4a09e7ed5ea7ff468589ce97231f83853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 19:06:28 +0100 Subject: [PATCH 29/45] Rename SearchableListener to Tools\OrmSearchableListener --- .../OrmSearchableListener.php} | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) rename lib/Doctrine/Search/{SearchableListener.php => Tools/OrmSearchableListener.php} (88%) diff --git a/lib/Doctrine/Search/SearchableListener.php b/lib/Doctrine/Search/Tools/OrmSearchableListener.php similarity index 88% rename from lib/Doctrine/Search/SearchableListener.php rename to lib/Doctrine/Search/Tools/OrmSearchableListener.php index 1a4298e..248e830 100644 --- a/lib/Doctrine/Search/SearchableListener.php +++ b/lib/Doctrine/Search/Tools/OrmSearchableListener.php @@ -8,18 +8,20 @@ * For the full copyright and license information, please view the file license.txt that was distributed with this source code. */ -namespace Doctrine\Search; +namespace Doctrine\Search\Tools; use Doctrine; use Doctrine\Common\EventSubscriber; use Doctrine\Search\Event\LifecycleEventArgs; +use Doctrine\Search\Searchable; +use Doctrine\Search\SearchManager; /** * @author Filip Procházka */ -class SearchableListener implements EventSubscriber +class OrmSearchableListener implements EventSubscriber { /** From 336d737ed285abbdc93f1fe08eb43ee9c692d45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 19:07:35 +0100 Subject: [PATCH 30/45] readme fixed --- README.markdown | 47 +++++++++++------------------------------------ 1 file changed, 11 insertions(+), 36 deletions(-) diff --git a/README.markdown b/README.markdown index fa0a6a2..384c359 100755 --- a/README.markdown +++ b/README.markdown @@ -47,8 +47,8 @@ $searchManager = new Doctrine\Search\SearchManager( ``` ## Mappings ## -Basic entity mappings for index and type generation can be annotated as shown in the following example. Mappings -can be rendered into a format suitable for automatically generating indexes and types using a build script +Basic entity mappings for index and type generation can be annotated as shown in the following example. Mappings +can be rendered into a format suitable for automatically generating indexes and types using a build script (advanced setup required). ```php 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 ### This approach simply expects a `toArray()` method on the entity, although this method be configured as required. @@ -132,7 +107,7 @@ entities that need to be persisted to the search engine (see above example). ... use Entities\Behaviour\SearchableEntityInterface -class Post implements SearchableEntityInterface +class Post implements SearchableEntityInterface { ... public function toArray() { @@ -147,7 +122,7 @@ class Post implements SearchableEntityInterface ``` ### JMS Serializer ### -You can alternatively use the advanced serialization power of the `JMS Serializer` to automatically handle +You can alternatively use the advanced serialization power of the `JMS Serializer` to automatically handle serialization for you based on annotations such as those shown in this example. ```php ... @@ -169,11 +144,11 @@ class Post implements SearchableEntityInterface * @JMS\Groups({"public", "search"}) */ private $title; - + /** * @ORM\Column(type="text") * @MAP\ElasticField(type="string", includeInAll=true) - * @JMS\Expose + * @JMS\Expose * @JMS\Groups({"public", "search"}) */ private $content; @@ -197,7 +172,7 @@ $hydrationQuery = $entityManager->createQueryBuilder() ->where('p.id IN (:ids)') ->orderBy('field') ->getQuery(); - + $query = $searchManager->createQuery() ->from('Entities\Post') ->searchWith(new Elastica\Query()) From 378fc266939fffbb4dae3e53385e3cb587089182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Mon, 29 Dec 2014 19:08:47 +0100 Subject: [PATCH 31/45] Expect ORM\Event not Search\Event in OrmSearchableListener --- lib/Doctrine/Search/Tools/OrmSearchableListener.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/Search/Tools/OrmSearchableListener.php b/lib/Doctrine/Search/Tools/OrmSearchableListener.php index 248e830..7f133cb 100644 --- a/lib/Doctrine/Search/Tools/OrmSearchableListener.php +++ b/lib/Doctrine/Search/Tools/OrmSearchableListener.php @@ -12,7 +12,7 @@ use Doctrine; use Doctrine\Common\EventSubscriber; -use Doctrine\Search\Event\LifecycleEventArgs; +use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\Search\Searchable; use Doctrine\Search\SearchManager; From 60cf953df790c449c88f6701d56bafad62762382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Koub=C3=ADk?= Date: Fri, 9 Jan 2015 15:44:04 +0100 Subject: [PATCH 32/45] Fixed race condition when creating index aliases --- lib/Doctrine/Search/ElasticSearch/SchemaManager.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/Search/ElasticSearch/SchemaManager.php b/lib/Doctrine/Search/ElasticSearch/SchemaManager.php index 7115181..ce1f838 100644 --- a/lib/Doctrine/Search/ElasticSearch/SchemaManager.php +++ b/lib/Doctrine/Search/ElasticSearch/SchemaManager.php @@ -98,9 +98,10 @@ public function dropMappings(array $classes) public function createMappings(array $classes, $withAliases = FALSE) { $aliases = []; + $date = date('YmdHis'); foreach ($classes as $class) { if ($withAliases) { - $indexAlias = $class->getIndexName() . '_' . date('YmdHis'); + $indexAlias = $class->getIndexName() . '_' . $date; $aliases[$indexAlias] = $class->getIndexName(); $fakeMetadata = clone $class; From 078962223edcd35b201519244b8fade9f8629059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Fri, 10 Apr 2015 12:00:50 +0200 Subject: [PATCH 33/45] Autofix type property settings to underscore --- .../Search/ElasticSearch/SchemaManager.php | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/Search/ElasticSearch/SchemaManager.php b/lib/Doctrine/Search/ElasticSearch/SchemaManager.php index ce1f838..be38311 100644 --- a/lib/Doctrine/Search/ElasticSearch/SchemaManager.php +++ b/lib/Doctrine/Search/ElasticSearch/SchemaManager.php @@ -175,7 +175,7 @@ public function hasType(ClassMetadata $class) public function createType(ClassMetadata $class) { - $mapping = new ElasticaTypeMapping($this->getType($class), $class->type->properties); + $mapping = new ElasticaTypeMapping($this->getType($class), self::settingsToUnderscore($class->type->properties)); $mapping->disableSource($class->type->source); if ($class->type->boost !== NULL) { @@ -216,4 +216,41 @@ public function createAlias($alias, $original) $this->elastica->request(sprintf('/%s/_alias/%s', $alias, $original), Request::PUT); } + + + private static function settingsToUnderscore($properties) + { + foreach ($properties as $name => $settings) { + $fixed = []; + foreach ($settings as $key => $value) { + $fixed[self::toUnderscore($key)] = $value; + } + + if (isset($settings['properties'])) { + $fixed['properties'] = self::settingsToUnderscore($settings['properties']); + } + + $properties[$name] = $fixed; + } + + return $properties; + } + + + + /** + * camelCaseField name -> underscore_separated. + * + * @param string $s + * @return string + */ + private static function toUnderscore($s) + { + $s = preg_replace('#(.)(?=[A-Z])#', '$1_', $s); + $s = strtolower($s); + $s = rawurlencode($s); + + return $s; + } + } From a6a9824a146bb23d9f625134765cb83d31097fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Fri, 10 Apr 2015 18:28:19 +0200 Subject: [PATCH 34/45] Experimental: ClassMetadata->riverImplementation --- lib/Doctrine/Search/EntityRiver.php | 26 +++++++++++++++++++ lib/Doctrine/Search/Mapping/ClassMetadata.php | 5 ++++ .../Search/Mapping/Driver/NeonDriver.php | 6 ++++- 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 lib/Doctrine/Search/EntityRiver.php diff --git a/lib/Doctrine/Search/EntityRiver.php b/lib/Doctrine/Search/EntityRiver.php new file mode 100644 index 0000000..41edb48 --- /dev/null +++ b/lib/Doctrine/Search/EntityRiver.php @@ -0,0 +1,26 @@ + + */ +interface EntityRiver +{ + + public function transfuse(Mapping\ClassMetadata $searchMeta); + +} diff --git a/lib/Doctrine/Search/Mapping/ClassMetadata.php b/lib/Doctrine/Search/Mapping/ClassMetadata.php index ae2f2f2..6e2f599 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadata.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadata.php @@ -64,6 +64,11 @@ class ClassMetadata implements ClassMetadataInterface */ public $className; + /** + * @var string with class implementing \Doctrine\Search\EntityRiver + */ + public $riverImplementation; + /** * @var ClassMetadataInterface|\Doctrine\ORM\Mapping\ClassMetadata */ diff --git a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php index 3f5ed0d..2347085 100644 --- a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php @@ -65,6 +65,10 @@ public function loadMetadataForClass($className, ClassMetadata $metadata) $indexMapping = $this->getIndexMapping($typeMapping['index']); $this->loadIndexMapping($metadata->index, $indexMapping); + + if (!empty($typeMapping['river'])) { + $metadata->riverImplementation = $typeMapping['river']; + } } @@ -86,7 +90,7 @@ protected function loadTypeMapping(TypeMetadata $type, $typeMapping) $type->source = !empty($typeMapping['source']); $type->boost = !empty($typeMapping['boost']) ? $typeMapping['boost'] : NULL; - unset($typeMapping['class'], $typeMapping['index'], $typeMapping['source'], $typeMapping['type']); + unset($typeMapping['class'], $typeMapping['river'], $typeMapping['index'], $typeMapping['source'], $typeMapping['type']); $type->setSettings((array) $typeMapping); } From 0a955e0b7cb8b535628985aa4a09a09303782cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Tue, 14 Apr 2015 18:52:52 +0200 Subject: [PATCH 35/45] Fixed serialization of riverImplementation in ClassMetadata --- lib/Doctrine/Search/Mapping/ClassMetadata.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Doctrine/Search/Mapping/ClassMetadata.php b/lib/Doctrine/Search/Mapping/ClassMetadata.php index 6e2f599..01bd92c 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadata.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadata.php @@ -94,6 +94,7 @@ public function __sleep() 'index', 'parent', 'className', + 'riverImplementation', ); } From afab25d681fa21725fa5d3849074884e1ffbf3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Tue, 14 Apr 2015 18:54:39 +0200 Subject: [PATCH 36/45] Fixed deep clone of ClassMetadata --- lib/Doctrine/Search/Mapping/ClassMetadata.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/Doctrine/Search/Mapping/ClassMetadata.php b/lib/Doctrine/Search/Mapping/ClassMetadata.php index 01bd92c..cf24594 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadata.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadata.php @@ -348,4 +348,12 @@ public function wakeupReflection(ReflectionService $reflService, ClassMetadataIn $this->parentMetadata = $parentMetadata; } + + + public function __clone() + { + $this->type = clone $this->type; + $this->index = clone $this->index; + } + } From 37f0b7a255435313344b4ad47b47f0ecc7282d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Tue, 14 Apr 2015 19:23:49 +0200 Subject: [PATCH 37/45] Fixed loading '0' value in metadata --- lib/Doctrine/Search/Mapping/Driver/NeonDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php index 2347085..a19dea7 100644 --- a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php @@ -99,7 +99,7 @@ protected function loadTypeMapping(TypeMetadata $type, $typeMapping) protected function loadIndexMapping(IndexMetadata $index, $indexMapping) { foreach (array('name', 'numberOfShards', 'numberOfReplicas', 'charFilter', 'filter', 'analyzer') as $key) { - if (empty($indexMapping[$key])) { + if (!array_key_exists($key, $indexMapping)) { continue; } From 7b5b376ab7791d9bd51457052af691a140f42298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Fri, 15 May 2015 18:37:44 +0200 Subject: [PATCH 38/45] Use ClassUtils::getRealClass in UnitOfWork --- lib/Doctrine/Search/UnitOfWork.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/Search/UnitOfWork.php b/lib/Doctrine/Search/UnitOfWork.php index d010792..3e0281f 100644 --- a/lib/Doctrine/Search/UnitOfWork.php +++ b/lib/Doctrine/Search/UnitOfWork.php @@ -20,9 +20,9 @@ namespace Doctrine\Search; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Util\ClassUtils; use Doctrine\Search\Mapping\ClassMetadata; use Elastica\Document; -use Elastica\Search; use Traversable; @@ -81,7 +81,7 @@ public function persist($entity) } $oid = spl_object_hash($entity); - $class = get_class($entity); + $class = ClassUtils::getRealClass($entity); $this->scheduledForPersist[$class][$oid] = $entity; if ($this->evm->hasListeners(Events::postPersist)) { @@ -101,7 +101,7 @@ public function remove($entity) } $oid = spl_object_hash($entity); - $class = get_class($entity); + $class = ClassUtils::getRealClass($entity); unset($this->scheduledForPersist[$class][$oid]); $this->scheduledForDelete[$class][$oid] = $entity; @@ -357,7 +357,7 @@ public function hydrateEntity(ClassMetadata $class, $document) public function isInIdentityMap($entity) { $oid = spl_object_hash($entity); - $class = get_class($entity); + $class = ClassUtils::getRealClass($entity); return isset($this->scheduledForPersist[$class][$oid]) || isset($this->scheduledForDelete[$class][$oid]); } From cd29a19ab53bdaa61a87981f01afe4e3b4045d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 21 May 2015 15:53:56 +0200 Subject: [PATCH 39/45] Fixed usage of ClassUtils::getRealClass in UoW [Closes #3] --- lib/Doctrine/Search/UnitOfWork.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/Search/UnitOfWork.php b/lib/Doctrine/Search/UnitOfWork.php index 3e0281f..34a198b 100644 --- a/lib/Doctrine/Search/UnitOfWork.php +++ b/lib/Doctrine/Search/UnitOfWork.php @@ -81,7 +81,7 @@ public function persist($entity) } $oid = spl_object_hash($entity); - $class = ClassUtils::getRealClass($entity); + $class = ClassUtils::getRealClass(get_class($entity)); $this->scheduledForPersist[$class][$oid] = $entity; if ($this->evm->hasListeners(Events::postPersist)) { @@ -101,7 +101,7 @@ public function remove($entity) } $oid = spl_object_hash($entity); - $class = ClassUtils::getRealClass($entity); + $class = ClassUtils::getRealClass(get_class($entity)); unset($this->scheduledForPersist[$class][$oid]); $this->scheduledForDelete[$class][$oid] = $entity; @@ -357,7 +357,7 @@ public function hydrateEntity(ClassMetadata $class, $document) public function isInIdentityMap($entity) { $oid = spl_object_hash($entity); - $class = ClassUtils::getRealClass($entity); + $class = ClassUtils::getRealClass(get_class($entity)); return isset($this->scheduledForPersist[$class][$oid]) || isset($this->scheduledForDelete[$class][$oid]); } From 34eeba85121d28ea6de46fc41044ac4bfe8c6495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 9 Jul 2015 04:19:22 +0200 Subject: [PATCH 40/45] phpdoc --- lib/Doctrine/Search/Query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/Search/Query.php b/lib/Doctrine/Search/Query.php index 765e330..4300408 100644 --- a/lib/Doctrine/Search/Query.php +++ b/lib/Doctrine/Search/Query.php @@ -306,7 +306,7 @@ protected function getHydrationQuery() * * @param integer $hydrationMode * @throws InvalidStateException - * @return array|Searchable[] + * @return array|Elastica\ResultSet|Searchable[] */ public function getResult($hydrationMode = null) { From 1de14f0c77650b577414f6b3af2bac95180c5dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Thu, 9 Jul 2015 04:19:35 +0200 Subject: [PATCH 41/45] Schedule getters on UnitOfWork --- lib/Doctrine/Search/UnitOfWork.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/Doctrine/Search/UnitOfWork.php b/lib/Doctrine/Search/UnitOfWork.php index 34a198b..f405f46 100644 --- a/lib/Doctrine/Search/UnitOfWork.php +++ b/lib/Doctrine/Search/UnitOfWork.php @@ -362,4 +362,24 @@ public function isInIdentityMap($entity) || isset($this->scheduledForDelete[$class][$oid]); } + + + /** + * @return array + */ + public function getScheduledForDelete() + { + return $this->scheduledForDelete; + } + + + + /** + * @return array + */ + public function getScheduledForPersist() + { + return $this->scheduledForPersist; + } + } From c6d0808c72961ad0b535f0062a6efc5f96d04515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Proch=C3=A1zka?= Date: Sun, 19 Jul 2015 16:16:48 +0200 Subject: [PATCH 42/45] Allow to configure index prefixing --- lib/Doctrine/Search/Configuration.php | 16 ++++++++++++++++ .../Search/Mapping/ClassMetadataFactory.php | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/lib/Doctrine/Search/Configuration.php b/lib/Doctrine/Search/Configuration.php index d63366b..fe78b5f 100755 --- a/lib/Doctrine/Search/Configuration.php +++ b/lib/Doctrine/Search/Configuration.php @@ -38,6 +38,22 @@ class Configuration */ private $attributes; + /** + * @param string $prefix + */ + public function setIndexPrefix($prefix) + { + $this->attributes['indexPrefix'] = $prefix; + } + + /** + * @return string|NULL + */ + public function getIndexPrefix() + { + return isset($this->attributes['indexPrefix']) ? $this->attributes['indexPrefix'] : NULL; + } + /** * Gets the cache driver implementation that is used for the mapping metadata. * (Annotation is the default) diff --git a/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php b/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php index 91408c9..dd6b0d8 100644 --- a/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/Search/Mapping/ClassMetadataFactory.php @@ -165,6 +165,10 @@ protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonS //Manipulates $classMetadata; $this->driver->loadMetadataForClass($class->getName(), $class); + if (($prefix = $this->config->getIndexPrefix()) !== NULL) { + $class->index->name = $prefix . $class->index->name; + } + if ($this->evm->hasListeners(Events::loadClassMetadata)) { $eventArgs = new LoadClassMetadataEventArgs($class, $this->sm); $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); From f99f6bf8e03be8b892274cb49e764ca4e6a12a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Koub=C3=ADk?= Date: Tue, 21 Jul 2015 13:28:54 +0200 Subject: [PATCH 43/45] ChainSerializer: if a serializer serializes type T, it also serializes subtypes of T --- .../Search/Serializer/ChainSerializer.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/Doctrine/Search/Serializer/ChainSerializer.php b/lib/Doctrine/Search/Serializer/ChainSerializer.php index d1b8281..c58b203 100644 --- a/lib/Doctrine/Search/Serializer/ChainSerializer.php +++ b/lib/Doctrine/Search/Serializer/ChainSerializer.php @@ -28,7 +28,7 @@ class ChainSerializer implements SerializerInterface public function addSerializer($classType, SerializerInterface $serializer) { - $this->serializers[strtolower($classType)] = $serializer; + $this->serializers[$classType] = $serializer; } @@ -47,9 +47,10 @@ public function setDefaultSerializer(SerializerInterface $serializer) */ public function serialize($object) { - $lName = strtolower(ClassUtils::getClass($object)); - if (isset($this->serializers[$lName])) { - return $this->serializers[$lName]->serialize($object); + foreach ($this->serializers as $classType => $serializer) { + if ($object instanceof $classType) { + return $serializer->serialize($object); + } } if (!$this->defaultSerializer) { @@ -69,9 +70,9 @@ public function serialize($object) */ public function deserialize($entityName, $data) { - $lName = strtolower($entityName); - if (isset($this->serializers[$lName])) { - return $this->serializers[$lName]->deserialize($entityName, $data); + $classType = $entityName; + if (isset($this->serializers[$classType])) { + return $this->serializers[$classType]->deserialize($entityName, $data); } if (!$this->defaultSerializer) { From 1dcec059076f4eb1ed4330b117dddd172d1922c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Koub=C3=ADk?= Date: Tue, 21 Jul 2015 13:30:39 +0200 Subject: [PATCH 44/45] NeonDriver: one mapping can be used also for inherited entities via 'subclasses:' --- lib/Doctrine/Search/Mapping/Driver/NeonDriver.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php index a19dea7..dbdbcb5 100644 --- a/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php +++ b/lib/Doctrine/Search/Mapping/Driver/NeonDriver.php @@ -90,7 +90,7 @@ protected function loadTypeMapping(TypeMetadata $type, $typeMapping) $type->source = !empty($typeMapping['source']); $type->boost = !empty($typeMapping['boost']) ? $typeMapping['boost'] : NULL; - unset($typeMapping['class'], $typeMapping['river'], $typeMapping['index'], $typeMapping['source'], $typeMapping['type']); + unset($typeMapping['class'], $typeMapping['subclasses'], $typeMapping['river'], $typeMapping['index'], $typeMapping['source'], $typeMapping['type']); $type->setSettings((array) $typeMapping); } @@ -148,6 +148,10 @@ protected function getTypeMapping($className) if ($mapping['class'] === $className) { return $mapping; } + + if (isset($mapping['subclasses']) && in_array($className, $mapping['subclasses'])) { + return $mapping; + } } return NULL; From 85a431ef8f1c010719052660e1c17833ab9f510c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Vok=C5=99=C3=ADnek?= Date: Sun, 1 Nov 2015 09:27:46 +0100 Subject: [PATCH 45/45] Fixes the problem with entities not being removed from ES --- lib/Doctrine/Search/Tools/OrmSearchableListener.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/Search/Tools/OrmSearchableListener.php b/lib/Doctrine/Search/Tools/OrmSearchableListener.php index 7f133cb..1d84df0 100644 --- a/lib/Doctrine/Search/Tools/OrmSearchableListener.php +++ b/lib/Doctrine/Search/Tools/OrmSearchableListener.php @@ -64,7 +64,7 @@ public function preRemove(LifecycleEventArgs $oArgs) { $oEntity = $oArgs->getEntity(); if ($oEntity instanceof Searchable) { - $this->sm->remove($oEntity); + $this->sm->remove(clone $oEntity); } }