Permalink
Browse files

Enrichment of SearchManager, ElasticSearch client, and serializer sup…

…port

Mostly updates to ElasticSearch integration with breaking changes for
other clients. Also added support for more complex annotations, Unit of
Work style persistence, and support for different entity serialization
methods for persistence. Updated for Elastica 0.20.x support.
  • Loading branch information...
MrHash committed Apr 28, 2013
1 parent 8cb5b60 commit 6d1195fb039b0acc6e0bc141103b33752667a823
@@ -19,7 +19,15 @@
namespace Doctrine\Search\ElasticSearch;
+
+
use Doctrine\Search\SearchClientInterface;
+use Doctrine\Search\Mapping\ClassMetadata;
+use Elastica\Client as Elastica_Client;
+use Elastica\Type\Mapping;
+use Elastica\Document;
+use Elastica\Index;
+use Elastica\Query\MatchAll;
/**
* SearchManager for ElasticSearch-Backend
@@ -31,18 +39,59 @@
class Client implements SearchClientInterface
{
/**
- * @var \Elastica_Client
+ * @var ElasticaClient
*/
private $client;
/**
- * @param \Elastica_Client $client
+ * @param Elastica_Client $client
*/
- public function __construct(\Elastica\Client $client)
+ public function __construct(Elastica_Client $client)
{
$this->client = $client;
}
+ /**
+ * {@inheritDoc}
+ */
+ public function addDocuments($index, $type, array $documents)
+ {
+ $index = $this->client->getIndex($index);
+ $type = $index->getType($type);
+
+ $batch = array();
+ foreach($documents as $id => $document)
+ {
+ $batch[] = new Document($id, $document);
+ }
+
+ $type->addDocuments($batch);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function removeDocuments($index, $type, array $documents)
+ {
+ $index = $this->client->getIndex($index);
+ $type = $index->getType($type);
+ $type->deleteIds(array_keys($documents));
+ }
+
+ /**
+ * Remove all documents of a given type from the specified index
+ * without deleting the index itself
+ *
+ * @param string $index
+ * @param string $type
+ */
+ public function removeAll($index, $type)
+ {
+ $index = $this->client->getIndex($index);
+ $type = $index->getType($type);
+ $type->deleteByQuery(new MatchAll());
+ }
+
/**
* {@inheritDoc}
*/
@@ -55,12 +104,61 @@ public function find($index, $type, $query)
/**
* {@inheritDoc}
*/
- public function createIndex($index, $type, array $data)
+ public function createIndex($name, array $config = array())
{
- $index = $this->client->getIndex($index);
- $index->create();
+ $index = $this->client->getIndex($name);
+ $index->create($config, true);
+ return $index;
+ }
+
+ /**
+ * Create a document type mapping in the specified index
+ *
+ * @param Elastica\Index $index
+ * @param Doctrine\Search\Mapping\ClassMetadata $metadata
+ */
+ public function createType(Index $index, ClassMetadata $metadata)
+ {
+ $type = $index->getType($metadata->type);
+ $properties = $this->getMapping($metadata->fieldMappings);
- $index->addDocuments($data);
+ $mapping = new Mapping($type, $properties);
+ $mapping->disableSource($metadata->source);
+ $mapping->setParam('_boost', array('name' => '_boost', 'null_value' => $metadata->boost));
+ $mapping->send();
+
+ return $type;
+ }
+
+ /**
+ * Generates property mapping from entity annotations
+ *
+ * @param array $fieldMapping
+ */
+ protected function getMapping($fieldMapping)
+ {
+ $properties = array();
+
+ foreach($fieldMapping as $propertyName => $fieldMapping)
+ {
+ if(isset($fieldMapping->name)) $propertyName = $fieldMapping->name;
+ $properties[$propertyName]['type'] = $fieldMapping->type;
+ if(isset($fieldMapping->includeInAll)) $properties[$propertyName]['include_in_all'] = $fieldMapping->includeInAll;
+ if(isset($fieldMapping->index)) $properties[$propertyName]['index'] = $fieldMapping->index;
+ if(isset($fieldMapping->boost)) $properties[$propertyName]['boost'] = $fieldMapping->boost;
+
+ 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;
}
/**
@@ -29,13 +29,10 @@
*/
class Searchable extends Annotation
{
- /**
- * @var string $index;
- */
+ /** @var string $index */
public $index;
- /**
- * @var string $type;
- */
+
+ /** @var string $type */
public $type;
}
@@ -45,27 +42,29 @@ class Searchable extends Annotation
*/
final class ElasticSearchable extends Searchable
{
- /**
- * @var int $numberOfShards;
- */
+ /** @var int $numberOfShards */
public $numberOfShards;
- /**
- * @var int $numnberOfReplicas
- */
+
+ /** @var int $numnberOfReplicas */
public $numberOfReplicas;
- /**
- * @var string $op_type;
- */
+
+ /** @var string $op_type */
public $opType;
- /**
- * @var float $parent;
- */
+
+ /** @var float $parent */
public $parent;
+
/**
* TTL in milliseconds
* @var int $timeToLive
*/
public $timeToLive;
+
+ /** @var float */
+ public $boost;
+
+ /** @var boolean */
+ public $source;
}
/**
@@ -74,10 +73,14 @@ class Searchable extends Annotation
*/
class Field extends Annotation
{
- /**
- * @var float
- */
+ /** @var float */
public $boost;
+
+ /** @var string */
+ public $type;
+
+ /** @var string */
+ public $name;
}
/**
@@ -91,9 +94,19 @@ class Field extends Annotation
/**
* @Annotation
- * @Target("PROPERTY")
+ * @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*/
final class ElasticField extends Field
{
- /* configuration */
-}
+ /** @var boolean */
+ public $includeInAll;
+
+ /** @var string */
+ public $index;
+
+ /** @var array */
+ public $fields;
+
+ /** @var array */
+ public $properties;
+}
@@ -60,7 +60,7 @@ class ClassMetadata implements ClassMetadataInterface
/**
* @var int
*/
- public $numberOfReplicas = 1;
+ public $numberOfReplicas = 0;
/**
* @var int
@@ -81,7 +81,17 @@ class ClassMetadata implements ClassMetadataInterface
* @var int
*/
public $value = 1;
+
+ /**
+ * @var boolean
+ */
+ public $source = true;
+ /**
+ * @var float
+ */
+ public $boost = 1.0;
+
/**
* @var string
*/
@@ -130,6 +140,7 @@ public function __sleep()
{
// This metadata is always serialized/cached.
return array(
+ 'boost',
'className',
'fieldMappings',
'index',
@@ -226,7 +237,7 @@ public function hasField($fieldName)
* @param \ReflectionProperty $field
* @param array $mapping
*/
- public function addFieldMapping(\ReflectionProperty $field, $mapping = array())
+ public function addFieldMapping(\Reflector $field, $mapping = array())
{
$fieldName = $field->getName();
$this->fieldMappings[$fieldName] = $mapping;
@@ -238,7 +249,7 @@ public function addFieldMapping(\ReflectionProperty $field, $mapping = array())
/*public function addField(\ReflectionProperty $field)
{
$fieldName = $field->getName();
- $this->reflFields[$fieldName] = $field;
+ $this->reflFields[] = $field;
}*/
@@ -69,10 +69,11 @@ public function loadMetadataForClass($className, ClassMetadata $metadata)
}
$reflProperties = $reflClass->getProperties();
+ $reflMethods = $reflClass->getMethods();
$this->extractClassAnnotations($reflClass, $metadata);
$this->extractPropertiesAnnotations($reflProperties, $metadata);
-
+ $this->extractMethodsAnnotations($reflMethods, $metadata);
}
@@ -138,6 +139,31 @@ private function extractPropertiesAnnotations(array $reflProperties, ClassMetada
return $metadata;
}
+ /**
+ * Extract the methods annotations.
+ *
+ * @param \ReflectionMethod[] $reflMethods
+ * @param ClassMetadata $metadata
+ *
+ * @return ClassMetadata
+ */
+ private function extractMethodsAnnotations(array $reflMethods, ClassMetadata $metadata)
+ {
+ $documentsFieldAnnotations = array();
+ foreach ($reflMethods as $reflMethod) {
+ foreach ($this->reader->getMethodAnnotations($reflMethod) as $annotation) {
+ foreach (self::$documentFieldAnnotationClasses as $fieldAnnotationClass) {
+ if ($annotation instanceof $fieldAnnotationClass) {
+ $metadata->addFieldMapping($reflMethod, $annotation);
+ continue 2;
+ }
+ }
+ }
+ }
+
+ return $metadata;
+ }
+
/**
* @param \ReflectionProperty[] $reflectedClassProperties
* @param ClassMetadata $metadata
@@ -155,7 +181,7 @@ private function addValuesToMetadata(array $reflectedClassProperties, ClassMetad
if (false === property_exists($metadata, $propertyName)) {
throw new DriverException\PropertyDoesNotExistsInMetadataException($reflectedProperty->getName());
} else {
- $metadata->$propertyName = $class->$propertyName;
+ if(!is_null($class->$propertyName)) $metadata->$propertyName = $class->$propertyName;
}
}
Oops, something went wrong.

0 comments on commit 6d1195f

Please sign in to comment.