rebase upstream
This commit is contained in:
commit
ab84d89eb7
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'];
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -65,6 +65,6 @@ class RawPaginatorAdapter implements PaginatorAdapterInterface
|
|||
*/
|
||||
public function getTotalHits()
|
||||
{
|
||||
return $this->searchable->count($this->query);
|
||||
return $this->searchable->search($this->query)->getTotalHits();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
35
README.md
35
README.md
|
@ -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
|
||||
|
|
22
Resetter.php
22
Resetter.php
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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%">
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,4 +24,11 @@ interface ElasticaToModelTransformerInterface
|
|||
* @return string
|
||||
*/
|
||||
function getObjectClass();
|
||||
|
||||
/**
|
||||
* Returns the identifier field from the options
|
||||
*
|
||||
* @return string the identifier field
|
||||
*/
|
||||
function getIdentifierField();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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":{
|
||||
|
|
Loading…
Reference in a new issue