rebase upstream

This commit is contained in:
NicolasBadey 2012-12-05 17:28:34 +01:00
commit ab84d89eb7
17 changed files with 341 additions and 24 deletions

View file

@ -91,7 +91,7 @@ class PopulateCommand extends ContainerAwareCommand
private function populateIndex(OutputInterface $output, $index, $reset)
{
if ($reset) {
$output->writeln(sprintf('Resetting: %s', $index));
$output->writeln(sprintf('<info>Resetting</info> <comment>%s</comment>', $index));
$this->resetter->resetIndex($index);
}
@ -99,13 +99,13 @@ class PopulateCommand extends ContainerAwareCommand
foreach ($providers as $type => $provider) {
$loggerClosure = function($message) use ($output, $index, $type) {
$output->writeln(sprintf('Populating: %s/%s, %s', $index, $type, $message));
$output->writeln(sprintf('<info>Populating</info> %s/%s, %s', $index, $type, $message));
};
$provider->populate($loggerClosure);
}
$output->writeln(sprintf('Refreshing: %s', $index));
$output->writeln(sprintf('<info>Refreshing</info> <comment>%s</comment>', $index));
$this->indexManager->getIndex($index)->refresh();
}

View file

@ -45,9 +45,41 @@ class Configuration
->useAttributeAsKey('id')
->prototype('array')
->performNoDeepMerging()
->beforeNormalization()
->ifTrue(function($v) { return isset($v['host']) && isset($v['port']); })
->then(function($v) {
return array(
'servers' => array(
array(
'host' => $v['host'],
'port' => $v['port'],
)
)
);
})
->end()
->beforeNormalization()
->ifTrue(function($v) { return isset($v['url']); })
->then(function($v) {
return array(
'servers' => array(
array(
'url' => $v['url'],
)
)
);
})
->end()
->children()
->scalarNode('host')->defaultValue('localhost')->end()
->scalarNode('port')->defaultValue('9000')->end()
->arrayNode('servers')
->prototype('array')
->children()
->scalarNode('url')->end()
->scalarNode('host')->end()
->scalarNode('port')->end()
->end()
->end()
->end()
->scalarNode('timeout')->end()
->scalarNode('headers')->end()
->end()
@ -214,6 +246,7 @@ class Configuration
->end()
->append($this->getMappingsNode())
->append($this->getSourceNode())
->append($this->getBoostNode())
->end()
;
@ -265,6 +298,33 @@ class Configuration
->end()
->end()
->end()
->arrayNode('_parent')
->treatNullLike(array())
->children()
->scalarNode('type')->end()
->scalarNode('identifier')->defaultValue('id')->end()
->end()
->end()
->arrayNode('properties')
->useAttributeAsKey('name')
->prototype('array')
->treatNullLike(array())
->addDefaultsIfNotSet()
->children()
->scalarNode('type')->defaultValue('string')->end()
->scalarNode('boost')->end()
->scalarNode('store')->end()
->scalarNode('index')->end()
->scalarNode('index_analyzer')->end()
->scalarNode('search_analyzer')->end()
->scalarNode('analyzer')->end()
->scalarNode('term_vector')->end()
->scalarNode('null_value')->end()
->booleanNode('include_in_all')->defaultValue('true')->end()
->scalarNode('lat_lon')->end()
->end()
->end()
->end()
->end()
->end()
;
@ -295,4 +355,22 @@ class Configuration
return $node;
}
/**
* Returns the array node used for "_boost".
*/
protected function getBoostNode()
{
$builder = new TreeBuilder();
$node = $builder->root('_boost');
$node
->children()
->scalarNode('name')->end()
->scalarNode('null_value')->end()
->end()
;
return $node;
}
}

View file

@ -170,6 +170,9 @@ class FOQElasticaExtension extends Extension
if (isset($type['_source'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_source'] = $type['_source'];
}
if (isset($type['_boost'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['_boost'] = $type['_boost'];
}
if (isset($type['mappings'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['properties'] = $type['mappings'];
$typeName = sprintf('%s/%s', $indexName, $name);
@ -184,6 +187,9 @@ class FOQElasticaExtension extends Extension
if (isset($type['search_analyzer'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['search_analyzer'] = $type['search_analyzer'];
}
if (isset($type['index'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['index'] = $type['index'];
}
}
}
@ -317,7 +323,7 @@ class FOQElasticaExtension extends Extension
$listenerDef->replaceArgument(2, $this->getDoctrineEvents($typeConfig));
switch ($typeConfig['driver']) {
case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break;
case 'mongodb': $listenerDef->addTag('doctrine.odm.mongodb.event_subscriber'); break;
case 'mongodb': $listenerDef->addTag('doctrine_mongodb.odm.event_subscriber'); break;
}
if (isset($typeConfig['listener']['is_indexable_callback'])) {
$callback = $typeConfig['listener']['is_indexable_callback'];

View file

@ -111,6 +111,14 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
return $result;
}
/**
* {@inheritdoc}
*/
public function getIdentifierField()
{
return $this->options['identifier'];
}
/**
* Fetches objects by theses identifier values
*

View file

@ -40,7 +40,6 @@ abstract class AbstractProvider extends BaseAbstractProvider
if ($loggerClosure) {
$stepStartTime = microtime(true);
}
$objects = $this->fetchSlice($queryBuilder, $this->options['batch_size'], $offset);
$this->objectPersister->insertMany($objects);

View file

@ -65,6 +65,6 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
*/
public function getTotalHits()
{
return $this->searchable->count($this->query);
return $this->searchable->search($this->query)->getTotalHits();
}
}

View file

@ -100,6 +100,14 @@ class ElasticaToModelTransformer implements ElasticaToModelTransformerInterface
return $this->objectClass;
}
/**
* {@inheritdoc}
*/
public function getIdentifierField()
{
return $this->options['identifier'];
}
/**
* Fetch objects for theses identifier values
*

View file

@ -139,6 +139,41 @@ Elasticsearch type is comparable to Doctrine entity repository.
Our type is now available as a service: `foq_elastica.index.website.user`. It is an instance of `Elastica_Type`.
### Declaring parent field
foq_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
types:
comment:
mappings:
post: {_parent: { type: "post", identifier: "id" } }
date: { boost: 5 }
content: ~
### Declaring `nested` or `object`
foq_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
website:
client: default
types:
post:
mappings:
date: { boost: 5 }
title: { boost: 3 }
content: ~
comments:
type: "nested"
properties:
date: { boost: 5 }
content: ~
### Populate the types
php app/console foq:elastica:populate

View file

@ -58,7 +58,27 @@ class Resetter
$type = $indexConfig['index']->getType($typeName);
$type->delete();
$type->setMapping($indexConfig['config']['mappings'][$typeName]['properties']);
$mapping = $this->createMapping($indexConfig['config']['mappings'][$typeName]);
$type->setMapping($mapping);
}
/**
* create type mapping object
*
* @param array $indexConfig
* @return Elastica_Type_Mapping
*/
protected function createMapping($indexConfig)
{
$mapping = \Elastica_Type_Mapping::create($indexConfig['properties']);
foreach($indexConfig['properties'] as $field => $type) {
if (!empty($type['_parent']) && $type['_parent'] !== '~') {
$mapping->setParam('_parent', array('type' => $type['_parent']['type']));
}
}
return $mapping;
}
/**

View file

@ -61,7 +61,6 @@
<service id="foq_elastica.elastica_to_model_transformer.collection.prototype" class="%foq_elastica.elastica_to_model_transformer.collection.class%" public="true" abstract="true">
<argument type="collection" /> <!-- transformers -->
<argument type="collection" /> <!-- options -->
</service>
<service id="foq_elastica.provider_registry" class="%foq_elastica.provider_registry.class%">

View file

@ -29,6 +29,17 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
),
),
),
'parent' => array(
'index' => $this->getMockElasticaIndex(),
'config' => array(
'mappings' => array(
'a' => array('properties' => array(
'field_1' => array('_parent' => array('type' => 'b', 'identifier' => 'id')),
'field_2' => array())),
'b' => array('properties' => array()),
),
),
),
);
}
@ -80,9 +91,10 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
$type->expects($this->once())
->method('delete');
$mapping = \Elastica_Type_Mapping::create($this->indexConfigsByName['foo']['config']['mappings']['a']['properties']);
$type->expects($this->once())
->method('setMapping')
->with($this->indexConfigsByName['foo']['config']['mappings']['a']['properties']);
->with($mapping);
$resetter = new Resetter($this->indexConfigsByName);
$resetter->resetIndexType('foo', 'a');
@ -106,6 +118,28 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
$resetter->resetIndexType('foo', 'c');
}
public function testIndexMappingForParent()
{
$type = $this->getMockElasticaType();
$this->indexConfigsByName['parent']['index']->expects($this->once())
->method('getType')
->with('a')
->will($this->returnValue($type));
$type->expects($this->once())
->method('delete');
$mapping = \Elastica_Type_Mapping::create($this->indexConfigsByName['parent']['config']['mappings']['a']['properties']);
$mapping->setParam('_parent', array('type' => 'b'));
$type->expects($this->once())
->method('setMapping')
->with($mapping);
$resetter = new Resetter($this->indexConfigsByName);
$resetter->resetIndexType('parent', 'a');
}
/**
* @return Elastica_Index
*/

View file

@ -19,11 +19,19 @@ class ElasticaToModelTransformerCollectionTest extends \PHPUnit_Framework_TestCa
->method('getObjectClass')
->will($this->returnValue('FOQ\ElasticaBundle\Tests\Transformer\POPO'));
$transformer1->expects($this->any())
->method('getIdentifierField')
->will($this->returnValue('id'));
$transformer2 = $this->getMock('FOQ\ElasticaBundle\Transformer\ElasticaToModelTransformerInterface');
$transformer2->expects($this->any())
->method('getObjectClass')
->will($this->returnValue('FOQ\ElasticaBundle\Tests\Transformer\POPO2'));
$transformer2->expects($this->any())
->method('getIdentifierField')
->will($this->returnValue('id'));
$this->collection = new ElasticaToModelTransformerCollection($this->transformers = array(
'type1' => $transformer1,
'type2' => $transformer2,

View file

@ -92,6 +92,19 @@ class POPO
{
return $this->file;
}
public function getSub()
{
return array(
(object) array('foo' => 'foo', 'bar' => 'foo', 'id' => 1),
(object) array('foo' => 'bar', 'bar' => 'bar', 'id' => 2),
);
}
public function getUpper()
{
return (object) array('id' => 'parent', 'name' => 'a random name');
}
}
class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase
@ -215,4 +228,66 @@ class ModelToElasticaAutoTransformerTest extends \PHPUnit_Framework_TestCase
base64_encode(file_get_contents(__DIR__ . '/../fixtures/attachment.odt')), $data['fileContents']
);
}
public function testNestedMapping()
{
$transformer = new ModelToElasticaAutoTransformer();
$document = $transformer->transform(new POPO(), array(
'sub' => array(
'type' => 'nested',
'properties' => array('foo' => '~')
)
));
$data = $document->getData();
$this->assertTrue(array_key_exists('sub', $data));
$this->assertInternalType('array', $data['sub']);
$this->assertEquals(array(
array('foo' => 'foo'),
array('foo' => 'bar')
), $data['sub']);
}
public function tesObjectMapping()
{
$transformer = new ModelToElasticaAutoTransformer();
$document = $transformer->transform(new POPO(), array(
'sub' => array(
'type' => 'object',
'properties' => array('bar')
)
));
$data = $document->getData();
$this->assertTrue(array_key_exists('sub', $data));
$this->assertInternalType('array', $data['sub']);
$this->assertEquals(array(
array('bar' => 'foo'),
array('bar' => 'bar')
), $data['sub']);
}
public function testParentMapping()
{
$transformer = new ModelToElasticaAutoTransformer();
$document = $transformer->transform(new POPO(), array(
'upper' => array(
'_parent' => array('type' => 'upper', 'identifier' => 'id'),
)
));
$this->assertEquals("parent", $document->getParent());
}
public function testParentMappingWithCustomIdentifier()
{
$transformer = new ModelToElasticaAutoTransformer();
$document = $transformer->transform(new POPO(), array(
'upper' => array(
'_parent' => array('type' => 'upper', 'identifier' => 'name'),
)
));
$this->assertEquals("a random name", $document->getParent());
}
}

View file

@ -13,14 +13,10 @@ use Symfony\Component\Form\Util\PropertyPath;
class ElasticaToModelTransformerCollection implements ElasticaToModelTransformerInterface
{
protected $transformers = array();
protected $options = array(
'identifier' => 'id'
);
public function __construct(array $transformers, array $options)
public function __construct(array $transformers)
{
$this->transformers = $transformers;
$this->options = array_merge($this->options, $options);
}
public function getObjectClass()
@ -30,6 +26,16 @@ class ElasticaToModelTransformerCollection implements ElasticaToModelTransformer
}, $this->transformers);
}
/**
* {@inheritdoc}
*/
public function getIdentifierField()
{
return array_map(function ($transformer) {
return $transformer->getIdentifierField();
}, $this->transformers);
}
public function transform(array $elasticaObjects)
{
$sorted = array();
@ -37,12 +43,19 @@ class ElasticaToModelTransformerCollection implements ElasticaToModelTransformer
$sorted[$object->getType()][] = $object;
}
$identifierProperty = new PropertyPath($this->options['identifier']);
$transformed = array();
foreach ($sorted AS $type => $objects) {
$transformedObjects = $this->transformers[$type]->transform($objects);
$transformed[$type] = array_combine(array_map(function($o) use ($identifierProperty) {return $identifierProperty->getValue($o);},$transformedObjects),$transformedObjects);
$identifierGetter = 'get' . ucfirst($this->transformers[$type]->getIdentifierField());
$transformed[$type] = array_combine(
array_map(
function($o) use ($identifierGetter) {
return $o->$identifierGetter();
},
$transformedObjects
),
$transformedObjects
);
}
$result = array();
@ -71,4 +84,4 @@ class ElasticaToModelTransformerCollection implements ElasticaToModelTransformer
return $transformer->getObjectClass();
}, $this->transformers);
}
}
}

View file

@ -24,4 +24,11 @@ interface ElasticaToModelTransformerInterface
* @return string
*/
function getObjectClass();
/**
* Returns the identifier field from the options
*
* @return string the identifier field
*/
function getIdentifierField();
}

View file

@ -45,7 +45,15 @@ class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterf
$document = new \Elastica_Document($identifier);
foreach ($fields as $key => $mapping) {
$property = new PropertyPath($key);
if (isset($mapping['type']) && $mapping['type'] == 'attachment') {
if (!empty($mapping['_parent']) && $mapping['_parent'] !== '~') {
$parent = $property->getValue($object);
$identifierProperty = new PropertyPath($mapping['_parent']['identifier']);
$document->setParent($identifierProperty->getValue($parent));
} else if (isset($mapping['type']) && in_array($mapping['type'], array('nested', 'object'))) {
$submapping = $mapping['properties'];
$subcollection = $property->getValue($object);
$document->add($key, $this->transformNested($subcollection, $submapping, $document));
} else if (isset($mapping['type']) && $mapping['type'] == 'attachment') {
$attachment = $property->getValue($object);
if ($attachment instanceof \SplFileInfo) {
$document->addFile($key, $attachment->getPathName());
@ -59,6 +67,25 @@ class ModelToElasticaAutoTransformer implements ModelToElasticaTransformerInterf
return $document;
}
/**
* transform a nested document or an object property into an array of ElasticaDocument
*
* @param array $objects the object to convert
* @param array $fields the keys we want to have in the returned array
* @param Elastica_Document $parent the parent document
* @return array
*/
protected function transformNested($objects, array $fields, $parent)
{
$documents = array();
foreach($objects as $object) {
$document = $this->transform($object, $fields);
$documents[] = $document->getData();
}
return $documents;
}
/**
* Attempts to convert any type to a string or an array of strings
*

View file

@ -12,9 +12,9 @@
],
"require": {
"php": ">=5.3.2",
"symfony/framework-bundle": "2.1.*",
"symfony/console": "2.1.*",
"symfony/form": "2.1.*",
"symfony/framework-bundle": ">=2.1.0,<2.3-dev",
"symfony/console": ">=2.1.0,<2.3-dev",
"symfony/form": ">=2.1.0,<2.3-dev",
"ruflin/elastica": "0.19.8"
},
"require-dev":{