├── Resources
├── doc
│ ├── overwriting_bundle.md
│ ├── configuration.md
│ ├── index.md
│ ├── scan.md
│ ├── meta_fields.md
│ ├── find_functions.md
│ ├── crud.md
│ └── results_parsing.md
├── public
│ └── images
│ │ ├── blue_picto_less.gif
│ │ └── blue_picto_more.gif
└── config
│ └── services.yml
├── Tests
├── app
│ ├── fixture
│ │ ├── data
│ │ │ ├── command_import_10.json.gz
│ │ │ ├── command_import_11.json.gz
│ │ │ ├── command_import_9.json.gz
│ │ │ ├── command_import_9.json
│ │ │ ├── command_import_10.json
│ │ │ ├── command_import_11.json
│ │ │ └── command_import_20.json
│ │ └── TestBundle
│ │ │ ├── TestBundle.php
│ │ │ ├── Document
│ │ │ ├── SubcategoryObject.php
│ │ │ ├── LongDescriptionTrait.php
│ │ │ ├── CategoryObject.php
│ │ │ └── User.php
│ │ │ └── Entity
│ │ │ ├── CategoryObject.php
│ │ │ └── Product.php
│ ├── config
│ │ └── config_test.yml
│ └── AppKernel.php
├── WebTestCase.php
├── Unit
│ ├── Result
│ │ ├── DummyIterator.php
│ │ ├── DocumentIteratorTest.php
│ │ ├── RawIteratorTest.php
│ │ ├── AbstractResultsIteratorTest.php
│ │ └── ConverterTest.php
│ ├── Event
│ │ ├── CommitEventTest.php
│ │ ├── BulkEventTest.php
│ │ └── PrePersistEventTest.php
│ ├── Annotation
│ │ └── PropertyTest.php
│ ├── Profiler
│ │ └── ElasticsearchProfilerTest.php
│ ├── Mapping
│ │ ├── DocumentParserTest.php
│ │ ├── MetadataCollectorTest.php
│ │ └── DocumentFinderTest.php
│ ├── Collection
│ │ └── CollectionTest.php
│ ├── EventListener
│ │ └── TerminateListenerTest.php
│ ├── Service
│ │ ├── ManagerFactoryTest.php
│ │ ├── RepositoryTest.php
│ │ ├── GenerateServiceTest.php
│ │ └── Json
│ │ │ └── JsonWriterTest.php
│ └── ElasticsearchBundleTest.php
└── Functional
│ ├── Annotation
│ ├── PropertyTest.php
│ └── DocumentTest.php
│ ├── Command
│ ├── GenerateDocumentCommandTest.php
│ ├── DropIndexCommandTest.php
│ ├── CacheClearCommandTest.php
│ └── IndexImportCommandTest.php
│ ├── Result
│ ├── DocumentWithNullObjectFieldTest.php
│ ├── GetDocumentSortTest.php
│ ├── DocumentWithMultipleFieldsTest.php
│ ├── AggregationIteratorFindTest.php
│ ├── ArrayIteratorTest.php
│ ├── HashMapObjectIteratorTest.php
│ └── DocumentIteratorTest.php
│ ├── DependencyInjection
│ └── ElasticsearchExtensionTest.php
│ └── Mapping
│ ├── DocumentFinderTest.php
│ └── MetadataCollectorTest.php
├── .gitignore
├── .github
└── ISSUE_TEMPLATE.md
├── Exception
├── DocumentParserException.php
├── MissingDocumentAnnotationException.php
└── BulkWithErrorsException.php
├── Annotation
├── Object.php
├── MetaField.php
├── Id.php
├── Version.php
├── Document.php
├── Routing.php
├── Nested.php
├── ParentDocument.php
├── ObjectType.php
├── HashMap.php
├── Property.php
└── Embedded.php
├── Collection
└── Collection.php
├── Result
├── RawIterator.php
├── DocumentIterator.php
├── ArrayIterator.php
├── ObjectIterator.php
└── Aggregation
│ └── AggregationValue.php
├── Mapping
├── DumperInterface.php
└── Caser.php
├── DependencyInjection
├── Compiler
│ ├── AnnotationReaderPass.php
│ ├── ManagerPass.php
│ ├── MappingPass.php
│ └── RepositoryPass.php
└── ONGRElasticsearchExtension.php
├── Profiler
├── CollectTrait.php
└── Handler
│ └── CollectionHandler.php
├── Event
├── PrePersistEvent.php
├── BaseEvent.php
├── PostCreateManagerEvent.php
├── Events.php
├── CommitEvent.php
├── PreCreateManagerEvent.php
└── BulkEvent.php
├── LICENSE
├── README.md
├── Service
├── IndexSuffixFinder.php
├── GenerateService.php
├── ImportService.php
├── Json
│ └── JsonWriter.php
└── ExportService.php
├── ONGRElasticsearchBundle.php
├── phpunit.xml.dist
├── Command
├── CacheClearCommand.php
├── IndexDropCommand.php
├── AbstractManagerAwareCommand.php
├── IndexImportCommand.php
└── IndexExportCommand.php
├── EventListener
└── TerminateListener.php
└── composer.json
/Resources/doc/overwriting_bundle.md:
--------------------------------------------------------------------------------
1 | # Overwriting bundle parts
2 |
3 | Documentation in progress...
4 |
--------------------------------------------------------------------------------
/Resources/public/images/blue_picto_less.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/handcraftedinthealps/ElasticsearchBundle/HEAD/Resources/public/images/blue_picto_less.gif
--------------------------------------------------------------------------------
/Resources/public/images/blue_picto_more.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/handcraftedinthealps/ElasticsearchBundle/HEAD/Resources/public/images/blue_picto_more.gif
--------------------------------------------------------------------------------
/Tests/app/fixture/data/command_import_10.json.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/handcraftedinthealps/ElasticsearchBundle/HEAD/Tests/app/fixture/data/command_import_10.json.gz
--------------------------------------------------------------------------------
/Tests/app/fixture/data/command_import_11.json.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/handcraftedinthealps/ElasticsearchBundle/HEAD/Tests/app/fixture/data/command_import_11.json.gz
--------------------------------------------------------------------------------
/Tests/app/fixture/data/command_import_9.json.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/handcraftedinthealps/ElasticsearchBundle/HEAD/Tests/app/fixture/data/command_import_9.json.gz
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | /Tests/app/cache/
3 | /Tests/app/logs/
4 | /Tests/app/build/
5 | /phpunit.xml
6 | /composer.lock
7 | /var/cache/
8 | .phpunit.result.cache
9 | /.idea/
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Tests/WebTestCase.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle;
13 |
14 | use Symfony\Component\HttpKernel\Bundle\Bundle;
15 |
16 | /**
17 | * AcmeTestBundle for testing.
18 | */
19 | class TestBundle extends Bundle
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/Exception/DocumentParserException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Exception;
13 |
14 | /**
15 | * This is the exception which should be thrown when document has invalid or incomplete annotations.
16 | */
17 | class DocumentParserException extends \Exception
18 | {
19 | }
20 |
--------------------------------------------------------------------------------
/Annotation/Object.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | if (version_compare(PHP_VERSION, '7.2.0') < 0) {
15 | class_alias('ONGR\ElasticsearchBundle\Annotation\ObjectType', 'ONGR\ElasticsearchBundle\Annotation\Object', false);
16 | class_exists('ONGR\ElasticsearchBundle\Annotation\ObjectType');
17 | }
18 |
--------------------------------------------------------------------------------
/Exception/MissingDocumentAnnotationException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Exception;
13 |
14 | /**
15 | * This is the exception which should be thrown when class does not have @ONGR\ElasticsearchBundle\Annotation\Document
16 | * annotation.
17 | */
18 | class MissingDocumentAnnotationException extends DocumentParserException
19 | {
20 | }
21 |
--------------------------------------------------------------------------------
/Collection/Collection.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Collection;
13 |
14 | use Doctrine\Common\Collections\ArrayCollection;
15 |
16 | /**
17 | * This class is a holder for collection of objects.
18 | *
19 | * @deprecated Use Doctrine\Common\Collections\ArrayCollection instead.
20 | */
21 | class Collection extends ArrayCollection
22 | {
23 | }
24 |
--------------------------------------------------------------------------------
/Result/RawIterator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Result;
13 |
14 | /**
15 | * Raw documents iterator.
16 | */
17 | class RawIterator extends AbstractResultsIterator
18 | {
19 | /**
20 | * {@inheritdoc}
21 | */
22 | protected function convertDocument(array $document)
23 | {
24 | return $document;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Tests/app/fixture/TestBundle/Document/SubcategoryObject.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document;
13 |
14 | use ONGR\ElasticsearchBundle\Annotation as ES;
15 |
16 | /**
17 | * Subcategory object for testing.
18 | *
19 | * @ES\ObjectType
20 | */
21 | class SubcategoryObject extends CategoryObject
22 | {
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/Tests/app/fixture/TestBundle/Entity/CategoryObject.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Entity;
13 |
14 | use ONGR\ElasticsearchBundle\Annotation as ES;
15 |
16 | /**
17 | * @ES\ObjectType
18 | */
19 | class CategoryObject
20 | {
21 | /**
22 | * @var string
23 | * @ES\Property(type="keyword")
24 | */
25 | public $title;
26 | }
27 |
--------------------------------------------------------------------------------
/Tests/Unit/Result/DummyIterator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Result;
13 |
14 | use ONGR\ElasticsearchBundle\Result\AbstractResultsIterator;
15 |
16 | class DummyIterator extends AbstractResultsIterator
17 | {
18 | /**
19 | * {@inheritdoc}
20 | */
21 | protected function convertDocument(array $document)
22 | {
23 | return $document;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Tests/app/fixture/data/command_import_9.json:
--------------------------------------------------------------------------------
1 | [
2 | {"count":9},
3 | {"_type":"product","_id":"doc1","_source":{"title":"Document 1"}},
4 | {"_type":"product","_id":"doc2","_source":{"title":"Document 2"}},
5 | {"_type":"product","_id":"doc3","_source":{"title":"Document 3"}},
6 | {"_type":"product","_id":"doc4","_source":{"title":"Document 4"}},
7 | {"_type":"product","_id":"doc5","_source":{"title":"Document 5"}},
8 | {"_type":"product","_id":"doc6","_source":{"title":"Document 6"}},
9 | {"_type":"product","_id":"doc7","_source":{"title":"Document 7"}},
10 | {"_type":"product","_id":"doc8","_source":{"title":"Document 8"}},
11 | {"_type":"product","_id":"doc9","_source":{"title":"Document 9"}}
12 | ]
13 |
--------------------------------------------------------------------------------
/Mapping/DumperInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Mapping;
13 |
14 | /**
15 | * DumperInterface is the interface implemented by elasticsearch document annotations.
16 | */
17 | interface DumperInterface
18 | {
19 | /**
20 | * Dumps properties into array.
21 | *
22 | * @param array $exclude Properties array to exclude from dump.
23 | *
24 | * @return array
25 | */
26 | public function dump(array $exclude = []);
27 | }
28 |
--------------------------------------------------------------------------------
/Annotation/MetaField.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | /**
15 | * All meta-field annotations must implement this interface.
16 | */
17 | interface MetaField
18 | {
19 | /**
20 | * Returns meta-field name.
21 | *
22 | * @return string
23 | */
24 | public function getName();
25 |
26 | /**
27 | * Returns meta-field settings.
28 | *
29 | * @return array
30 | */
31 | public function getSettings();
32 | }
33 |
--------------------------------------------------------------------------------
/Tests/Unit/Event/CommitEventTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Event;
13 |
14 | use ONGR\ElasticsearchBundle\Event\CommitEvent;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class CommitEventTest extends TestCase
18 | {
19 | public function testGetters()
20 | {
21 | $event = new CommitEvent('flush', []);
22 |
23 | $this->assertEquals('flush', $event->getCommitMode());
24 | $this->assertEquals([], $event->getBulkParams());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Tests/app/fixture/data/command_import_10.json:
--------------------------------------------------------------------------------
1 | [
2 | {"count":10},
3 | {"_type":"product","_id":"doc1","_source":{"title":"Document 1"}},
4 | {"_type":"product","_id":"doc2","_source":{"title":"Document 2"}},
5 | {"_type":"product","_id":"doc3","_source":{"title":"Document 3"}},
6 | {"_type":"product","_id":"doc4","_source":{"title":"Document 4"}},
7 | {"_type":"product","_id":"doc5","_source":{"title":"Document 5"}},
8 | {"_type":"product","_id":"doc6","_source":{"title":"Document 6"}},
9 | {"_type":"product","_id":"doc7","_source":{"title":"Document 7"}},
10 | {"_type":"product","_id":"doc8","_source":{"title":"Document 8"}},
11 | {"_type":"product","_id":"doc9","_source":{"title":"Document 9"}},
12 | {"_type":"product","_id":"doc10","_source":{"title":"Document 10"}}
13 | ]
14 |
--------------------------------------------------------------------------------
/Tests/Unit/Event/BulkEventTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Event;
13 |
14 | use ONGR\ElasticsearchBundle\Event\BulkEvent;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class BulkEventTest extends TestCase
18 | {
19 | public function testGetters()
20 | {
21 | $event = new BulkEvent('index', 'test_type', []);
22 |
23 | $this->assertEquals('test_type', $event->getType());
24 | $this->assertEquals('test_type', $event->getType());
25 | $this->assertEquals([], $event->getQuery());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Tests/Unit/Event/PrePersistEventTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Event;
13 |
14 | use ONGR\ElasticsearchBundle\Event\PrePersistEvent;
15 | use ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | class PrePersistEventTest extends TestCase
19 | {
20 | public function testGetters()
21 | {
22 | $entity = new Product();
23 | $event = new PrePersistEvent($entity);
24 |
25 | $this->assertInstanceOf(Product::class, $event->getDocument());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Annotation/Id.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | /**
15 | * Annotation to associate document property with _id meta-field.
16 | *
17 | * @Annotation
18 | * @Target("PROPERTY")
19 | */
20 | final class Id implements MetaField
21 | {
22 | const NAME = '_id';
23 |
24 | /**
25 | * {@inheritdoc}
26 | */
27 | public function getName()
28 | {
29 | return self::NAME;
30 | }
31 |
32 | /**
33 | * {@inheritdoc}
34 | */
35 | public function getSettings()
36 | {
37 | return [];
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Annotation/Version.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | /**
15 | * Associates document property with _version meta-field.
16 | *
17 | * @Annotation
18 | * @Target("PROPERTY")
19 | */
20 | final class Version implements MetaField
21 | {
22 | const NAME = '_version';
23 |
24 | /**
25 | * {@inheritdoc}
26 | */
27 | public function getName()
28 | {
29 | return self::NAME;
30 | }
31 |
32 | /**
33 | * {@inheritdoc}
34 | */
35 | public function getSettings()
36 | {
37 | return [];
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Tests/app/fixture/data/command_import_11.json:
--------------------------------------------------------------------------------
1 | [
2 | {"count":11},
3 | {"_type":"product","_id":"doc1","_source":{"title":"Document 1"}},
4 | {"_type":"product","_id":"doc2","_source":{"title":"Document 2"}},
5 | {"_type":"product","_id":"doc3","_source":{"title":"Document 3"}},
6 | {"_type":"product","_id":"doc4","_source":{"title":"Document 4"}},
7 | {"_type":"product","_id":"doc5","_source":{"title":"Document 5"}},
8 | {"_type":"product","_id":"doc6","_source":{"title":"Document 6"}},
9 | {"_type":"product","_id":"doc7","_source":{"title":"Document 7"}},
10 | {"_type":"product","_id":"doc8","_source":{"title":"Document 8"}},
11 | {"_type":"product","_id":"doc9","_source":{"title":"Document 9"}},
12 | {"_type":"product","_id":"doc10","_source":{"title":"Document 10"}},
13 | {"_type":"product","_id":"doc11","_source":{"title":"Document 11"}}
14 | ]
--------------------------------------------------------------------------------
/DependencyInjection/Compiler/AnnotationReaderPass.php:
--------------------------------------------------------------------------------
1 | hasDefinition('es.annotations.reader')) {
18 | return;
19 | }
20 |
21 | $definition = new Definition(AnnotationReader::class);
22 | $definition->addMethodCall('addGlobalIgnoredName', ['required']);
23 | $container->setDefinition('es.annotations.reader', $definition);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Exception/BulkWithErrorsException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Exception;
13 |
14 | class BulkWithErrorsException extends \Exception
15 | {
16 | /**
17 | * @var array
18 | */
19 | protected $response;
20 |
21 | /**
22 | * {@inheritdoc}
23 | */
24 | public function __construct($message = '', $code = 0, \Exception $previous = null, $response = [])
25 | {
26 | parent::__construct($message, $code, $previous);
27 | $this->response = $response;
28 | }
29 |
30 | /**
31 | * @return array
32 | */
33 | public function getResponse()
34 | {
35 | return $this->response;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Annotation/Document.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | use ONGR\ElasticsearchBundle\Mapping\DumperInterface;
15 |
16 | /**
17 | * Annotation to mark a class as an Elasticsearch document.
18 | *
19 | * @Annotation
20 | * @Target("CLASS")
21 | */
22 | final class Document implements DumperInterface
23 | {
24 | /**
25 | * @var string
26 | */
27 | public $type;
28 |
29 | /**
30 | * @var array
31 | */
32 | public $options = [];
33 |
34 | /**
35 | * {@inheritdoc}
36 | */
37 | public function dump(array $exclude = [])
38 | {
39 | return array_diff_key(
40 | $this->options,
41 | $exclude
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Tests/Unit/Result/DocumentIteratorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Result;
13 |
14 | use ONGR\ElasticsearchBundle\Result\DocumentIterator;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class DocumentIteratorTest extends TestCase
18 | {
19 | /**
20 | * Test for getAggregation() in case requested aggregation is not set.
21 | */
22 | public function testGetAggregationNull()
23 | {
24 | $manager = $this->getMockBuilder('ONGR\ElasticsearchBundle\Service\Manager')
25 | ->disableOriginalConstructor()
26 | ->getMock();
27 |
28 | $iterator = new DocumentIterator([], $manager);
29 |
30 | $this->assertNull($iterator->getAggregation('foo'));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Profiler/CollectTrait.php:
--------------------------------------------------------------------------------
1 | doCollect($request, $response, $exception);
20 | }
21 | }
22 | } else {
23 | /**
24 | * @internal
25 | */
26 | trait CollectTrait
27 | {
28 | public function collect(Request $request, Response $response, \Throwable $exception = null)
29 | {
30 | return $this->doCollect($request, $response, $exception);
31 | }
32 | }
33 | }
34 | // @codingStandardsIgnoreEnd
35 |
--------------------------------------------------------------------------------
/Event/PrePersistEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Event;
13 |
14 | class PrePersistEvent extends BaseEvent
15 | {
16 | /**
17 | * @var object
18 | */
19 | private $document;
20 |
21 | /**
22 | * PrePersistEvent constructor.
23 | * @param $document
24 | */
25 | public function __construct($document)
26 | {
27 | $this->document = $document;
28 | }
29 |
30 | /**
31 | * @return object
32 | */
33 | public function getDocument()
34 | {
35 | return $this->document;
36 | }
37 |
38 | /**
39 | * @param object $document
40 | */
41 | public function setDocument($document)
42 | {
43 | $this->document = $document;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Tests/Unit/Annotation/PropertyTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Annotation;
13 |
14 | use ONGR\ElasticsearchBundle\Annotation\Property;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class PropertyTest extends TestCase
18 | {
19 | /**
20 | * Tests if values are filtered correctly.
21 | */
22 | public function testFilter()
23 | {
24 | $type = new Property();
25 |
26 | $type->name = 'id';
27 | $type->index = 'no_index';
28 | $type->type = 'string';
29 |
30 | $this->assertEquals(
31 | [
32 | 'type' => 'string',
33 | ],
34 | $type->dump(),
35 | 'Properties should be filtered'
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Annotation/Routing.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | /**
15 | * Annotation used to enable ROUTING and associate document property with _routing meta-field.
16 | *
17 | * @Annotation
18 | * @Target("PROPERTY")
19 | */
20 | final class Routing implements MetaField
21 | {
22 | const NAME = '_routing';
23 |
24 | /**
25 | * @var bool
26 | */
27 | public $required = false;
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | public function getName()
33 | {
34 | return self::NAME;
35 | }
36 |
37 | /**
38 | * {@inheritdoc}
39 | */
40 | public function getSettings()
41 | {
42 | return [
43 | 'required' => $this->required
44 | ];
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Tests/app/fixture/TestBundle/Document/LongDescriptionTrait.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document;
13 |
14 | use ONGR\ElasticsearchBundle\Annotation as ES;
15 |
16 | trait LongDescriptionTrait
17 | {
18 | /**
19 | * @ES\Property(type="text", name="long_description")
20 | * @var string
21 | */
22 | private $long_description;
23 |
24 | /**
25 | * @param string $long_description
26 | */
27 | public function setLongDescription($long_description)
28 | {
29 | $this->long_description = $long_description;
30 | return $this;
31 | }
32 |
33 | /**
34 | * @return string
35 | */
36 | public function getLongDescription()
37 | {
38 | return $this->long_description;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Annotation/Nested.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | /**
15 | * Annotation to mark a class as a nested type during the parsing process.
16 | *
17 | * @Annotation
18 | * @Target("CLASS")
19 | *
20 | * @deprecated Object is reserved word in PHP 7.2 This class due Object class will be changed to NestedType as well.
21 | */
22 | final class Nested
23 | {
24 | const NAME = 'nested';
25 |
26 | /**
27 | * In this field you can define options.
28 | *
29 | * @var array
30 | */
31 | public $options = [];
32 |
33 | /**
34 | * {@inheritdoc}
35 | */
36 | public function dump(array $exclude = [])
37 | {
38 | return array_diff_key(
39 | $this->options,
40 | $exclude
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Event/BaseEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | // @codingStandardsIgnoreStart
13 |
14 | namespace ONGR\ElasticsearchBundle\Event;
15 |
16 | use Symfony\Component\EventDispatcher\Event;
17 | use Symfony\Component\EventDispatcher\EventDispatcherInterface;
18 | use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
19 | use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface;
20 |
21 | // Clean up when sf 3.4 support is removed
22 | if (is_subclass_of(EventDispatcherInterface::class, ContractsEventDispatcherInterface::class)) {
23 | /**
24 | * @internal
25 | */
26 | abstract class BaseEvent extends ContractsEvent
27 | {
28 | }
29 | } else {
30 | /**
31 | * @internal
32 | */
33 | abstract class BaseEvent extends Event
34 | {
35 | }
36 | }
37 | // @codingStandardsIgnoreEnd
38 |
--------------------------------------------------------------------------------
/Event/PostCreateManagerEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Event;
13 |
14 | use ONGR\ElasticsearchBundle\Service\Manager;
15 |
16 | class PostCreateManagerEvent extends BaseEvent
17 | {
18 | /**
19 | * @var Manager
20 | */
21 | private $manager;
22 |
23 | /**
24 | * PostCreateManagerEvent constructor.
25 | *
26 | * @param Manager $manager
27 | */
28 | public function __construct(Manager $manager)
29 | {
30 | $this->manager = $manager;
31 | }
32 |
33 | /**
34 | * @return Manager
35 | */
36 | public function getManager()
37 | {
38 | return $this->manager;
39 | }
40 |
41 | /**
42 | * @param Manager $manager
43 | */
44 | public function setManager($manager)
45 | {
46 | $this->manager = $manager;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Tests/Functional/Annotation/PropertyTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Annotation;
13 |
14 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
15 |
16 | class PropertyTest extends AbstractElasticsearchTestCase
17 | {
18 | /**
19 | * Test if field names are correctly generated from property names.
20 | */
21 | public function testIfNamesFormedCorrectly()
22 | {
23 | $mappings = $this->getManager()->getMetadataCollector()->getMapping('TestBundle:User');
24 |
25 | $expected = [
26 | 'first_name' => [
27 | 'type' => 'text',
28 | ],
29 | 'last_name' => [
30 | 'type' => 'text',
31 | ],
32 | ];
33 |
34 | $this->assertEquals($expected, $mappings['properties']);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-2016 NFQ Technologies UAB
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Annotation/ParentDocument.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | /**
15 | * Annotation used to enable parent-child relationship.
16 | *
17 | * @Annotation
18 | * @Target("PROPERTY")
19 | */
20 | final class ParentDocument implements MetaField
21 | {
22 | const NAME = '_parent';
23 |
24 | /**
25 | * Parent document class name.
26 | *
27 | * @var string
28 | *
29 | * @Doctrine\Common\Annotations\Annotation\Required
30 | */
31 | public $class;
32 |
33 | /**
34 | * {@inheritdoc}
35 | */
36 | public function getName()
37 | {
38 | return self::NAME;
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | public function getSettings()
45 | {
46 | return [
47 | 'type' => null, // Actual value will be generated from $class property
48 | ];
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Annotation/ObjectType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | /**
15 | * Annotation to mark a class as an object during the parsing process.
16 | *
17 | * `Object` name as class name is forbidden in PHP 7 but we never create this
18 | * class as object and only use it for annotation definition.
19 | *
20 | * @Annotation
21 | * @Target("CLASS")
22 | *
23 | * @deprecated Object is reserved word in PHP 7.2, it will be changed to ObjectType class
24 | */
25 | final class ObjectType
26 | {
27 | const NAME = 'object';
28 |
29 | /**
30 | * In this field you can define options.
31 | *
32 | * @var array
33 | */
34 | public $options = [];
35 |
36 | /**
37 | * {@inheritdoc}
38 | */
39 | public function dump(array $exclude = [])
40 | {
41 | return array_diff_key(
42 | $this->options,
43 | $exclude
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Profiler/Handler/CollectionHandler.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Profiler\Handler;
13 |
14 | use Monolog\Handler\AbstractProcessingHandler;
15 | use Monolog\LogRecord;
16 |
17 | /**
18 | * Handler that saves all records to him self.
19 | */
20 | class CollectionHandler extends AbstractProcessingHandler
21 | {
22 | /**
23 | * @var array[]|LogRecord[]
24 | */
25 | private $records = [];
26 |
27 | /**
28 | * @param array|LogRecord $record
29 | */
30 | protected function write($record): void
31 | {
32 | $this->records[] = $record;
33 | }
34 |
35 | /**
36 | * Returns recorded data.
37 | *
38 | * @return array[]|LogRecord[]
39 | */
40 | public function getRecords()
41 | {
42 | return $this->records;
43 | }
44 |
45 | /**
46 | * Clears recorded data.
47 | */
48 | public function clearRecords()
49 | {
50 | $this->records = [];
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Tests/app/config/config_test.yml:
--------------------------------------------------------------------------------
1 | framework:
2 | secret: "SUPER-TOP-SECRET"
3 | test: ~
4 |
5 | ongr_elasticsearch:
6 | analysis:
7 | filter:
8 | incremental_filter:
9 | type: edge_ngram
10 | min_gram: 1
11 | max_gram: 20
12 | analyzer:
13 | incrementalAnalyzer: #-> analyzer name
14 | type: custom
15 | tokenizer: standard
16 | filter:
17 | - lowercase
18 | - incremental_filter
19 | managers:
20 | custom_dir:
21 | index:
22 | hosts:
23 | - 127.0.0.1:9200
24 | index_name: ongr-custom-document-dir-test
25 | mappings:
26 | TestBundle:
27 | document_dir: Entity
28 | default:
29 | index:
30 | hosts:
31 | - 127.0.0.1:9200
32 | index_name: ongr-esb-test
33 | settings:
34 | refresh_interval: -1
35 | number_of_replicas: 0
36 | number_of_shards: 5
37 | mappings:
38 | - TestBundle
39 |
--------------------------------------------------------------------------------
/Tests/Unit/Profiler/ElasticsearchProfilerTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Profiler;
13 |
14 | use ONGR\ElasticsearchBundle\Profiler\ElasticsearchProfiler;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class ElasticsearchProfilerTest extends TestCase
18 | {
19 | /**
20 | * Tests if correct name is being returned.
21 | */
22 | public function testGetName()
23 | {
24 | $collector = new ElasticsearchProfiler();
25 | $this->assertEquals('ongr.profiler', $collector->getName());
26 | }
27 |
28 | /**
29 | * Tests getManagers method.
30 | */
31 | public function testGetManagers()
32 | {
33 | $collector = new ElasticsearchProfiler();
34 | $collector->setManagers([ 'default' => [], 'acme' => [] ]);
35 |
36 | $result = $collector->getManagers();
37 | $this->assertEquals(
38 | [ 'default' => 'es.manager', 'acme' => 'es.manager.acme' ],
39 | $result
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Tests/Functional/Annotation/DocumentTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Annotation;
13 |
14 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
15 |
16 | class DocumentTest extends AbstractElasticsearchTestCase
17 | {
18 | /**
19 | * Test document mapping.
20 | */
21 | public function testDocumentMapping()
22 | {
23 | $manager = $this->getManager();
24 | $repo = $manager->getRepository('TestBundle:Product');
25 |
26 | $type = $repo->getType();
27 | $mappings = $manager->getClient()->indices()->getMapping(['index' => $manager->getIndexName()]);
28 |
29 | $this->assertArrayHasKey($type, $mappings[$manager->getIndexName()]['mappings']);
30 |
31 | $managerMappings = $manager->getMetadataCollector()->getMapping('TestBundle:Product');
32 |
33 | $this->assertEquals(
34 | sort($managerMappings['properties']),
35 | sort($mappings[$manager->getIndexName()]['mappings'][$type])
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Tests/app/AppKernel.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | use Symfony\Component\Config\Loader\LoaderInterface;
13 | use Symfony\Component\HttpKernel\Kernel;
14 |
15 | /**
16 | * AppKernel class.
17 | */
18 | class AppKernel extends Kernel
19 | {
20 | /**
21 | * Register bundles.
22 | *
23 | * @return array
24 | */
25 | #[\ReturnTypeWillChange] public function registerBundles(): iterable /*
26 | public function registerBundles() /* PHP<8 */
27 | { // @codingStandardsIgnoreLine
28 | return [
29 | new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
30 | new ONGR\ElasticsearchBundle\ONGRElasticsearchBundle(),
31 | new ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\TestBundle(),
32 | ];
33 | }
34 |
35 | /**
36 | * Register container configuration.
37 | *
38 | * @param LoaderInterface $loader
39 | */
40 | public function registerContainerConfiguration(LoaderInterface $loader)
41 | {
42 | $loader->load(__DIR__ . '/config/config_' . $this->getEnvironment() . '.yml');
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ElasticsearchBundle
2 |
3 | This is a fork of the [ongr/elasticsearch-bundle](https://github.com/ongr-io/elasticsearchbundle).
4 | With some basic changes to support wider range of Symfony Versions.
5 |
6 | | Version | Supported Elasticsearch Version | Supported Symfony Version | Supported Doctrine Annotations Version |
7 | |-------------|---------------------------------|--------------------------------------|----------------------------------------|
8 | | `5.5` | `^7.0, ^6.0, ^5.0` | `^6.0, ^5.0, ^4.0, ^3.4` | `^1.13, ^2.0` |
9 | | `5.0 - 5.4` | `^7.0, ^6.0, ^5.0` | `^7.0, ^6.0, ^5.0, ^4.0, ^3.4, ^2.8` | `^1.0` |
10 | | `1.x` | `^1.0, ^2.0` | `^3.0, ^2.7` | `^1.0` |
11 |
12 | ## Documentation
13 |
14 | [The online documentation of the bundle is here](Resources/doc/index.md)
15 |
16 | ## Try it!
17 |
18 | ### Installation
19 |
20 | Install library with [composer](https://getcomposer.org):
21 |
22 | ```bash
23 | composer require handcraftedinthealps/elasticsearch-bundle
24 | ```
25 |
26 | ### Usage
27 |
28 | Its used by the [sulu/article-bundle](https://github.com/sulu/SuluArticleBundle/) to support newer a wide range of symfony and elasticsearch versions.
29 |
--------------------------------------------------------------------------------
/Tests/app/fixture/TestBundle/Document/CategoryObject.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document;
13 |
14 | use ONGR\ElasticsearchBundle\Annotation as ES;
15 |
16 | /**
17 | * Category object for testing.
18 | *
19 | * @ES\ObjectType
20 | */
21 | class CategoryObject
22 | {
23 | /**
24 | * @var string
25 | * @ES\Property(type="keyword")
26 | */
27 | private $title;
28 |
29 | /**
30 | * Public property to test converter if it can handle private and public properties.
31 | *
32 | * @var string
33 | * @ES\Property(type="text", options={"analyzer":"keyword"})
34 | */
35 | public $description;
36 |
37 | /*
38 | * Test the use of traits
39 | */
40 | use LongDescriptionTrait;
41 |
42 | /**
43 | * @return string
44 | */
45 | public function getTitle()
46 | {
47 | return $this->title;
48 | }
49 |
50 | /**
51 | * @param string $title
52 | */
53 | public function setTitle($title)
54 | {
55 | $this->title = $title;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Resources/doc/configuration.md:
--------------------------------------------------------------------------------
1 | # Configuration tree
2 |
3 | Here's an example of full configuration with all possible options including default values:
4 |
5 | ```yml
6 | ongr_elasticsearch:
7 | analysis:
8 | analyzer:
9 | pathAnalyzer:
10 | type: custom
11 | tokenizer: pathTokenizer
12 | tokenizer:
13 | pathTokenizer:
14 | type : path_hierarchy
15 | buffer_size: 2024
16 | skip: 0
17 | delimiter: /
18 | filter:
19 | incremental_filter:
20 | type: edge_ngram
21 | min_gram: 1
22 | max_gram: 20
23 | managers:
24 | default:
25 | index:
26 | hosts:
27 | - 127.0.0.1:9200
28 | index_name: ongr-default
29 | settings:
30 | refresh_interval: -1
31 | number_of_replicas: 0
32 | number_of_shards: 1
33 | logger: true #default %kernel.debug%
34 | mappings:
35 | - AcmeBarBundle #Scans all bundle documents
36 | custom:
37 | index:
38 | hosts:
39 | - 10.0.0.1:9200 #default 127.0.0.1:9200
40 | index_name: ongr-custom
41 | mappings:
42 | AcmeBundle:
43 | document_dir: Document
44 | ```
--------------------------------------------------------------------------------
/Event/Events.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Event;
13 |
14 | /**
15 | * Contains all events thrown in the ONGRElasticsearchBundle
16 | */
17 | final class Events
18 | {
19 | /**
20 | * The PRE_PERSIST event occurs before convert to Array
21 | */
22 | const PRE_PERSIST = 'es.pre_persist';
23 |
24 | /**
25 | * The BULK event occurs before during the processing of bulk method
26 | */
27 | const BULK = 'es.bulk';
28 |
29 | /**
30 | * The PRE_COMMIT event occurs before committing queries to ES
31 | */
32 | const PRE_COMMIT = 'es.pre_commit';
33 |
34 | /**
35 | * The POST_COMMIT event occurs after committing queries to ES
36 | */
37 | const POST_COMMIT = 'es.post_commit';
38 |
39 | /**
40 | * The PRE_MANAGER_CREATE event occurs before manager is created, right after client is initiated.
41 | * You can modify anything in the core elasticsearch-php client by this event.
42 | */
43 | const PRE_MANAGER_CREATE = 'es.pre_manager_create';
44 |
45 | /**
46 | * The POST_MANAGER_CREATE event occurs after manager is created.
47 | */
48 | const POST_MANAGER_CREATE = 'es.post_manager_create';
49 | }
50 |
--------------------------------------------------------------------------------
/Tests/app/fixture/data/command_import_20.json:
--------------------------------------------------------------------------------
1 | [
2 | {"count":20},
3 | {"_type":"product","_id":"doc1","_source":{"title":"Document 1"}},
4 | {"_type":"product","_id":"doc2","_source":{"title":"Document 2"}},
5 | {"_type":"product","_id":"doc3","_source":{"title":"Document 3"}},
6 | {"_type":"product","_id":"doc4","_source":{"title":"Document 4"}},
7 | {"_type":"product","_id":"doc5","_source":{"title":"Document 5"}},
8 | {"_type":"product","_id":"doc6","_source":{"title":"Document 6"}},
9 | {"_type":"product","_id":"doc7","_source":{"title":"Document 7"}},
10 | {"_type":"product","_id":"doc8","_source":{"title":"Document 8"}},
11 | {"_type":"product","_id":"doc9","_source":{"title":"Document 9"}},
12 | {"_type":"product","_id":"doc10","_source":{"title":"Document 10"}},
13 | {"_type":"product","_id":"doc11","_source":{"title":"Document 11"}},
14 | {"_type":"product","_id":"doc12","_source":{"title":"Document 12"}},
15 | {"_type":"product","_id":"doc13","_source":{"title":"Document 13"}},
16 | {"_type":"product","_id":"doc14","_source":{"title":"Document 14"}},
17 | {"_type":"product","_id":"doc15","_source":{"title":"Document 15"}},
18 | {"_type":"product","_id":"doc16","_source":{"title":"Document 16"}},
19 | {"_type":"product","_id":"doc17","_source":{"title":"Document 17"}},
20 | {"_type":"product","_id":"doc18","_source":{"title":"Document 18"}},
21 | {"_type":"product","_id":"doc19","_source":{"title":"Document 19"}},
22 | {"_type":"product","_id":"doc20","_source":{"title":"Document 20"}}
23 | ]
--------------------------------------------------------------------------------
/Service/IndexSuffixFinder.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Service;
13 |
14 | /**
15 | * Constructs index name with date's suffix.
16 | */
17 | class IndexSuffixFinder
18 | {
19 | /**
20 | * Constructs index name with date suffix. Sets name in the connection.
21 | *
22 | * E.g. 2022.03.22-5 (if 4 indexes exists already for given date)
23 | *
24 | * @param Manager $manager Connection to act upon.
25 | * @param null|\DateTime $time Date for which the suffix will be based on.
26 | * Current date if null.
27 | *
28 | * @return string
29 | */
30 | public function setNextFreeIndex(Manager $manager, \DateTime $time = null)
31 | {
32 | if ($time === null) {
33 | $time = new \DateTime();
34 | }
35 |
36 | $date = $time->format('Y.m.d');
37 | $indexName = $manager->getIndexName();
38 |
39 | $nameBase = $indexName . '-' . $date;
40 | $name = $nameBase;
41 | $i = 0;
42 | $manager->setIndexName($name);
43 |
44 | while ($manager->indexExists()) {
45 | $i++;
46 | $name = "{$nameBase}-{$i}";
47 | $manager->setIndexName($name);
48 | }
49 |
50 | return $name;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/ONGRElasticsearchBundle.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle;
13 |
14 | use ONGR\ElasticsearchBundle\DependencyInjection\Compiler\AnnotationReaderPass;
15 | use ONGR\ElasticsearchBundle\DependencyInjection\Compiler\ManagerPass;
16 | use ONGR\ElasticsearchBundle\DependencyInjection\Compiler\MappingPass;
17 | use ONGR\ElasticsearchBundle\DependencyInjection\Compiler\RepositoryPass;
18 | use Symfony\Component\DependencyInjection\Compiler\PassConfig;
19 | use Symfony\Component\DependencyInjection\ContainerBuilder;
20 | use Symfony\Component\HttpKernel\Bundle\Bundle;
21 |
22 | /**
23 | * ONGR Elasticsearch bundle system file required by kernel.
24 | */
25 | class ONGRElasticsearchBundle extends Bundle
26 | {
27 | /**
28 | * {@inheritdoc}
29 | */
30 | public function build(ContainerBuilder $container)
31 | {
32 | parent::build($container);
33 |
34 | $container->addCompilerPass(new MappingPass());
35 | $container->addCompilerPass(new ManagerPass());
36 | $container->addCompilerPass(new AnnotationReaderPass());
37 | // The `RepositoryPass` need to be behind the Symfony `DecoratorServicePass`
38 | // to allow decorating the annotation reader
39 | $container->addCompilerPass(new RepositoryPass(), PassConfig::TYPE_OPTIMIZE, -10);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Resources/doc/index.md:
--------------------------------------------------------------------------------
1 | ## Elasticsearch Bundle
2 |
3 | Welcome to the ElasticsearchBundle, the modern solution to work with [Elasticsearch database](https://www.elastic.co/products/elasticsearch) in the [Symfony](https://github.com/symfony/symfony-standard) applications. We created this bundle with love :heart: and we think you will love it too.
4 |
5 | > Bundle set up guide you can find in the `README.md` file.
6 |
7 | #### Usage
8 | * [Mapping explained](mapping.md)
9 | * [Using Meta-Fields](meta_fields.md)
10 | * [Configuration](configuration.md)
11 | * [Console commands](commands.md)
12 | * [How to do a simple CRUD actions](crud.md)
13 | * [Quick find functions](find_functions.md)
14 | * [How to search the index](search.md)
15 | * [Scan through the index](scan.md)
16 | * [Parsing the results](results_parsing.md)
17 |
18 | #### Troubleshooting
19 | * [How to upgrade from the older versions?](upgrade.md)
20 | * [How to overwrite some parts of the bundle?](overwriting_bundle.md)
21 |
22 | #### Some news for upcoming versions
23 |
24 | We already have a new milestone set for v1.1.0 version (see active milestones [here](https://github.com/ongr-io/ElasticsearchBundle/milestones)). For the new milestone we are planning to support new endpoints:
25 | * Highlight
26 | * Autocomplete
27 | * Suggestions
28 |
29 | We are hoping that those features will help our community to create better projects. **If you think that we are missing something very important which would help you, please do not hesitate to create an issue and ask about it.** We are very open and looking forward to see a projects running on this awesome bundle :smirk:.
30 |
--------------------------------------------------------------------------------
/Tests/Unit/Mapping/DocumentParserTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Mapping;
13 |
14 | use ONGR\ElasticsearchBundle\Mapping\DocumentParser;
15 | use ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\LongDescriptionTrait;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | class DocumentParserTest extends TestCase
19 | {
20 | /*
21 | * @var \Doctrine\Common\Annotations\Reader
22 | */
23 | private $reader;
24 |
25 | /*
26 | * @var \ONGR\ElasticsearchBundle\Mapping\DocumentFinder
27 | */
28 | private $finder;
29 |
30 | protected function setUp(): void
31 | {
32 | $this->reader = $this->getMockBuilder('Doctrine\Common\Annotations\Reader')
33 | ->disableOriginalConstructor()
34 | ->getMock();
35 |
36 | $this->finder = $this->getMockBuilder('ONGR\ElasticsearchBundle\Mapping\DocumentFinder')
37 | ->disableOriginalConstructor()
38 | ->getMock();
39 | }
40 |
41 | public function testReturnFalseOnTrait()
42 | {
43 | $traitReflection = new \ReflectionClass(
44 | '\\ONGR\\ElasticsearchBundle\\Tests\\app\\fixture\\TestBundle\\Document\\LongDescriptionTrait'
45 | );
46 |
47 | $parser = new DocumentParser($this->reader, $this->finder);
48 | $this->assertFalse($parser->parse($traitReflection));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
15 | ./Tests/Unit/
16 |
17 |
18 | ./Tests/Functional/
19 |
20 |
21 | ./Tests/
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | ./
34 |
35 | ./Tests
36 | ./vendor
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/Event/CommitEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Event;
13 |
14 | class CommitEvent extends BaseEvent
15 | {
16 | /**
17 | * @var string
18 | */
19 | private $commitMode;
20 |
21 | /**
22 | * @var array
23 | */
24 | private $bulkParams;
25 |
26 | /**
27 | * @param string $commitMode
28 | * @param array|null $bulkParams BulkQueries or BulkResponse, depending on event
29 | */
30 | public function __construct($commitMode, $bulkParams = [])
31 | {
32 | $this->commitMode = $commitMode;
33 | $this->bulkParams = $bulkParams;
34 | }
35 |
36 | /**
37 | * Returns commit mode
38 | *
39 | * @return string
40 | */
41 | public function getCommitMode()
42 | {
43 | return $this->commitMode;
44 | }
45 |
46 | /**
47 | * @param string $commitMode
48 | */
49 | public function setCommitMode($commitMode)
50 | {
51 | $this->commitMode = $commitMode;
52 | }
53 |
54 | /**
55 | * Returns params
56 | *
57 | * @return array
58 | */
59 | public function getBulkParams()
60 | {
61 | return $this->bulkParams;
62 | }
63 |
64 | /**
65 | * @param array $bulkParams
66 | */
67 | public function setBulkParams($bulkParams)
68 | {
69 | $this->bulkParams = $bulkParams;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Result/DocumentIterator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Result;
13 |
14 | use ONGR\ElasticsearchBundle\Result\Aggregation\AggregationValue;
15 |
16 | /**
17 | * Class DocumentIterator.
18 | */
19 | class DocumentIterator extends AbstractResultsIterator
20 | {
21 | /**
22 | * Returns aggregations.
23 | *
24 | * @return array
25 | */
26 | public function getAggregations()
27 | {
28 | $aggregations = [];
29 |
30 | foreach (parent::getAggregations() as $key => $aggregation) {
31 | $aggregations[$key] = $this->getAggregation($key);
32 | }
33 |
34 | return $aggregations;
35 | }
36 |
37 | /**
38 | * Get a specific aggregation by name. It fetches from the top level only.
39 | *
40 | * @param string $name
41 | *
42 | * @return AggregationValue|null
43 | */
44 | public function getAggregation($name)
45 | {
46 | $aggregations = parent::getAggregations();
47 | if (!array_key_exists($name, $aggregations)) {
48 | return null;
49 | }
50 |
51 | return new AggregationValue($aggregations[$name]);
52 | }
53 |
54 | /**
55 | * {@inheritdoc}
56 | */
57 | protected function convertDocument(array $document)
58 | {
59 | return $this->getConverter()->convertToDocument($document, $this->getManager());
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Event/PreCreateManagerEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Event;
13 |
14 | use Elasticsearch\ClientBuilder;
15 |
16 | class PreCreateManagerEvent extends BaseEvent
17 | {
18 | /**
19 | * @var ClientBuilder
20 | */
21 | private $client;
22 |
23 | /**
24 | * @var array
25 | */
26 | private $indexSettings;
27 |
28 | /**
29 | * CreateManagerEvent constructor.
30 | *
31 | * @param ClientBuilder $client
32 | * @param $indexSettings array
33 | */
34 | public function __construct(ClientBuilder $client, &$indexSettings)
35 | {
36 | $this->client = $client;
37 | $this->indexSettings = $indexSettings;
38 | }
39 |
40 | /**
41 | * @return ClientBuilder
42 | */
43 | public function getClient()
44 | {
45 | return $this->client;
46 | }
47 |
48 | /**
49 | * @param ClientBuilder $client
50 | */
51 | public function setClient(ClientBuilder $client)
52 | {
53 | $this->client = $client;
54 | }
55 |
56 | /**
57 | * @return array
58 | */
59 | public function getIndexSettings()
60 | {
61 | return $this->indexSettings;
62 | }
63 |
64 | /**
65 | * @param array $indexSettings
66 | */
67 | public function setIndexSettings($indexSettings)
68 | {
69 | $this->indexSettings = $indexSettings;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Tests/Functional/Command/GenerateDocumentCommandTest.php:
--------------------------------------------------------------------------------
1 | expectException(\InvalidArgumentException::class);
18 |
19 | $app = new Application();
20 | $app->add($this->getCommand());
21 |
22 | $command = $app->find('ongr:es:document:generate');
23 |
24 | $tester = new CommandTester($command);
25 | $tester->execute(['command' => $command->getName()], ['interactive' => false]);
26 | $tester->execute(
27 | ['command' => $command->getName(), '--no-interaction' => true],
28 | ['interactive' => false]
29 | );
30 | }
31 |
32 | /**
33 | * @return DocumentGenerateCommand
34 | */
35 | private function getCommand()
36 | {
37 | $container = self::createClient()->getContainer();
38 |
39 | return new DocumentGenerateCommand(
40 | $container->getParameter('kernel.bundles'),
41 | $container->get('es.generate'),
42 | $container->get('es.metadata_collector'),
43 | $container->get('es.annotations.cached_reader'),
44 | ['es.manager.default' => $container->get('es.manager.default')]
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Result/ArrayIterator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Result;
13 |
14 | /**
15 | * Class DocumentIterator.
16 | */
17 | class ArrayIterator extends AbstractResultsIterator implements \ArrayAccess
18 | {
19 | /**
20 | * {@inheritdoc}
21 | */
22 | #[\ReturnTypeWillChange]
23 | public function offsetExists($offset)
24 | {
25 | return $this->documentExists($offset);
26 | }
27 |
28 | /**
29 | * {@inheritdoc}
30 | */
31 | #[\ReturnTypeWillChange]
32 | public function offsetGet($offset)
33 | {
34 | return $this->getDocument($offset);
35 | }
36 |
37 | /**
38 | * {@inheritdoc}
39 | */
40 | #[\ReturnTypeWillChange]
41 | public function offsetSet($offset, $value)
42 | {
43 | $this->documents[$offset] = $value;
44 | }
45 |
46 | /**
47 | * {@inheritdoc}
48 | */
49 | #[\ReturnTypeWillChange]
50 | public function offsetUnset($offset)
51 | {
52 | unset($this->documents[$offset]);
53 | }
54 |
55 | /**
56 | * {@inheritdoc}
57 | */
58 | protected function convertDocument(array $document)
59 | {
60 | if (array_key_exists('_source', $document)) {
61 | return $document['_source'];
62 | } elseif (array_key_exists('fields', $document)) {
63 | return array_map('reset', $document['fields']);
64 | }
65 |
66 | return $document;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Tests/Functional/Result/DocumentWithNullObjectFieldTest.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 |
11 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Result;
12 |
13 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
14 | use ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product;
15 |
16 | class DocumentNullObjectFieldTest extends AbstractElasticsearchTestCase
17 | {
18 | /**
19 | * {@inheritdoc}
20 | */
21 | protected function getDataArray()
22 | {
23 | return [
24 | 'default' => [
25 | 'product' => [
26 | [
27 | '_id' => 'foo',
28 | 'title' => 'Bar Product',
29 | 'location' => null,
30 | 'released' => null,
31 | ],
32 | ],
33 | ],
34 | ];
35 | }
36 |
37 | /**
38 | * Test if fetched object field is actually NULL.
39 | */
40 | public function testResultWithNullObjectField()
41 | {
42 | /** @var Product $document */
43 | $document = $this->getManager()->find('TestBundle:Product', 'foo');
44 |
45 | $this->assertInstanceOf(
46 | 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product',
47 | $document
48 | );
49 |
50 | $this->assertNull($document->getLocation());
51 | $this->assertNull($document->getReleased());
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/DependencyInjection/Compiler/ManagerPass.php:
--------------------------------------------------------------------------------
1 | findTaggedServiceIds(self::TAG_NAME);
16 | foreach ($taggedServices as $key => $ids) {
17 | $managers[$key] = $container->getDefinition($key);
18 | }
19 |
20 | $this->addManagers($container, 'es.command.cache_clear', $managers);
21 | $this->addManagers($container, 'es.command.document_generate', $managers);
22 | $this->addManagers($container, 'es.command.index_create', $managers);
23 | $this->addManagers($container, 'es.command.index_export', $managers);
24 | $this->addManagers($container, 'es.command.index_import', $managers);
25 | $this->addManagers($container, 'es.command.index_drop', $managers);
26 | }
27 |
28 | private function addManagers(ContainerBuilder $container, $definitionName, array $managers)
29 | {
30 | $definition = $container->getDefinition($definitionName);
31 |
32 | if (method_exists($definition, 'setArgument')) {
33 | $definition->setArgument('$managers', $managers);
34 |
35 | return;
36 | }
37 |
38 | $arguments = $definition->getArguments();
39 | $arguments[] = $managers;
40 |
41 | $definition->setArguments($arguments);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Command/CacheClearCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Command;
13 |
14 | use Symfony\Component\Console\Input\InputInterface;
15 | use Symfony\Component\Console\Output\OutputInterface;
16 | use Symfony\Component\Console\Style\SymfonyStyle;
17 |
18 | /**
19 | * Symfony command for clearing elasticsearch cache.
20 | */
21 | class CacheClearCommand extends AbstractManagerAwareCommand
22 | {
23 | public static $defaultName = 'ongr:es:cache:clear';
24 |
25 | public function __construct(array $managers = [])
26 | {
27 | parent::__construct($managers, self::$defaultName);
28 | }
29 |
30 | /**
31 | * {@inheritdoc}
32 | */
33 | protected function configure(): void
34 | {
35 | parent::configure();
36 |
37 | $this
38 | ->setName(static::$defaultName)
39 | ->setDescription('Clears elasticsearch client cache.');
40 | }
41 |
42 | /**
43 | * {@inheritdoc}
44 | */
45 | protected function execute(InputInterface $input, OutputInterface $output): int
46 | {
47 | $io = new SymfonyStyle($input, $output);
48 | $this
49 | ->getManager($input->getOption('manager'))
50 | ->clearCache();
51 | $io->success(
52 | sprintf(
53 | 'Elasticsearch index cache has been cleared for manager named `%s`',
54 | $input->getOption('manager')
55 | )
56 | );
57 |
58 | return 0;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Annotation/HashMap.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | use Doctrine\Common\Annotations\Annotation\Enum;
15 |
16 | /**
17 | * Annotation for property which points to inner object.
18 | *
19 | * @Annotation
20 | * @Target("PROPERTY")
21 | */
22 | final class HashMap
23 | {
24 | const NAME = 'hash_map';
25 |
26 | /**
27 | * Name of the type field. Defaults to normalized property name.
28 | *
29 | * @var string
30 | */
31 | public $name;
32 |
33 | /**
34 | * Property type for nested structure values.
35 | *
36 | * @var mixed
37 | * @Enum({
38 | * "text", "keyword",
39 | * "long", "integer", "short", "byte", "double", "float",
40 | * "date",
41 | * "boolean",
42 | * "binary",
43 | * "geo_point", "geo_shape",
44 | * "ip", "completion", "token_count", "murmur3", "attachments", "percolator"
45 | * })
46 | */
47 | public $type;
48 |
49 | /**
50 | * In this field you can define options.
51 | *
52 | * @var array
53 | */
54 | public $options = [];
55 |
56 | /**
57 | * {@inheritdoc}
58 | */
59 | public function dump(array $exclude = [])
60 | {
61 | return array_diff_key(
62 | array_merge(
63 | [
64 | 'type' => $this->type
65 | ],
66 | $this->options
67 | ),
68 | $exclude
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Result/ObjectIterator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Result;
13 |
14 | use Doctrine\Common\Collections\AbstractLazyCollection;
15 | use Doctrine\Common\Collections\ArrayCollection;
16 |
17 | /**
18 | * ObjectIterator class.
19 | */
20 | class ObjectIterator extends AbstractLazyCollection
21 | {
22 | /**
23 | * @var Converter
24 | */
25 | private $converter;
26 |
27 | /**
28 | * @var array Aliases information.
29 | */
30 | private $alias;
31 |
32 | /**
33 | * Converts raw document data to objects when requested.
34 | *
35 | * @param Converter $converter
36 | * @param array $objects
37 | * @param array $alias
38 | */
39 | public function __construct($converter, $objects, $alias)
40 | {
41 | $this->converter = $converter;
42 | $this->alias = $alias;
43 | $this->collection = new ArrayCollection($objects);
44 | }
45 |
46 | /**
47 | * {@inheritdoc}
48 | */
49 | protected function convertDocument(array $document)
50 | {
51 | return $this->converter->assignArrayToObject(
52 | $document,
53 | new $this->alias['namespace'](),
54 | $this->alias['aliases']
55 | );
56 | }
57 |
58 | /**
59 | * @inheritDoc
60 | */
61 | protected function doInitialize()
62 | {
63 | $this->collection = $this->collection->map(function ($rawObject) {
64 | return $this->convertDocument($rawObject);
65 | });
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Annotation/Property.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | use Doctrine\Common\Annotations\Annotation\Enum;
15 |
16 | /**
17 | * Annotation used to check mapping type during the parsing process.
18 | *
19 | * @Annotation
20 | * @Target("PROPERTY")
21 | */
22 | final class Property
23 | {
24 | /**
25 | * Field type.
26 | *
27 | * @var string
28 | *
29 | * @Doctrine\Common\Annotations\Annotation\Required
30 | * @Enum({
31 | * "text", "keyword",
32 | * "long", "integer", "short", "byte", "double", "float",
33 | * "date",
34 | * "boolean",
35 | * "binary",
36 | * "geo_point", "geo_shape",
37 | * "ip", "completion", "token_count", "murmur3", "attachments", "percolator"
38 | * })
39 | */
40 | public $type;
41 |
42 | /**
43 | * Name of the type field. Defaults to normalized property name.
44 | *
45 | * @var string
46 | */
47 | public $name;
48 |
49 | /**
50 | * In this field you can define options (like analyzers and etc) for specific field types.
51 | *
52 | * @var array
53 | */
54 | public $options = [];
55 |
56 | /**
57 | * {@inheritdoc}
58 | */
59 | public function dump(array $exclude = [])
60 | {
61 | return array_diff_key(
62 | array_merge(
63 | [
64 | 'type' => $this->type
65 | ],
66 | $this->options
67 | ),
68 | $exclude
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Tests/Functional/DependencyInjection/ElasticsearchExtensionTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\DependencyInjection;
13 |
14 | use ONGR\ElasticsearchBundle\Tests\WebTestCase;
15 |
16 | class ElasticsearchExtensionTest extends WebTestCase
17 | {
18 | /**
19 | * @return array
20 | */
21 | public function getTestContainerData()
22 | {
23 | return [
24 | [
25 | 'es.manager',
26 | 'ONGR\ElasticsearchBundle\Service\Manager',
27 | ],
28 | [
29 | 'es.manager.default',
30 | 'ONGR\ElasticsearchBundle\Service\Manager',
31 | ],
32 | [
33 | 'es.manager.default.product',
34 | 'ONGR\ElasticsearchBundle\Service\Repository',
35 | ],
36 | [
37 | 'es.metadata_collector',
38 | 'ONGR\ElasticsearchBundle\Mapping\MetadataCollector',
39 | ],
40 | ];
41 | }
42 |
43 | /**
44 | * Tests if container has all services.
45 | *
46 | * @param string $id
47 | * @param string $instance
48 | *
49 | * @dataProvider getTestContainerData
50 | */
51 | public function testContainer($id, $instance)
52 | {
53 | $container = static::createClient()->getContainer();
54 |
55 | $this->assertTrue($container->has($id), 'Container should have set id.');
56 | $this->assertInstanceOf($instance, $container->get($id), 'Container has wrong instance set to id.');
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Tests/Unit/Collection/CollectionTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Collection;
13 |
14 | use Doctrine\Common\Collections\ArrayCollection;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class CollectionTest extends TestCase
18 | {
19 | /**
20 | * @var array
21 | */
22 | private $data = [
23 | 'foo' => 'Bob 1',
24 | 'bar' => 'Bob 2',
25 | ];
26 |
27 | /**
28 | * Tests \Countable implementation.
29 | */
30 | public function testCountable()
31 | {
32 | $this->assertCount(count($this->data), new ArrayCollection($this->data));
33 | }
34 |
35 | /**
36 | * Tests \Iterator implementation.
37 | */
38 | public function testIterator()
39 | {
40 | $this->assertEquals($this->data, iterator_to_array(new ArrayCollection($this->data)));
41 | }
42 |
43 | /**
44 | * Tests \ArrayAccess implementation.
45 | */
46 | public function testArrayAccess()
47 | {
48 | $collection = new ArrayCollection($this->data);
49 |
50 | $this->assertArrayHasKey('foo', $collection);
51 | $this->assertEquals($this->data['foo'], $collection['foo']);
52 |
53 | $newData = $this->data;
54 | $newData['baz'] = 'Bob 3';
55 | $newData[] = 'Bob 4';
56 | $collection['baz'] = 'Bob 3';
57 | $collection[] = 'Bob 4';
58 |
59 | $this->assertEquals($newData, iterator_to_array($collection));
60 |
61 | unset($collection['baz']);
62 | $this->assertArrayNotHasKey('baz', $collection);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/EventListener/TerminateListener.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\EventListener;
13 |
14 | use ONGR\ElasticsearchBundle\Service\Manager;
15 | use Symfony\Component\DependencyInjection\Container;
16 |
17 | class TerminateListener
18 | {
19 | /**
20 | * @var Container
21 | */
22 | private $container;
23 |
24 | /**
25 | * @var array
26 | */
27 | private $managers;
28 |
29 | /**
30 | * Constructor
31 | *
32 | * @param Container $container
33 | * @param array $managers
34 | */
35 | public function __construct(Container $container, array $managers)
36 | {
37 | $this->container = $container;
38 | $this->managers = $managers;
39 | }
40 |
41 | /**
42 | * Forces commit to elasticsearch on kernel terminate
43 | */
44 | public function onKernelTerminate()
45 | {
46 | foreach ($this->managers as $key => $value) {
47 | if ($value['force_commit']) {
48 | try {
49 | $managerName = sprintf('es.manager.%s', $key);
50 |
51 | // Ignore managers who have not been initialized.
52 | if (!$this->container->initialized($managerName)) {
53 | continue;
54 | }
55 |
56 | /** @var Manager $manager */
57 | $manager = $this->container->get($managerName);
58 | } catch (\Exception $e) {
59 | continue;
60 | }
61 | $manager->commit();
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Tests/Functional/Command/DropIndexCommandTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Command;
13 |
14 | use ONGR\ElasticsearchBundle\Command\IndexDropCommand;
15 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
16 | use Symfony\Component\Console\Application;
17 | use Symfony\Component\Console\Tester\CommandTester;
18 |
19 | class DropIndexCommandTest extends AbstractElasticsearchTestCase
20 | {
21 | /**
22 | * Tests dropping index. Configuration from tests yaml.
23 | */
24 | public function testExecute()
25 | {
26 | $manager = $this->getManager();
27 |
28 | $command = new IndexDropCommand(['es.manager.default' => $this->getManager()]);
29 |
30 | $app = new Application();
31 | $app->add($command);
32 |
33 | // Does not drop index.
34 | $command = $app->find('ongr:es:index:drop');
35 | $commandTester = new CommandTester($command);
36 | $commandTester->execute(
37 | [
38 | 'command' => $command->getName(),
39 | ]
40 | );
41 | $this->assertTrue(
42 | $manager
43 | ->indexExists(),
44 | 'Index should still exist.'
45 | );
46 |
47 | // Does drop index.
48 | $commandTester->execute(
49 | [
50 | 'command' => $command->getName(),
51 | '--force' => true,
52 | ]
53 | );
54 |
55 | $this->assertFalse(
56 | $manager
57 | ->indexExists(),
58 | 'Index should be dropped.'
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Tests/Functional/Mapping/DocumentFinderTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Mapping;
13 |
14 | use ONGR\ElasticsearchBundle\Mapping\DocumentFinder;
15 | use ONGR\ElasticsearchBundle\Tests\WebTestCase;
16 | use Symfony\Component\DependencyInjection\ContainerInterface;
17 | use Symfony\Component\DependencyInjection\Container;
18 |
19 | class DocumentFinderTest extends WebTestCase
20 | {
21 | /**
22 | * Tests if document paths are returned for fixture bundle.
23 | */
24 | public function testGetBundleDocumentClasses()
25 | {
26 | $finder = new DocumentFinder($this->getClientContainer()->getParameter('kernel.bundles'));
27 | $this->assertGreaterThan(0, count($finder->getBundleDocumentClasses('TestBundle')));
28 | $this->assertEquals(0, count($finder->getBundleDocumentClasses('FrameworkBundle')));
29 | }
30 |
31 | /**
32 | * Tests if exception is thrown for unregistered bundle.
33 | */
34 | public function testGetBundleClassException()
35 | {
36 | $this->expectException(\LogicException::class);
37 | $this->expectExceptionMessage('Bundle \'NotExistingBundle\' does not exist.');
38 |
39 | $finder = new DocumentFinder($this->getClientContainer()->getParameter('kernel.bundles'));
40 | $finder->getBundleClass('NotExistingBundle');
41 | }
42 |
43 | /**
44 | * Returns service container.
45 | *
46 | * @return ContainerInterface
47 | */
48 | protected static function getClientContainer(): ContainerInterface
49 | {
50 | return static::createClient()->getContainer();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Tests/app/fixture/TestBundle/Document/User.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document;
13 |
14 | use ONGR\ElasticsearchBundle\Annotation as ES;
15 |
16 | /**
17 | * User document for testing. Type name is different than class name.
18 | *
19 | * @ES\Document(type="users")
20 | */
21 | class User
22 | {
23 | /**
24 | * @var string
25 | *
26 | * @ES\Id()
27 | */
28 | private $id;
29 |
30 | /**
31 | * @var string
32 | * @ES\Property(type="text")
33 | */
34 | private $firstName;
35 |
36 | /**
37 | * @var string
38 | * @ES\Property(type="text", name="last_name")
39 | */
40 | private $lastName;
41 |
42 | /**
43 | * @return string
44 | */
45 | public function getId()
46 | {
47 | return $this->id;
48 | }
49 |
50 | /**
51 | * @param string $id
52 | */
53 | public function setId($id)
54 | {
55 | $this->id = $id;
56 | }
57 |
58 | /**
59 | * @return string
60 | */
61 | public function getFirstName()
62 | {
63 | return $this->firstName;
64 | }
65 |
66 | /**
67 | * @param string $firstName
68 | */
69 | public function setFirstName($firstName)
70 | {
71 | $this->firstName = $firstName;
72 | }
73 |
74 | /**
75 | * @return string
76 | */
77 | public function getLastName()
78 | {
79 | return $this->lastName;
80 | }
81 |
82 | /**
83 | * @param string $lastName
84 | */
85 | public function setLastName($lastName)
86 | {
87 | $this->lastName = $lastName;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Event/BulkEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Event;
13 |
14 | class BulkEvent extends BaseEvent
15 | {
16 | /**
17 | * @var string
18 | */
19 | private $operation;
20 |
21 | /**
22 | * @var string|array
23 | */
24 | private $type;
25 |
26 | /**
27 | * @var array
28 | */
29 | private $query;
30 |
31 | /**
32 | * @param string $operation
33 | * @param string|array $type
34 | * @param array $query
35 | */
36 | public function __construct($operation, $type, array $query)
37 | {
38 | $this->type = $type;
39 | $this->query = $query;
40 | $this->operation = $operation;
41 | }
42 |
43 | /**
44 | * @return array|string
45 | */
46 | public function getType()
47 | {
48 | return $this->type;
49 | }
50 |
51 | /**
52 | * @param array|string $type
53 | */
54 | public function setType($type)
55 | {
56 | $this->type = $type;
57 | }
58 |
59 | /**
60 | * @return array
61 | */
62 | public function getQuery()
63 | {
64 | return $this->query;
65 | }
66 |
67 | /**
68 | * @param array $query
69 | */
70 | public function setQuery($query)
71 | {
72 | $this->query = $query;
73 | }
74 |
75 | /**
76 | * @return array
77 | */
78 | public function getOperation()
79 | {
80 | return $this->operation;
81 | }
82 |
83 | /**
84 | * @param string $operation
85 | */
86 | public function setOperation($operation)
87 | {
88 | $this->operation = $operation;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Tests/Unit/EventListener/TerminateListenerTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\EventListener;
13 |
14 | use ONGR\ElasticsearchBundle\EventListener\TerminateListener;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | /**
18 | * Tests TerminateListener class
19 | */
20 | class TerminateListenerTest extends TestCase
21 | {
22 | /**
23 | * Tests kernel terminate event
24 | */
25 | public function testKernelTerminate()
26 | {
27 | $manager = $this->getMockBuilder('ONGR\ElasticsearchBundle\Service\Manager')
28 | ->disableOriginalConstructor()
29 | ->getMock();
30 |
31 | $manager->expects($this->once())
32 | ->method('commit');
33 |
34 | $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\Container')
35 | ->disableOriginalConstructor()
36 | ->getMock();
37 |
38 | $container->expects($this->any())
39 | ->method('initialized')
40 | ->with('es.manager.test_available')
41 | ->willReturn(true);
42 |
43 | $container->expects($this->any())
44 | ->method('get')
45 | ->with('es.manager.test_available')
46 | ->willReturn($manager);
47 |
48 | $listener = new TerminateListener(
49 | $container,
50 | [
51 | 'test_available' => [
52 | 'force_commit' => true,
53 | ],
54 | 'test_unavailable' => [
55 | 'force_commit' => true,
56 | ],
57 | ]
58 | );
59 |
60 | $listener->onKernelTerminate();
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Resources/doc/scan.md:
--------------------------------------------------------------------------------
1 | # Scan through the index
2 |
3 | If the index is huge and in a single request there is not enough to get all records index scan might help.
4 |
5 | > More info about index scanning in [elastic official docs](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html#scroll-scan)
6 |
7 | You can scan index with any structured search and do a continuous scroll. To execute that kind of search you only need to append a scroll time amount to a `Search` object.
8 |
9 | > Scan & Scroll doesn't work for `Repository::findArray()` method.
10 |
11 | Here's an example with scrolling:
12 |
13 | ```php
14 |
15 | $repo = $this->get('es.manager.default.city');
16 | $search = $repo->createSearch();
17 |
18 | $search->setScroll('10m'); // Scroll time
19 |
20 | $termQuery = new TermQuery('country', 'Lithuania');
21 | $search->addQuery($termQuery);
22 |
23 | $results = $repo->findDocuments($search);
24 |
25 | foreach ($results as $document) {
26 |
27 | //....
28 |
29 | }
30 |
31 | ```
32 |
33 | Usually result amount will be 10 (if no size set), but if the result type is any iterator, it will do a next request when the results set limit is reached and will continue results scrolling in foreach cycle. You dont have to worry about any scroll ids and how to perform second request, everything is handled automatically.
34 |
35 | If you are using `findRaw()` method, then bundle won't request next iteration and you should do it by yourself. Here's an example how to do it:
36 |
37 |
38 | ```php
39 |
40 | $repo = $this->get('es.manager.default.city');
41 | $search = $repo->createSearch();
42 |
43 | $search->setScroll('10m'); // Scroll time
44 |
45 | $termQuery = new TermQuery('country', 'Lithuania');
46 | $search->addQuery($termQuery);
47 |
48 | $results = $repo->findRaw($search);
49 |
50 | foreach ($results as $raw) {
51 |
52 | // Do something with RAW results
53 |
54 | }
55 |
56 | $nextIteration = $repo->getManager()->scroll($results['_scroll_id'], '10m');
57 |
58 | ```
59 |
--------------------------------------------------------------------------------
/Tests/app/fixture/TestBundle/Entity/Product.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Entity;
13 |
14 | use Doctrine\Common\Collections\ArrayCollection;
15 | use ONGR\ElasticsearchBundle\Annotation as ES;
16 |
17 | /**
18 | * Product document for testing.
19 | *
20 | * @ES\Document()
21 | */
22 | class Product
23 | {
24 | /**
25 | * @var string
26 | *
27 | * @ES\Id()
28 | */
29 | public $id;
30 |
31 | /**
32 | * @var string
33 | * @ES\Property(type="keyword", name="title")
34 | */
35 | public $title;
36 |
37 | /**
38 | * @var CategoryObject[]
39 | *
40 | * @ES\Embedded(class="TestBundle:CategoryObject", multiple=true)
41 | */
42 | public $categories;
43 |
44 | public function __construct()
45 | {
46 | $this->categories = new ArrayCollection();
47 | }
48 |
49 | /**
50 | * @return string
51 | */
52 | public function getId()
53 | {
54 | return $this->id;
55 | }
56 |
57 | /**
58 | * @param string $id
59 | */
60 | public function setId($id)
61 | {
62 | $this->id = $id;
63 | }
64 |
65 | /**
66 | * @return string
67 | */
68 | public function getTitle()
69 | {
70 | return $this->title;
71 | }
72 |
73 | /**
74 | * @param string $title
75 | */
76 | public function setTitle($title)
77 | {
78 | $this->title = $title;
79 | }
80 |
81 | /**
82 | * @return CategoryObject[]
83 | */
84 | public function getCategories()
85 | {
86 | return $this->categories;
87 | }
88 |
89 | /**
90 | * @param CategoryObject[] $categories
91 | */
92 | public function setCategories($categories)
93 | {
94 | $this->categories = $categories;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Command/IndexDropCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Command;
13 |
14 | use Symfony\Component\Console\Input\InputInterface;
15 | use Symfony\Component\Console\Input\InputOption;
16 | use Symfony\Component\Console\Output\OutputInterface;
17 | use Symfony\Component\Console\Style\SymfonyStyle;
18 |
19 | /**
20 | * Command for dropping Elasticsearch index.
21 | */
22 | class IndexDropCommand extends AbstractManagerAwareCommand
23 | {
24 | public static $defaultName = 'ongr:es:index:drop';
25 |
26 | /**
27 | * {@inheritdoc}
28 | */
29 | protected function configure(): void
30 | {
31 | parent::configure();
32 |
33 | $this
34 | ->setName(static::$defaultName)
35 | ->setDescription('Drops elasticsearch index.')
36 | ->addOption(
37 | 'force',
38 | 'f',
39 | InputOption::VALUE_NONE,
40 | 'Set this parameter to execute this command'
41 | );
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | protected function execute(InputInterface $input, OutputInterface $output): int
48 | {
49 | $io = new SymfonyStyle($input, $output);
50 | if ($input->getOption('force')) {
51 | $this->getManager($input->getOption('manager'))->dropIndex();
52 |
53 | $io->text(
54 | sprintf(
55 | 'Dropped index for the `%s` manager',
56 | $input->getOption('manager')
57 | )
58 | );
59 | } else {
60 | $io->error('ATTENTION:');
61 | $io->text('This action should not be used in the production environment.');
62 | $io->error('Option --force is mandatory to drop type(s).');
63 | }
64 |
65 | return 0;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Tests/Unit/Result/RawIteratorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Result;
13 |
14 | use ONGR\ElasticsearchBundle\Result\RawIterator;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class RawIteratorTest extends TestCase
18 | {
19 | /**
20 | * Test for getAggregations().
21 | */
22 | public function testGetAggregations()
23 | {
24 | $rawData = [
25 | 'aggregations' => [
26 | 'avg_grade' => [
27 | 'value' => 75,
28 | ],
29 | ],
30 | ];
31 |
32 | $manager = $this->getMockBuilder('ONGR\ElasticsearchBundle\Service\Manager')
33 | ->disableOriginalConstructor()
34 | ->getMock();
35 |
36 | $iterator = new RawIterator($rawData, $manager);
37 |
38 | $this->assertEquals($rawData['aggregations'], $iterator->getAggregations());
39 | }
40 |
41 | /**
42 | * Tests iterator.
43 | */
44 | public function testIterator()
45 | {
46 | $rawData = [
47 | 'hits' => [
48 | 'total' => 1,
49 | 'hits' => [
50 | [
51 | '_index' => 'test',
52 | '_type' => 'product',
53 | '_id' => 'foo',
54 | '_score' => 1,
55 | '_source' => [
56 | 'title' => 'Product Foo',
57 | ],
58 | ],
59 | ],
60 | ],
61 | ];
62 |
63 | $manager = $this->getMockBuilder('ONGR\ElasticsearchBundle\Service\Manager')
64 | ->disableOriginalConstructor()
65 | ->getMock();
66 |
67 | $iterator = new RawIterator($rawData, $manager);
68 |
69 | $this->assertEquals($rawData['hits']['hits'][0], $iterator->current());
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Service/GenerateService.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Service;
13 |
14 | use ONGR\ElasticsearchBundle\Generator\DocumentGenerator;
15 | use Symfony\Component\Filesystem\Filesystem;
16 | use Symfony\Component\HttpKernel\Bundle\BundleInterface;
17 |
18 | /**
19 | * Generate Service
20 | */
21 | class GenerateService
22 | {
23 | /**
24 | * @var DocumentGenerator
25 | */
26 | private $generator;
27 |
28 | /**
29 | * @var Filesystem
30 | */
31 | private $filesystem;
32 |
33 | /**
34 | * Constructor
35 | *
36 | * @param DocumentGenerator $generator
37 | * @param Filesystem $filesystem
38 | */
39 | public function __construct(DocumentGenerator $generator, Filesystem $filesystem)
40 | {
41 | $this->generator = $generator;
42 | $this->filesystem = $filesystem;
43 | }
44 |
45 | /**
46 | * Generates document class
47 | *
48 | * @param BundleInterface $bundle
49 | * @param string $document
50 | * @param string $annotation
51 | * @param string $type
52 | * @param array $properties
53 | */
54 | public function generate(
55 | BundleInterface $bundle,
56 | $document,
57 | $annotation,
58 | $type,
59 | array $properties
60 | ) {
61 | $documentPath = $bundle->getPath() . '/Document/' . str_replace('\\', '/', $document) . '.php';
62 | $class = [
63 | 'name' => $bundle->getNamespace() . '\\Document\\' . $document,
64 | 'annotation' => $annotation,
65 | 'type' => $type,
66 | 'properties' => $properties,
67 | ];
68 |
69 | $documentCode = $this->generator->generateDocumentClass($class);
70 |
71 | $this->filesystem->mkdir(dirname($documentPath));
72 | file_put_contents($documentPath, $documentCode);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Annotation/Embedded.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Annotation;
13 |
14 | use ONGR\ElasticsearchBundle\Mapping\Caser;
15 |
16 | /**
17 | * Annotation for property which points to inner object.
18 | *
19 | * @Annotation
20 | * @Target("PROPERTY")
21 | */
22 | final class Embedded
23 | {
24 | /**
25 | * Inner object class name.
26 | *
27 | * @var string Object name to map
28 | *
29 | * @Doctrine\Common\Annotations\Annotation\Required
30 | */
31 | public $class;
32 |
33 | /**
34 | * Name of the type field. Defaults to normalized property name.
35 | *
36 | * @var string
37 | */
38 | public $name;
39 |
40 | /**
41 | * Defines if related value will store a single object or an array of objects
42 | *
43 | * If this value is set to true, in the result ObjectIterator will be provided,
44 | * otherwise you will get single object.
45 | *
46 | * @var bool Object or ObjectIterator
47 | */
48 | public $multiple;
49 |
50 | /**
51 | * In this field you can define options.
52 | *
53 | * @var array
54 | */
55 | public $options;
56 |
57 | /**
58 | * {@inheritdoc}
59 | */
60 | public function dump(array $exclude = [])
61 | {
62 | $array = array_diff_key(
63 | array_filter(
64 | get_object_vars($this),
65 | function ($value) {
66 | return $value || is_bool($value);
67 | }
68 | ),
69 | array_flip(array_merge(['class', 'name', 'multiple'], $exclude))
70 | );
71 |
72 | return array_combine(
73 | array_map(
74 | function ($key) {
75 | return Caser::snake($key);
76 | },
77 | array_keys($array)
78 | ),
79 | array_values($array)
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Mapping/Caser.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Mapping;
13 |
14 | use Doctrine\Inflector\Inflector;
15 | use Doctrine\Inflector\InflectorFactory;
16 |
17 | /**
18 | * Utility for string case transformations.
19 | */
20 | class Caser
21 | {
22 | /**
23 | * @var Inflector
24 | */
25 | private static $inflector;
26 |
27 | /**
28 | * Transforms string to camel case (e.g., resultString).
29 | *
30 | * @param string $string Text to transform.
31 | *
32 | * @return string
33 | */
34 | public static function camel($string)
35 | {
36 | $inflector = static::getInflector();
37 |
38 | return $inflector->camelize($string);
39 | }
40 |
41 | /**
42 | * Transforms string to snake case (e.g., result_string).
43 | *
44 | * @param string $string Text to transform.
45 | *
46 | * @return string
47 | */
48 | public static function snake($string)
49 | {
50 | $string = preg_replace('#([A-Z\d]+)([A-Z][a-z])#', '\1_\2', self::camel($string));
51 | $string = preg_replace('#([a-z\d])([A-Z])#', '\1_\2', $string);
52 |
53 | return strtolower(strtr($string, '-', '_'));
54 | }
55 |
56 | /**
57 | * @return Inflector
58 | */
59 | private static function getInflector()
60 | {
61 | if (static::$inflector === null) {
62 | if (\class_exists(InflectorFactory::class)) {
63 | static::$inflector = InflectorFactory::create()->build();
64 | } else {
65 | @trigger_error(
66 | 'Using the old inflector is deprecated please upgrade the "doctrine/inflector" package.',
67 | E_USER_DEPRECATED
68 | );
69 |
70 | static::$inflector = new \Doctrine\Common\Inflector\Inflector();
71 | }
72 | }
73 |
74 | return static::$inflector;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Tests/Unit/Service/ManagerFactoryTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Service;
13 |
14 | use ONGR\ElasticsearchBundle\Service\ManagerFactory;
15 | use ONGR\ElasticsearchBundle\Service\Manager;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | class ManagerFactoryTest extends TestCase
19 | {
20 | /**
21 | * Tests createManager with logger
22 | */
23 | public function testCreateManagerWithEnabledLogger()
24 | {
25 | $metadataCollector = $this->getMockBuilder('ONGR\ElasticsearchBundle\Mapping\MetadataCollector')
26 | ->disableOriginalConstructor()
27 | ->getMock();
28 | $metadataCollector->expects($this->any())->method('getClientMapping')->will($this->returnValue([]));
29 | $converter = $this->getMockBuilder('ONGR\ElasticsearchBundle\Result\Converter')
30 | ->disableOriginalConstructor()
31 | ->getMock();
32 |
33 | $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher')
34 | ->disableOriginalConstructor()
35 | ->getMock();
36 |
37 | $logger = $this->createMock('Psr\Log\LoggerInterface');
38 | $managerFactory = new ManagerFactory(
39 | $metadataCollector,
40 | $converter,
41 | $logger,
42 | $logger
43 | );
44 |
45 | $managerFactory->setEventDispatcher($dispatcher);
46 |
47 | $manager = $managerFactory->createManager(
48 | 'test',
49 | [
50 | 'index_name' => 'test',
51 | 'settings' => [],
52 | 'hosts' => []
53 | ],
54 | [],
55 | [
56 | 'mappings' => [],
57 | 'logger' => [
58 | 'enabled' => true
59 | ],
60 | 'commit_mode' => 'flush',
61 | 'bulk_size' => 10
62 | ]
63 | );
64 | $this->assertTrue($manager instanceof Manager);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Tests/Functional/Result/GetDocumentSortTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Result;
13 |
14 | use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
15 | use ONGR\ElasticsearchDSL\Sort\FieldSort;
16 | use ONGR\ElasticsearchBundle\Service\Repository;
17 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
18 |
19 | class GetDocumentSortTest extends AbstractElasticsearchTestCase
20 | {
21 | /**
22 | * {@inheritdoc}
23 | */
24 | protected function getDataArray()
25 | {
26 | return [
27 | 'default' => [
28 | 'product' => [
29 | [
30 | '_id' => 'doc1',
31 | 'title' => 'Foo Product',
32 | 'price' => 5.00,
33 | ],
34 | [
35 | '_id' => 'doc2',
36 | 'title' => 'Bar Product',
37 | 'price' => 8.33,
38 | ],
39 | [
40 | '_id' => 'doc3',
41 | 'title' => 'Lao Product',
42 | 'price' => 1.95,
43 | ],
44 | ],
45 | ],
46 | ];
47 | }
48 |
49 | /**
50 | * GetDocumentSort test.
51 | */
52 | public function testGetDocumentSort()
53 | {
54 | /** @var Repository $repo */
55 | $repo = $this->getManager()->getRepository('TestBundle:Product');
56 | $match = new MatchAllQuery();
57 | $sort = new FieldSort('price', 'asc');
58 | $search = $repo->createSearch()->addQuery($match);
59 | $search->addSort($sort);
60 | $results = $repo->findDocuments($search);
61 | $sort_result = [];
62 | $expected = [1.95, 5, 8.33];
63 |
64 | foreach ($results as $result) {
65 | $sort_result[] = $results->getDocumentSort();
66 | }
67 |
68 | $this->assertEquals($sort_result, $expected);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Command/AbstractManagerAwareCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Command;
13 |
14 | use ONGR\ElasticsearchBundle\Service\Manager;
15 | use Symfony\Component\Console\Command\Command;
16 | use Symfony\Component\Console\Input\InputOption;
17 |
18 | /**
19 | * AbstractElasticsearchCommand class.
20 | */
21 | abstract class AbstractManagerAwareCommand extends Command
22 | {
23 | /**
24 | * @var Manager[]
25 | */
26 | protected $managers;
27 |
28 | public function __construct(array $managers, $name = null)
29 | {
30 | parent::__construct($name);
31 | $this->managers = $managers;
32 | }
33 |
34 | /**
35 | * {@inheritdoc}
36 | */
37 | protected function configure(): void
38 | {
39 | $this->addOption(
40 | 'manager',
41 | 'm',
42 | InputOption::VALUE_REQUIRED,
43 | 'Manager name',
44 | 'default'
45 | );
46 | }
47 |
48 | /**
49 | * Returns elasticsearch manager by name from service container.
50 | *
51 | * @param string $name Manager name defined in configuration.
52 | *
53 | * @return Manager
54 | *
55 | * @throws \RuntimeException If manager was not found.
56 | */
57 | protected function getManager($name)
58 | {
59 | $id = $this->getManagerId($name);
60 |
61 | if (array_key_exists($id, $this->managers)) {
62 | return $this->managers[$id];
63 | }
64 |
65 | throw new \RuntimeException(
66 | sprintf(
67 | 'Manager named `%s` not found. Available: `%s`.',
68 | $name,
69 | implode('`, `', array_keys($this->managers))
70 | )
71 | );
72 | }
73 |
74 | /**
75 | * Formats manager service id from its name.
76 | *
77 | * @param string $name Manager name.
78 | *
79 | * @return string Service id.
80 | */
81 | private function getManagerId($name)
82 | {
83 | return sprintf('es.manager.%s', $name);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/DependencyInjection/Compiler/MappingPass.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\DependencyInjection\Compiler;
13 |
14 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15 | use Symfony\Component\DependencyInjection\ContainerBuilder;
16 | use Symfony\Component\DependencyInjection\Definition;
17 | use Symfony\Component\DependencyInjection\Reference;
18 | use Symfony\Component\HttpKernel\Kernel;
19 |
20 | /**
21 | * Compiles elastic search data.
22 | */
23 | class MappingPass implements CompilerPassInterface
24 | {
25 | /**
26 | * {@inheritdoc}
27 | */
28 | public function process(ContainerBuilder $container)
29 | {
30 | $analysis = $container->getParameter('es.analysis');
31 | $managers = $container->getParameter('es.managers');
32 |
33 | foreach ($managers as $managerName => $manager) {
34 | $connection = $manager['index'];
35 | $managerName = strtolower($managerName);
36 |
37 | $managerDefinition = new Definition(
38 | 'ONGR\ElasticsearchBundle\Service\Manager',
39 | [
40 | $managerName,
41 | $connection,
42 | $analysis,
43 | $manager,
44 | ]
45 | );
46 | $managerDefinition->setPublic(true);
47 | $managerDefinition->setFactory(
48 | [
49 | new Reference('es.manager_factory'),
50 | 'createManager',
51 | ]
52 | );
53 | $managerKey = sprintf('es.manager.%s', $managerName);
54 | $managerDefinition->addTag('es.manager', ['key' => $managerKey]);
55 | $container->setDefinition($managerKey, $managerDefinition);
56 |
57 | // Make es.manager.default as es.manager service.
58 | if ($managerName === 'default') {
59 | $container->setAlias('es.manager', 'es.manager.default');
60 |
61 | if (Kernel::MAJOR_VERSION >= 4) {
62 | $container->getAlias('es.manager')
63 | ->setPublic(true);
64 | }
65 | }
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Tests/Unit/ElasticsearchBundleTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit;
13 |
14 | use ONGR\ElasticsearchBundle\ONGRElasticsearchBundle;
15 | use PHPUnit\Framework\TestCase;
16 | use Symfony\Component\DependencyInjection\Compiler\PassConfig;
17 | use Symfony\Component\DependencyInjection\ContainerBuilder;
18 | use Symfony\Component\Finder\Finder;
19 | use Symfony\Component\Finder\SplFileInfo;
20 |
21 | /**
22 | * Unit test for ONGR\ElasticsearchBundle.
23 | */
24 | class ElasticsearchBundleTest extends TestCase
25 | {
26 | /**
27 | * @var array List of passes, which should not be added to compiler.
28 | */
29 | protected $passesBlacklist = [];
30 |
31 | /**
32 | * Check whether all Passes in DependencyInjection/Compiler/ are added to container.
33 | */
34 | public function testPassesRegistered()
35 | {
36 | $container = new ContainerBuilder();
37 | $bundle = new ONGRElasticsearchBundle();
38 | $bundle->build($container);
39 |
40 | /** @var array $loadedPasses Array of class names of loaded passes */
41 | $loadedPasses = [];
42 | /** @var PassConfig $passConfig */
43 | $passConfig = $container->getCompiler()->getPassConfig();
44 | foreach ($passConfig->getPasses() as $pass) {
45 | $classPath = explode('\\', get_class($pass));
46 | $loadedPasses[] = end($classPath);
47 | }
48 |
49 | $finder = new Finder();
50 | $finder->files()->in(__DIR__ . '/../../DependencyInjection/Compiler/');
51 |
52 | /** @var $file SplFileInfo */
53 | foreach ($finder as $file) {
54 | $passName = str_replace('.php', '', $file->getFilename());
55 | // Check whether pass is not blacklisted and not added by bundle.
56 | if (!in_array($passName, $this->passesBlacklist)) {
57 | $this->assertContains(
58 | $passName,
59 | $loadedPasses,
60 | sprintf(
61 | "Compiler pass '%s' is not added to container or not blacklisted in test.",
62 | $passName
63 | )
64 | );
65 | }
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Resources/doc/meta_fields.md:
--------------------------------------------------------------------------------
1 | Using Elasticsearch Meta-Fields
2 | ===
3 |
4 | This page is about document metadata also known as meta-fields. For detailed
5 | explanation on what it is and how it works read official Elasticsearch
6 | [documentation][1]. Below we explain how to use meta-fields supported
7 | by this bundle.
8 |
9 | Supported Meta-Fields
10 | ---
11 |
12 | ### @Id (_id)
13 |
14 | None of meta-fields is mandatory, but probably you will be using `_id` more than
15 | others. This meta-field works both ways. When you read document from Elasticsearch
16 | you get document's ID in associated property. Anytime you want to change document's ID
17 | or set custom ID for new document, just set value to this property and it will be
18 | stored in Elasticsearch. `_id` meta-field is represented by `@Id` annotation:
19 |
20 | ```php
21 | use ONGR\ElasticsearchBundle\Annotation as ES;
22 |
23 | /**
24 | * @ES\Document()
25 | */
26 | class Person
27 | {
28 | /**
29 | * @ES\Id()
30 | */
31 | public $id;
32 |
33 | // ...
34 | }
35 | ```
36 |
37 | ### @ParentDocument (_parent)
38 |
39 | `_parent` meta-field is used to create a parent-child relationship between two mapping
40 | types. It is represented by `@ParentDocument` annotation. The only one and required
41 | attribute is `class`, where you need to specify class of parent document.
42 |
43 | ```php
44 | /**
45 | * @ES\ParentDocument(class="AppBundle:Family")
46 | */
47 | public $parent;
48 | ```
49 |
50 | ### @Routing (_routing)
51 |
52 | Custom routing patterns can be implemented by specifying a custom routing value per document.
53 | The same routing value needs to be provided when getting, deleting, or updating the document.
54 | It is represented by the `@Routing` annotation. Here is an example of such a field:
55 |
56 | ```php
57 | /**
58 | * @ES\Routing()
59 | */
60 | public $routing;
61 | ```
62 |
63 | Forgetting the routing value can lead to a document being indexed on more than one shard.
64 | As a safeguard, the _routing field can be configured to make a custom routing value
65 | required for all CRUD operations. This can be implemented by setting the `equired`
66 | attribute to true (`@ES\Routing(required=true)`).
67 |
68 | More information on routing can be found in the [dedicated docs][2]
69 |
70 | [1]: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-fields.html
71 | [2]: https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-routing-field.html
72 |
--------------------------------------------------------------------------------
/Tests/Unit/Mapping/MetadataCollectorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Mapping;
13 |
14 | use Doctrine\Common\Cache\CacheProvider;
15 | use ONGR\ElasticsearchBundle\Mapping\DocumentFinder;
16 | use ONGR\ElasticsearchBundle\Mapping\DocumentParser;
17 | use ONGR\ElasticsearchBundle\Mapping\MetadataCollector;
18 | use PHPUnit\Framework\TestCase;
19 |
20 | class MetadataCollectorTest extends TestCase
21 | {
22 | /**
23 | * @var MetadataCollector
24 | */
25 | private $metadataCollector;
26 |
27 | /**
28 | * @var DocumentFinder
29 | */
30 | private $docFinder;
31 |
32 | /**
33 | * @var DocumentParser
34 | */
35 | private $docParser;
36 |
37 | /**
38 | * @var CacheProvider
39 | */
40 | private $cache;
41 |
42 | /**
43 | * Initialize MetadataCollector.
44 | */
45 | protected function setUp(): void
46 | {
47 | $this->docFinder = $this->getMockBuilder('ONGR\ElasticsearchBundle\Mapping\DocumentFinder')
48 | ->disableOriginalConstructor()
49 | ->getMock();
50 |
51 | $this->docParser = $this->getMockBuilder('ONGR\ElasticsearchBundle\Mapping\DocumentParser')
52 | ->disableOriginalConstructor()
53 | ->getMock();
54 |
55 | $this->cache = $this->getMockBuilder('Doctrine\Common\Cache\FilesystemCache')
56 | ->disableOriginalConstructor()
57 | ->getMock();
58 |
59 | $this->metadataCollector = new MetadataCollector($this->docFinder, $this->docParser, $this->cache);
60 | }
61 |
62 | /**
63 | * Test bundle mapping parser when requesting non string bundle name.
64 | */
65 | public function testGetBundleMappingWithNotStringName()
66 | {
67 | $this->expectException(\LogicException::class);
68 | $this->expectExceptionMessage('getBundleMapping() in the Metadata collector expects a string argument only!');
69 |
70 | $this->metadataCollector->getBundleMapping(1000);
71 | }
72 |
73 | /**
74 | * Test for getClientMapping() in case no mapping exists.
75 | */
76 | public function testGetClientMappingNull()
77 | {
78 | $this->assertNull($this->metadataCollector->getClientMapping([]));
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "handcraftedinthealps/elasticsearch-bundle",
3 | "description": "Elasticsearch bundle for Symfony.",
4 | "type": "symfony-bundle",
5 | "homepage": "http://ongr.io",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "ONGR team",
10 | "homepage": "http://www.ongr.io"
11 | },
12 | {
13 | "name": "Handcrafted in the Alps Team",
14 | "homepage": "https://github.com/handcraftedinthealps/ElasticsearchBundle/graphs/contributors"
15 | }
16 | ],
17 | "require": {
18 | "php": "^7.2|^8.0",
19 | "symfony/framework-bundle": "^3.4|^4|^5|^6|^7",
20 | "symfony/console": "^3.4|^4|^5|^6|^7",
21 | "symfony/stopwatch": "^3.4|^4|^5|^6|^7",
22 | "symfony/templating": "^3.4|^4|^5|^6|^7",
23 | "symfony/asset": "^3.4|^4|^5|^6|^7",
24 | "doctrine/annotations": "^1.13 || ^2.0",
25 | "doctrine/inflector": "^1.0 || ^2.0",
26 | "doctrine/collections": "^1.4|^2.0",
27 | "monolog/monolog": "^1.10 || ^2.0 || ^3.0",
28 | "handcraftedinthealps/elasticsearch-dsl": "^5.0.7.1|^6.2.0.1|^7.2.0.1",
29 | "symfony/event-dispatcher": "^3.4|^4|^5|^6|^7"
30 | },
31 | "require-dev": {
32 | "mikey179/vfsstream": "~1.4",
33 | "squizlabs/php_codesniffer": "^2.0|^3.0",
34 | "symfony/browser-kit" : "^3.4|^4|^5|^6|^7",
35 | "symfony/expression-language" : "^3.4|^4|^5|^6|^7",
36 | "symfony/twig-bundle": "^3.4|^4|^5|^6|^7",
37 | "symfony/serializer": "^3.4|^4|^5|^6|^7",
38 | "symfony/yaml": "^3.4|^4|^5|^6|^7",
39 | "symfony/phpunit-bridge": "^5.1|^6|^7",
40 | "symfony/dependency-injection": "^3.4|^4|^5|^6|^7",
41 | "symfony/validator": "^3.4|^4|^5|^6|^7",
42 | "symfony/options-resolver": "^3.4|^4|^5|^6|^7"
43 | },
44 | "autoload": {
45 | "psr-4": { "ONGR\\ElasticsearchBundle\\": "" },
46 | "exclude-from-classmap": ["/Tests/", "/Test/"]
47 | },
48 | "conflict": {
49 | "handcraftedinthealps/elasticsearch-dsl": "6.0 - 6.1.1 || 7.0 - 7.1.1",
50 | "ongr/elasticsearch-dsl": "6.0 - 6.1.1 || 7.0 - 7.1.1"
51 | },
52 | "replace": {
53 | "ongr/elasticsearch-bundle": "self.version"
54 | },
55 | "minimum-stability": "dev",
56 | "extra": {
57 | "symfony": {
58 | "recipe-contrib": false,
59 | "allow-contrib": false,
60 | "require": "7.1.*"
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Tests/Unit/Service/RepositoryTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Service;
13 |
14 | use ONGR\ElasticsearchBundle\Service\Repository;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class RepositoryTest extends TestCase
18 | {
19 | /**
20 | * Data provider for testConstructorException().
21 | *
22 | * @return array
23 | */
24 | public function getTestConstructorExceptionData()
25 | {
26 | return [
27 | [
28 | 12345,
29 | '\InvalidArgumentException',
30 | 'must be a string',
31 | ],
32 | [
33 | 'Non\Existing\ClassName',
34 | '\InvalidArgumentException',
35 | 'non-existing class',
36 | ],
37 | ];
38 | }
39 |
40 | /**
41 | * @param $className
42 | * @param $expectedException
43 | * @param $expectedExceptionMessage
44 | *
45 | * @dataProvider getTestConstructorExceptionData()
46 | */
47 | public function testConstructorException($className, $expectedException, $expectedExceptionMessage)
48 | {
49 | $this->expectException($expectedException, $expectedExceptionMessage);
50 |
51 | new Repository(null, $className);
52 | }
53 |
54 | /**
55 | * Tests class getter
56 | */
57 | public function testGetRepositoryClass()
58 | {
59 | $collector = $this->getMockBuilder('ONGR\ElasticsearchBundle\Mapping\MetadataCollector')
60 | ->disableOriginalConstructor()
61 | ->getMock();
62 | $collector->expects($this->any())->method('getDocumentType')->willReturn('product');
63 | $manager = $this->getMockBuilder('ONGR\ElasticsearchBundle\Service\Manager')
64 | ->disableOriginalConstructor()
65 | ->getMock();
66 | $manager->expects($this->any())->method('getMetadataCollector')->willReturn($collector);
67 | $repository = new Repository(
68 | $manager,
69 | 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product'
70 | );
71 | $this->assertEquals(
72 | 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product',
73 | $repository->getClassName()
74 | );
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Tests/Functional/Command/CacheClearCommandTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Command;
13 |
14 | use ONGR\ElasticsearchBundle\Command\CacheClearCommand;
15 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
16 | use Symfony\Component\Console\Application;
17 | use Symfony\Component\Console\Tester\CommandTester;
18 |
19 | class CacheClearCommandTest extends AbstractElasticsearchTestCase
20 | {
21 |
22 | const COMMAND_NAME = 'ongr:es:cache:clear';
23 |
24 | /**
25 | * {@inheritdoc}
26 | */
27 | protected function getDataArray()
28 | {
29 | return ['default' => []];
30 | }
31 |
32 | /**
33 | * Tests if command is being executed.
34 | */
35 | public function testExecute()
36 | {
37 | $this->getManager();
38 |
39 | $app = new Application();
40 | $app->add($this->getCommand());
41 | $command = $app->find(self::COMMAND_NAME);
42 | $tester = new CommandTester($command);
43 | $tester->execute(
44 | [
45 | 'command' => $command->getName(),
46 | ]
47 | );
48 |
49 | $this->assertStringContainsString(
50 | 'Elasticsearch index cache has been cleared for manager named `default`',
51 | $tester->getDisplay()
52 | );
53 | $this->assertEquals(0, $tester->getStatusCode(), 'Status code should be zero.');
54 | }
55 |
56 | /**
57 | * Tests if exception is thown when no manager is found.
58 | */
59 | public function testExecuteException()
60 | {
61 | $this->expectException(\RuntimeException::class);
62 |
63 | $app = new Application();
64 | $app->add($this->getCommand());
65 | $command = $app->find('ongr:es:cache:clear');
66 | $tester = new CommandTester($command);
67 | $tester->execute(
68 | [
69 | 'command' => $command->getName(),
70 | '--manager' => 'notexistingmanager',
71 | ]
72 | );
73 | }
74 |
75 | /**
76 | * Returns cache clear command instance.
77 | *
78 | * @return CacheClearCommand
79 | */
80 | private function getCommand()
81 | {
82 | return new CacheClearCommand(['es.manager.default' => $this->getManager()]);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Tests/Unit/Service/GenerateServiceTest.php:
--------------------------------------------------------------------------------
1 | tmpDir = sys_get_temp_dir() . '/ongr';
28 |
29 | $this->filesystem = new Filesystem();
30 | $this->filesystem->remove($this->tmpDir);
31 | }
32 |
33 | /**
34 | * {@inheritdoc}
35 | */
36 | public function doTearDown()
37 | {
38 | $this->filesystem->remove($this->tmpDir);
39 | }
40 |
41 | public function testGenerate()
42 | {
43 | $service = new GenerateService(new DocumentGenerator(), $this->filesystem);
44 |
45 | $service->generate(
46 | $this->getBundle(),
47 | 'Foo',
48 | 'Document',
49 | 'foo',
50 | [
51 | [
52 | 'field_name' => 'test',
53 | 'annotation' => 'property',
54 | 'visibility' => 'private',
55 | 'property_type' => 'string',
56 | 'property_name' => 'testProperty',
57 | 'property_options' => 'test',
58 | ],
59 | [
60 | 'field_name' => 'embedded',
61 | 'visibility' => 'protected',
62 | 'annotation' => 'embedded',
63 | 'property_class' => 'TestBundle:Product',
64 | 'property_multiple' => true,
65 | ]
66 | ]
67 | );
68 |
69 | $this->assertFileExists($this->getBundle()->getPath() . '/Document/Foo.php');
70 | }
71 |
72 | /**
73 | * @return \PHPUnit_Framework_MockObject_MockObject
74 | */
75 | private function getBundle()
76 | {
77 | $bundle = $this->createMock('Symfony\Component\HttpKernel\Bundle\BundleInterface');
78 | $bundle->expects($this->any())->method('getPath')->will($this->returnValue($this->tmpDir));
79 | $bundle->expects($this->any())->method('getName')->will($this->returnValue('FooBarBundle'));
80 | $bundle->expects($this->any())->method('getNamespace')->will($this->returnValue('Foo\BarBundle'));
81 |
82 | return $bundle;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Service/ImportService.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Service;
13 |
14 | use ONGR\ElasticsearchBundle\Service\Json\JsonReader;
15 | use Symfony\Component\Console\Helper\ProgressBar;
16 | use Symfony\Component\Console\Output\OutputInterface;
17 |
18 | /**
19 | * ImportService class.
20 | */
21 | class ImportService
22 | {
23 | /**
24 | * Imports Elasticsearch index data.
25 | *
26 | * @param Manager $manager
27 | * @param string $filename
28 | * @param OutputInterface $output
29 | * @param array $options
30 | */
31 | public function importIndex(
32 | Manager $manager,
33 | $filename,
34 | OutputInterface $output,
35 | $options
36 | ) {
37 | $reader = $this->getReader($manager, $this->getFilePath($filename), $options);
38 |
39 | $progress = new ProgressBar($output, $reader->count());
40 | $progress->setRedrawFrequency(100);
41 | $progress->start();
42 |
43 | $bulkSize = $options['bulk-size'];
44 | foreach ($reader as $key => $document) {
45 | $data = $document['_source'];
46 | $data['_id'] = $document['_id'];
47 |
48 | if (array_key_exists('fields', $document)) {
49 | $data = array_merge($document['fields'], $data);
50 | }
51 |
52 | $manager->bulk('index', $document['_type'], $data);
53 |
54 | if (($key + 1) % $bulkSize == 0) {
55 | $manager->commit();
56 | }
57 |
58 | $progress->advance();
59 | }
60 |
61 | $manager->commit();
62 |
63 | $progress->finish();
64 | $output->writeln('');
65 | }
66 |
67 | /**
68 | * Returns a real file path.
69 | *
70 | * @param string $filename
71 | *
72 | * @return string
73 | */
74 | protected function getFilePath($filename)
75 | {
76 | if ($filename[0] == '/' || strstr($filename, ':') !== false) {
77 | return $filename;
78 | }
79 |
80 | return realpath(getcwd() . '/' . $filename);
81 | }
82 |
83 | /**
84 | * Prepares JSON reader.
85 | *
86 | * @param Manager $manager
87 | * @param string $filename
88 | * @param array $options
89 | *
90 | * @return JsonReader
91 | */
92 | protected function getReader($manager, $filename, $options)
93 | {
94 | return new JsonReader($manager, $filename, $options);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Tests/Functional/Result/DocumentWithMultipleFieldsTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Result;
13 |
14 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
15 | use ONGR\ElasticsearchDSL\Query\FullText\MatchQuery;
16 | use ONGR\ElasticsearchDSL\Query\TermLevel\TermQuery;
17 |
18 | class DocumentWithMultipleFieldsTest extends AbstractElasticsearchTestCase
19 | {
20 | /**
21 | * {@inheritdoc}
22 | */
23 | protected function getDataArray()
24 | {
25 | return [
26 | 'default' => [
27 | 'product' => [
28 | [
29 | '_id' => 'doc1',
30 | 'title' => 'Bar Product',
31 | 'related_categories' => [
32 | [
33 | 'title' => 'Acme',
34 | ],
35 | [
36 | 'title' => 'Bar',
37 | ],
38 | ],
39 | ],
40 | [
41 | '_id' => 'doc2',
42 | 'title' => 'Foo Product',
43 | ],
44 | [
45 | '_id' => 'doc3',
46 | 'title' => 'Bar Production',
47 | ],
48 | ],
49 | ],
50 | ];
51 | }
52 |
53 | /**
54 | * Test if we can add more objects into document's "multiple objects" field.
55 | */
56 | public function testMultipleFields()
57 | {
58 | $repo = $this->getManager()->getRepository('TestBundle:Product');
59 |
60 | // throw new \Exception('Fuck you phpunit');
61 |
62 | $query = new MatchQuery('title', 'Bar');
63 | $search = $repo->createSearch();
64 | $search->addQuery($query);
65 | $result = $repo->findDocuments($search);
66 | $this->assertEquals(2, count($result));
67 |
68 | $query = new TermQuery('title.raw', 'Bar');
69 | $search = $repo->createSearch();
70 | $search->addQuery($query);
71 | $result = $repo->findDocuments($search);
72 | $this->assertEquals(0, count($result));
73 |
74 | $query = new TermQuery('title.raw', 'Foo Product');
75 | $search = $repo->createSearch();
76 | $search->addQuery($query);
77 | $result = $repo->findDocuments($search);
78 | $this->assertEquals(1, count($result));
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Tests/Functional/Mapping/MetadataCollectorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Mapping;
13 |
14 | use ONGR\ElasticsearchBundle\Exception\MissingDocumentAnnotationException;
15 | use ONGR\ElasticsearchBundle\Mapping\MetadataCollector;
16 | use ONGR\ElasticsearchBundle\Tests\WebTestCase;
17 |
18 | class MetadataCollectorTest extends WebTestCase
19 | {
20 | /**
21 | * @var MetadataCollector
22 | */
23 | private $metadataCollector;
24 |
25 | /**
26 | * Initialize MetadataCollector.
27 | */
28 | protected function setUp(): void
29 | {
30 | $container = $this->createClient()->getContainer();
31 | $this->metadataCollector = $container->get('es.metadata_collector');
32 | }
33 |
34 | /**
35 | * Test if function throws exception if ES type names are not unique.
36 | */
37 | public function testGetBundleMappingWithTwoSameESTypes()
38 | {
39 | $this->expectException(\LogicException::class);
40 |
41 | $this->metadataCollector->getMappings(['TestBundle', 'TestBundle']);
42 | }
43 |
44 | /**
45 | * Test mapping getter when there are no bundles loaded from parser.
46 | */
47 | public function testGetBundleMappingWithNoBundlesLoaded()
48 | {
49 | $this->expectException(\LogicException::class);
50 | $this->expectExceptionMessage('Bundle \'acme\' does not exist.');
51 |
52 | $this->metadataCollector->getBundleMapping('acme');
53 | }
54 |
55 | /**
56 | * Test for getBundleMapping(). Make sure meta fields are excluded from mapping.
57 | */
58 | public function testGetBundleMapping()
59 | {
60 | $mapping = $this->metadataCollector->getBundleMapping('TestBundle');
61 |
62 | $properties = $mapping['product']['properties'];
63 | $this->assertArrayNotHasKey('_id', $properties);
64 | // $this->assertArrayNotHasKey('_ttl', $properties);
65 |
66 | $aliases = $mapping['product']['aliases'];
67 | $this->assertArrayHasKey('_id', $aliases);
68 | // $this->assertArrayHasKey('_ttl', $aliases);
69 | $this->assertArrayHasKey('_routing', $aliases);
70 | }
71 |
72 | /**
73 | * Test for getDocumentType() in case invalid class given.
74 | */
75 | public function testGetDocumentTypeException()
76 | {
77 | $this->expectException(MissingDocumentAnnotationException::class);
78 | $this->expectExceptionMessage('cannot be parsed as document because @Document annotation is missing');
79 |
80 | $this->metadataCollector->getDocumentType('\StdClass');
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Command/IndexImportCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Command;
13 |
14 | use ONGR\ElasticsearchBundle\Service\ImportService;
15 | use Symfony\Component\Console\Input\InputArgument;
16 | use Symfony\Component\Console\Input\InputInterface;
17 | use Symfony\Component\Console\Input\InputOption;
18 | use Symfony\Component\Console\Output\OutputInterface;
19 | use Symfony\Component\Console\Style\SymfonyStyle;
20 |
21 | /**
22 | * IndexImportCommand class.
23 | */
24 | class IndexImportCommand extends AbstractManagerAwareCommand
25 | {
26 | public static $defaultName = 'ongr:es:index:import';
27 |
28 | /**
29 | * @var ImportService
30 | */
31 | protected $importService;
32 |
33 | public function __construct(ImportService $importService, array $managers = [])
34 | {
35 | parent::__construct($managers, self::$defaultName);
36 |
37 | $this->importService = $importService;
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | protected function configure(): void
44 | {
45 | parent::configure();
46 |
47 | $this
48 | ->setName(static::$defaultName)
49 | ->setDescription('Imports data to elasticsearch index.')
50 | ->addArgument(
51 | 'filename',
52 | InputArgument::REQUIRED,
53 | 'Select file to store output'
54 | )
55 | ->addOption(
56 | 'bulk-size',
57 | 'b',
58 | InputOption::VALUE_REQUIRED,
59 | 'Set bulk size for import',
60 | 1000
61 | )
62 | ->addOption(
63 | 'gzip',
64 | 'z',
65 | InputOption::VALUE_NONE,
66 | 'Import a gzip file'
67 | );
68 | }
69 |
70 | /**
71 | * {@inheritdoc}
72 | */
73 | protected function execute(InputInterface $input, OutputInterface $output): int
74 | {
75 | $io = new SymfonyStyle($input, $output);
76 | $manager = $this->getManager($input->getOption('manager'));
77 |
78 | // Initialize options array
79 | $options = [];
80 | if ($input->getOption('gzip')) {
81 | $options['gzip'] = null;
82 | }
83 | $options['bulk-size'] = $input->getOption('bulk-size');
84 |
85 | $this->importService->importIndex(
86 | $manager,
87 | $input->getArgument('filename'),
88 | $output,
89 | $options
90 | );
91 |
92 | $io->success('Data import completed!');
93 |
94 | return 0;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Resources/doc/find_functions.md:
--------------------------------------------------------------------------------
1 | # Quick find functions
2 |
3 | > If you haven't read about maping and simple usage of the bundle please take a look at first to the [mapping](mapping.md) docs.
4 |
5 | For all examples below we will use `Content` document class from the [CRUD actions](crud.md) chapter.
6 |
7 | ## Find a document by ID
8 |
9 | Find by id will execute [elasticsearch get query](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html).
10 |
11 | ```php
12 |
13 | $repo = $this->get('es.manager.default.content');
14 |
15 | /** @var $content Content **/
16 | $content = $repo->find(1); // 5 is the document _uid in the elasticsearch.
17 |
18 | ```
19 |
20 | > All `find` methods return an object. If you want to get raw result use `execute($search, Result::RESULTS_RAW)`.
21 |
22 | ## Find multiple documents by ID
23 |
24 | If multiple documents need to be found by their IDs, `findByIds()` method can be used. It accepts an array of document IDs
25 | and returns `DocumentIterator` with found documents:
26 |
27 | ```php
28 |
29 | $documents = $repo->findByIds(['26', '8', '11']);
30 |
31 | ```
32 |
33 | For this functionality the `Repository` uses
34 | [elasticsearch multi get API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-multi-get.html).
35 |
36 | ## Find by field
37 |
38 | Find by field uses [query_string query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-query-string-query.html) to fetch results by a specified field value.
39 |
40 | > Document matches heavily depend on how mapping is defined. If you are unsure of whether it will work, it is better to use structured search from `Search` object.
41 |
42 |
43 | ```php
44 |
45 | $repo = $this->get('es.manager.default.content');
46 |
47 | /** @var $content Content **/
48 | $content = $repo->findBy(['title' => 'Acme']);
49 |
50 | ```
51 |
52 | The return will be:
53 |
54 | ```
55 | Array
56 | (
57 | [0] => Array
58 | (
59 | [title] => Acme
60 | )
61 | )
62 | ```
63 |
64 | Also with `findBy` you can define the way the results are ordered, limit the amount of retrieved documents and define the offset of the results, eg:
65 |
66 | ```php
67 |
68 | $content = $repo->findBy(['title' => 'Acme'], ['price' => 'asc'], 20, 10);
69 |
70 | ```
71 |
72 | This will return up to 20 documents with the word 'Acme' in their title, also it will skip the first 10 results and the results will be ordered from the ones with the smallest price to the ones with the highest.
73 |
74 | ## Find one document by field
75 |
76 | Completely the same as `findBy()` function, except it will return the first document.
77 |
78 | ```php
79 |
80 | $repo = $this->get('es.manager.default.content');
81 |
82 | /** @var $content Content **/
83 | $content = $repo->findOneBy(['title' => 'Acme']);
84 |
85 | ```
86 |
87 | The return will be:
88 |
89 | ```
90 | Array
91 | (
92 | [title] => Acme
93 | )
94 | ```
95 |
--------------------------------------------------------------------------------
/Command/IndexExportCommand.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Command;
13 |
14 | use ONGR\ElasticsearchBundle\Service\ExportService;
15 | use Symfony\Component\Console\Input\InputArgument;
16 | use Symfony\Component\Console\Input\InputInterface;
17 | use Symfony\Component\Console\Input\InputOption;
18 | use Symfony\Component\Console\Output\OutputInterface;
19 | use Symfony\Component\Console\Style\SymfonyStyle;
20 |
21 | /**
22 | * IndexExportCommand class.
23 | */
24 | class IndexExportCommand extends AbstractManagerAwareCommand
25 | {
26 | public static $defaultName = 'ongr:es:index:export';
27 |
28 | /**
29 | * @var ExportService
30 | */
31 | protected $exportService;
32 |
33 | public function __construct(ExportService $exportService, array $managers = [])
34 | {
35 | parent::__construct($managers, self::$defaultName);
36 |
37 | $this->exportService = $exportService;
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | protected function configure(): void
44 | {
45 | parent::configure();
46 |
47 | $this
48 | ->setName(static::$defaultName)
49 | ->setDescription('Exports data from elasticsearch index.')
50 | ->addArgument(
51 | 'filename',
52 | InputArgument::REQUIRED,
53 | 'Select file to store output'
54 | )->addOption(
55 | 'types',
56 | null,
57 | InputOption::VALUE_REQUIRED + InputOption::VALUE_IS_ARRAY,
58 | 'Export specific types only'
59 | )->addOption(
60 | 'chunk',
61 | null,
62 | InputOption::VALUE_REQUIRED,
63 | 'Chunk size to use in scan api',
64 | 500
65 | )->addOption(
66 | 'split',
67 | null,
68 | InputOption::VALUE_REQUIRED,
69 | 'Split file in a separate parts if line number exceeds provided value',
70 | 300000
71 | );
72 | }
73 |
74 | /**
75 | * {@inheritdoc}
76 | */
77 | protected function execute(InputInterface $input, OutputInterface $output): int
78 | {
79 | $io = new SymfonyStyle($input, $output);
80 | $manager = $this->getManager($input->getOption('manager'));
81 |
82 | $this->exportService->exportIndex(
83 | $manager,
84 | $input->getArgument('filename'),
85 | $input->getOption('types'),
86 | $input->getOption('chunk'),
87 | $output,
88 | $input->getOption('split')
89 | );
90 |
91 | $io->success('Data export completed!');
92 |
93 | return 0;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Tests/Functional/Result/AggregationIteratorFindTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Result;
13 |
14 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
15 | use ONGR\ElasticsearchDSL\Aggregation\Bucketing\RangeAggregation;
16 |
17 | class AggregationIteratorFindTest extends AbstractElasticsearchTestCase
18 | {
19 | /**
20 | * {@inheritdoc}
21 | */
22 | protected function getDataArray()
23 | {
24 | return [
25 | 'default' => [
26 | 'product' => [
27 | [
28 | '_id' => 1,
29 | 'title' => 'Onion',
30 | 'description' => 'solid',
31 | 'price' => 10.45,
32 | ],
33 | [
34 | '_id' => 2,
35 | 'title' => 'Tomato',
36 | 'description' => 'weak',
37 | 'price' => 32,
38 | ],
39 | [
40 | '_id' => 3,
41 | 'title' => 'Pizza',
42 | 'description' => 'weak',
43 | 'price' => 15.1,
44 | ],
45 | ],
46 | ],
47 | ];
48 | }
49 |
50 | /**
51 | * Aggregation iterator main test.
52 | */
53 | public function testIteration()
54 | {
55 | $expected = [
56 | [
57 | 'key' => '*-20.0',
58 | 'doc_count' => 2,
59 | ],
60 | [
61 | 'key' => '20.0-*',
62 | 'doc_count' => 1,
63 | ],
64 | ];
65 |
66 | $repository = $this
67 | ->getManager()
68 | ->getRepository('TestBundle:Product');
69 |
70 | $rangeAggregation = new RangeAggregation('range', 'price');
71 | $rangeAggregation->addRange(null, 20);
72 | $rangeAggregation->addRange(20, null);
73 |
74 | $search = $repository
75 | ->createSearch()
76 | ->addAggregation($rangeAggregation);
77 |
78 | $results = $repository->findDocuments($search);
79 | $rangeResult = $results->getAggregation('range');
80 |
81 | $this->assertInstanceOf('ONGR\ElasticsearchBundle\Result\Aggregation\AggregationValue', $rangeResult);
82 |
83 | foreach ($rangeResult->getBuckets() as $aggKey => $subAgg) {
84 | $this->assertInstanceOf('ONGR\ElasticsearchBundle\Result\Aggregation\AggregationValue', $subAgg);
85 | $this->assertEquals($expected[$aggKey]['key'], $subAgg->getValue('key'));
86 | $this->assertEquals($expected[$aggKey]['doc_count'], $subAgg->getValue('doc_count'));
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Tests/Functional/Result/ArrayIteratorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Result;
13 |
14 | use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
15 | use ONGR\ElasticsearchBundle\Service\Repository;
16 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
17 |
18 | class ArrayIteratorTest extends AbstractElasticsearchTestCase
19 | {
20 | /**
21 | * {@inheritdoc}
22 | */
23 | protected function getDataArray()
24 | {
25 | return [
26 | 'default' => [
27 | 'product' => [
28 | [
29 | '_id' => 'doc1',
30 | 'title' => 'Foo Product',
31 | 'related_categories' => [
32 | [
33 | 'title' => 'Acme',
34 | ],
35 | ],
36 | ],
37 | [
38 | '_id' => 'doc2',
39 | 'title' => 'Bar Product',
40 | 'related_categories' => [
41 | [
42 | 'title' => 'Acme',
43 | ],
44 | [
45 | 'title' => 'Bar',
46 | ],
47 | ],
48 | ],
49 | ],
50 | ],
51 | ];
52 | }
53 |
54 | /**
55 | * Iteration test.
56 | */
57 | public function testIteration()
58 | {
59 | /** @var Repository $repo */
60 | $repo = $this->getManager()->getRepository('TestBundle:Product');
61 | $match = new MatchAllQuery();
62 | $search = $repo->createSearch()->addQuery($match);
63 | $iterator = $repo->findArray($search);
64 |
65 | $this->assertInstanceOf('ONGR\ElasticsearchBundle\Result\ArrayIterator', $iterator);
66 |
67 | $assertResults = $this->getDataArray();
68 | foreach ($iterator as $key => $document) {
69 | $assertDocument = $assertResults['default']['product'][$key];
70 | unset($assertDocument['_id']);
71 | $this->assertEquals($assertDocument, $document);
72 | }
73 | }
74 |
75 | /**
76 | * Test array results iteration with fields set.
77 | */
78 | public function testIterationWhenFieldsAreSet()
79 | {
80 | /** @var Repository $repo */
81 | $repo = $this->getManager()->getRepository('TestBundle:Product');
82 | $match = new MatchAllQuery();
83 | $search = $repo->createSearch()->addQuery($match);
84 | $iterator = $repo->findArray($search);
85 |
86 | $this->assertInstanceOf('ONGR\ElasticsearchBundle\Result\ArrayIterator', $iterator);
87 |
88 | $assertResults = $this->getDataArray();
89 | foreach ($iterator as $key => $document) {
90 | $this->assertEquals($assertResults['default']['product'][$key]['title'], $document['title']);
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/DependencyInjection/Compiler/RepositoryPass.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\DependencyInjection\Compiler;
13 |
14 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15 | use Symfony\Component\DependencyInjection\ContainerBuilder;
16 | use Symfony\Component\DependencyInjection\Definition;
17 | use Symfony\Component\DependencyInjection\Reference;
18 | use Symfony\Component\HttpKernel\Kernel;
19 |
20 | /**
21 | * Compiles elastic search data.
22 | */
23 | class RepositoryPass implements CompilerPassInterface
24 | {
25 | /**
26 | * {@inheritdoc}
27 | */
28 | public function process(ContainerBuilder $container)
29 | {
30 | $managers = $container->getParameter('es.managers');
31 |
32 | $removeContainerBuildId = false;
33 | if (!$container->hasParameter('container.build_id')) {
34 | // the 'container.build_id' is required for `es.cache_engine` system cache which normally can not
35 | // be constructor inside a compiler pass. This is a workaround to make it work.
36 | // see also:
37 | // - https://github.com/symfony/symfony/blob/52a92926f7fed15cdff399c6921100a10e0d6f61/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php#L389
38 | // - https://github.com/symfony/symfony/blob/52a92926f7fed15cdff399c6921100a10e0d6f61/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php#L2322
39 | $container->setParameter('container.build_id', hash('crc32', 'Abc123' . time()));
40 | $removeContainerBuildId = true;
41 | }
42 |
43 | $collector = $container->get('es.metadata_collector');
44 | if ($removeContainerBuildId) {
45 | $container->getParameterBag()->remove('container.build_id');
46 | }
47 |
48 | foreach ($managers as $managerName => $manager) {
49 | $mappings = $collector->getMappings($manager['mappings']);
50 |
51 | // Building repository services.
52 | foreach ($mappings as $repositoryType => $repositoryDetails) {
53 | $repositoryDefinition = new Definition(
54 | 'ONGR\ElasticsearchBundle\Service\Repository',
55 | [$repositoryDetails['namespace']]
56 | );
57 | $repositoryDefinition->setPublic(true);
58 |
59 | if (isset($repositoryDetails['directory_name']) && $managerName == 'default') {
60 | $container->get('es.document_finder')->setDocumentDir($repositoryDetails['directory_name']);
61 | }
62 |
63 | $repositoryDefinition->setFactory(
64 | [
65 | new Reference(sprintf('es.manager.%s', $managerName)),
66 | 'getRepository',
67 | ]
68 | );
69 |
70 | $repositoryId = sprintf('es.manager.%s.%s', $managerName, $repositoryType);
71 | $container->setDefinition($repositoryId, $repositoryDefinition);
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Service/Json/JsonWriter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Service\Json;
13 |
14 | /**
15 | * Serializes records one by one. Outputs given metadata before first record.
16 | *
17 | * Sample output:
18 | *
19 | * [
20 | * {"count":2},
21 | * {"_id":"doc1","title":"Document 1"},
22 | * {"_id":"doc2","title":"Document 2"}
23 | * ]
24 | *
25 | */
26 | class JsonWriter
27 | {
28 | /**
29 | * @var string
30 | */
31 | private $filename;
32 |
33 | /**
34 | * @var resource A file system pointer resource.
35 | */
36 | private $handle;
37 |
38 | /**
39 | * @var array
40 | */
41 | private $metadata;
42 |
43 | /**
44 | * @var int Current record number.
45 | */
46 | private $currentPosition = 0;
47 |
48 | /**
49 | * Constructor.
50 | *
51 | * Metadata can contain any fields but only the field "count"
52 | * is recognized and used while writing to file. If written lines count
53 | * reaches "count", writer will automatically finalize file.
54 | *
55 | * @param string $filename A file in which data will be saved.
56 | * @param array $metadata Additional metadata to be stored.
57 | */
58 | public function __construct($filename, $metadata = [])
59 | {
60 | $this->filename = $filename;
61 | $this->metadata = $metadata;
62 | }
63 |
64 | /**
65 | * Destructor. Closes file handler if open.
66 | */
67 | public function __destruct()
68 | {
69 | $this->finalize();
70 | }
71 |
72 | /**
73 | * Performs initialization.
74 | */
75 | protected function initialize()
76 | {
77 | if ($this->handle !== null) {
78 | return;
79 | }
80 |
81 | $this->handle = fopen($this->filename, 'w');
82 | fwrite($this->handle, "[\n");
83 | fwrite($this->handle, json_encode($this->metadata));
84 | }
85 |
86 | /**
87 | * Performs finalization.
88 | */
89 | public function finalize()
90 | {
91 | $this->initialize();
92 |
93 | if (is_resource($this->handle)) {
94 | fwrite($this->handle, "\n]");
95 | fclose($this->handle);
96 | }
97 | }
98 |
99 | /**
100 | * Writes single document to stream.
101 | *
102 | * @param mixed $document Object to insert into stream.
103 | *
104 | * @throws \OverflowException
105 | */
106 | public function push($document)
107 | {
108 | $this->initialize();
109 | $this->currentPosition++;
110 |
111 | if (isset($this->metadata['count']) && $this->currentPosition > $this->metadata['count']) {
112 | throw new \OverflowException(
113 | sprintf('This writer was set up to write %d documents, got more.', $this->metadata['count'])
114 | );
115 | }
116 |
117 | fwrite($this->handle, ",\n");
118 | fwrite($this->handle, json_encode($document));
119 |
120 | if (isset($this->metadata['count']) && $this->currentPosition == $this->metadata['count']) {
121 | $this->finalize();
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/Tests/Unit/Mapping/DocumentFinderTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Mapping;
13 |
14 | use ONGR\ElasticsearchBundle\Mapping\DocumentFinder;
15 | use PHPUnit\Framework\TestCase;
16 |
17 | class DocumentFinderTest extends TestCase
18 | {
19 | /**
20 | * Data provider for testGetNamespace().
21 | *
22 | * @return array
23 | */
24 | public function getTestGetNamespaceData()
25 | {
26 | return [
27 | [
28 | 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product',
29 | 'TestBundle:Product'
30 | ],
31 | [
32 | 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\User',
33 | 'TestBundle:User'
34 | ],
35 | ];
36 | }
37 |
38 | /**
39 | * Data provider for testGetNamespaceWithSubDirInDocumentDirectory().
40 | *
41 | * @return array
42 | */
43 | public function getTestGetNamespaceDataWithSubDirInDocumentDir()
44 | {
45 | return [
46 | [
47 | 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Store\Product',
48 | 'TestBundle:Product',
49 | 'Document\Store'
50 | ],
51 | ];
52 | }
53 |
54 | /**
55 | * Tests for getNamespace().
56 | *
57 | * @param string $expectedNamespace
58 | * @param string $className
59 | *
60 | * @dataProvider getTestGetNamespaceData()
61 | */
62 | public function testGetNamespace($expectedNamespace, $className)
63 | {
64 | $bundles = [
65 | 'TestBundle' => 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\TestBundle'
66 | ];
67 | $finder = new DocumentFinder($bundles);
68 |
69 | $this->assertEquals($expectedNamespace, $finder->getNamespace($className));
70 | }
71 |
72 | /**
73 | * Tests for getNamespace() with a configured document directory.
74 | *
75 | * @param string $expectedNamespace
76 | * @param string $className
77 | * @param string $documentDir
78 | *
79 | * @dataProvider getTestGetNamespaceDataWithSubDirInDocumentDir()
80 | */
81 | public function testGetNamespaceWithSubDirInDocumentDirectory($expectedNamespace, $className, $documentDir)
82 | {
83 | $bundles = [
84 | 'TestBundle' => 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\TestBundle'
85 | ];
86 | $finder = new DocumentFinder($bundles);
87 |
88 | $this->assertEquals($expectedNamespace, $finder->getNamespace($className, $documentDir));
89 | }
90 |
91 | /**
92 | * Test for getBundleDocumentClasses().
93 | */
94 | public function testGetBundleDocumentClasses()
95 | {
96 | $bundles = [
97 | 'TestBundle' => 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\TestBundle'
98 | ];
99 | $finder = new DocumentFinder($bundles);
100 |
101 | $documents = $finder->getBundleDocumentClasses('TestBundle');
102 |
103 | $this->assertGreaterThan(0, count($documents));
104 | $this->assertContains('Product', $documents);
105 | $this->assertContains('User', $documents);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/Tests/Unit/Service/Json/JsonWriterTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Service\Json;
13 |
14 | use ONGR\ElasticsearchBundle\Service\Json\JsonWriter;
15 | use org\bovigo\vfs\vfsStream;
16 | use PHPUnit\Framework\TestCase;
17 |
18 | class JsonWriterTest extends TestCase
19 | {
20 | /**
21 | * {@inheritdoc}
22 | */
23 | public static function setUpBeforeClass(): void
24 | {
25 | parent::setUpBeforeClass();
26 |
27 | vfsStream::setup('tmp');
28 | }
29 |
30 | /**
31 | * Data provider for testPush().
32 | *
33 | * @return array
34 | */
35 | public function getTestPushData()
36 | {
37 | $cases = [];
38 |
39 | // Case #0 Standard case.
40 | $metadata = ['count' => 2];
41 | $documents = [
42 | [
43 | '_id' => 'doc1',
44 | 'title' => 'Document 1',
45 | ],
46 | [
47 | '_id' => 'doc2',
48 | 'title' => 'Document 2',
49 | ],
50 | ];
51 | $expectedOutput = << 'doc1',
70 | 'title' => 'Document 1',
71 | ],
72 | [
73 | '_id' => 'doc2',
74 | 'title' => 'Document 2',
75 | ],
76 | ];
77 | $expectedOutput = <<push($document);
118 | }
119 |
120 | $writer->finalize();
121 |
122 | $this->assertEquals($expectedOutput, file_get_contents($filename));
123 | }
124 |
125 | /**
126 | * Test for push() in case of too many documents passed.
127 | */
128 | public function testPushException()
129 | {
130 | $this->expectException(\OverflowException::class);
131 |
132 | $filename = vfsStream::url('tmp/test.json');
133 |
134 | $writer = new JsonWriter($filename, ['count' => 0]);
135 | $writer->push(null);
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/DependencyInjection/ONGRElasticsearchExtension.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\DependencyInjection;
13 |
14 | use Symfony\Component\Config\FileLocator;
15 | use Symfony\Component\DependencyInjection\ContainerBuilder;
16 | use Symfony\Component\DependencyInjection\ContainerInterface;
17 | use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
18 | use Symfony\Component\DependencyInjection\Loader;
19 | use Symfony\Component\DependencyInjection\Definition;
20 | use Symfony\Component\HttpKernel\DependencyInjection\Extension;
21 | use Symfony\Component\DependencyInjection\Reference;
22 | use Symfony\Component\HttpKernel\Kernel;
23 | use Symfony\Component\HttpKernel\KernelEvents;
24 |
25 | /**
26 | * This is the class that loads and manages bundle configuration.
27 | */
28 | class ONGRElasticsearchExtension extends Extension implements PrependExtensionInterface
29 | {
30 | public function prepend(ContainerBuilder $container)
31 | {
32 | if ($container->hasExtension('framework')) {
33 | $container->prependExtensionConfig(
34 | 'framework',
35 | [
36 | 'cache' => [
37 | 'pools' => [
38 | 'es.cache_engine' => [
39 | 'adapter' => 'cache.system',
40 | ],
41 | ],
42 | ],
43 | ]
44 | );
45 | }
46 | }
47 |
48 | /**
49 | * {@inheritdoc}
50 | */
51 | public function load(array $configs, ContainerBuilder $container)
52 | {
53 | $configuration = new Configuration();
54 | $config = $this->processConfiguration($configuration, $configs);
55 |
56 | $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
57 | $loader->load('services.yml');
58 |
59 | $config['cache'] = isset($config['cache']) ?
60 | $config['cache'] : !$container->getParameter('kernel.debug');
61 | $config['profiler'] = isset($config['profiler']) ?
62 | $config['profiler'] : $container->getParameter('kernel.debug');
63 |
64 | $managers = $config['managers'];
65 | foreach ($managers as &$manager) {
66 | $manager['mappings'] = array_unique($manager['mappings'], SORT_REGULAR);
67 | }
68 | $container->setParameter('es.cache', $config['cache']);
69 | $container->setParameter('es.analysis', $config['analysis']);
70 | $container->setParameter('es.managers', $managers);
71 | $container->setParameter('es.app_root_class', $config['app_root_class'] ?? null);
72 | $definition = new Definition(
73 | 'ONGR\ElasticsearchBundle\Service\ManagerFactory',
74 | [
75 | new Reference('es.metadata_collector'),
76 | new Reference('es.result_converter'),
77 | $config['profiler'] ? new Reference('es.tracer') : null,
78 | new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE),
79 | ]
80 | );
81 | $definition->addMethodCall('setEventDispatcher', [new Reference('event_dispatcher')]);
82 | $definition->addMethodCall(
83 | 'setStopwatch',
84 | [
85 | new Reference('debug.stopwatch', ContainerInterface::NULL_ON_INVALID_REFERENCE)
86 | ]
87 | );
88 | $definition->setPublic(true);
89 | $container->setDefinition('es.manager_factory', $definition);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Tests/Functional/Result/HashMapObjectIteratorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Result;
13 |
14 | use ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product;
15 | use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
16 | use ONGR\ElasticsearchBundle\Service\Repository;
17 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
18 |
19 | class HashMapObjectIteratorTest extends AbstractElasticsearchTestCase
20 | {
21 | /**
22 | * {@inheritdoc}
23 | */
24 | protected function getDataArray()
25 | {
26 | return [
27 | 'default' => [
28 | 'product' => [
29 | [
30 | '_id' => '1',
31 | 'title' => 'Foo Product',
32 | 'custom_attributes' => [
33 | [
34 | 'one' => 'Acme',
35 | ],
36 | [
37 | 'two' => 'Bar',
38 | ],
39 | [
40 | 'three' => 'Foo',
41 | ],
42 | ],
43 | ],
44 | [
45 | '_id' => '2',
46 | 'title' => 'Bar Product',
47 | 'custom_attributes' => [
48 | [
49 | 'title' => 'Acme',
50 | ],
51 | [
52 | 'title' => 'Bar',
53 | ],
54 | [
55 | 'title' => 'Foo',
56 | ],
57 | ],
58 | ],
59 | ],
60 | ],
61 | ];
62 | }
63 |
64 | /**
65 | * Iteration test.
66 | */
67 | public function testIteration()
68 | {
69 | /** @var Repository $repo */
70 | $repo = $this->getManager()->getRepository('TestBundle:Product');
71 | $match = new MatchAllQuery();
72 | $search = $repo->createSearch()->addQuery($match);
73 | $iterator = $repo->findDocuments($search);
74 |
75 | $this->assertInstanceOf('ONGR\ElasticsearchBundle\Result\DocumentIterator', $iterator);
76 |
77 | /** @var Product $document */
78 | foreach ($iterator as $document) {
79 | $this->assertInstanceOf(
80 | 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product',
81 | $document
82 | );
83 |
84 | $this->assertCount(3, $document->getCustomAttributes());
85 |
86 | $attributes = $document->getCustomAttributes();
87 |
88 | switch ($document->getId()) {
89 | case '1':
90 | $this->assertEquals($attributes[0]['one'], 'Acme');
91 | $this->assertEquals($attributes[1]['two'], 'Bar');
92 | $this->assertEquals($attributes[2]['three'], 'Foo');
93 | break;
94 | case '2':
95 | $this->assertEquals($attributes[0]['title'], 'Acme');
96 | $this->assertEquals($attributes[1]['title'], 'Bar');
97 | $this->assertEquals($attributes[2]['title'], 'Foo');
98 | break;
99 | }
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Tests/Unit/Result/AbstractResultsIteratorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Result;
13 |
14 | use PHPUnit\Framework\TestCase;
15 |
16 | class AbstractResultsIteratorTest extends TestCase
17 | {
18 | /**
19 | * Test if scroll is cleared on destructor.
20 | */
21 | public function testClearScroll()
22 | {
23 | $rawData = [
24 | '_scroll_id' => 'foo',
25 | ];
26 |
27 | $manager = $this->getMockBuilder('ONGR\ElasticsearchBundle\Service\Manager')
28 | ->setMethods(['getConfig', 'clearScroll'])
29 | ->disableOriginalConstructor()
30 | ->getMock();
31 | $manager->expects($this->any())->method('getConfig')->willReturn([]);
32 | $manager->expects($this->once())->method('clearScroll')->with('foo');
33 |
34 | $scroll = ['_scroll_id' => 'foo', 'duration' => '5m'];
35 | $iterator = new DummyIterator($rawData, $manager, $scroll);
36 |
37 | // Trigger destructor call
38 | unset($iterator);
39 | }
40 |
41 | /**
42 | * Test for getDocumentScore().
43 | */
44 | public function testGetDocumentScore()
45 | {
46 | $rawData = [
47 | 'hits' => [
48 | 'total' => 3,
49 | 'hits' => [
50 | [
51 | '_index' => 'test',
52 | '_type' => 'product',
53 | '_id' => 'foo',
54 | '_score' => 1,
55 | '_source' => [
56 | 'title' => 'Product Foo',
57 | ],
58 | ],
59 | [
60 | '_index' => 'test',
61 | '_type' => 'product',
62 | '_id' => 'bar',
63 | '_score' => 2,
64 | '_source' => [
65 | 'title' => 'Product Bar',
66 | ],
67 | ],
68 | [
69 | '_index' => 'test',
70 | '_type' => 'product',
71 | '_id' => 'baz',
72 | '_score' => null,
73 | '_source' => [
74 | 'title' => 'Product Baz',
75 | ],
76 | ],
77 | ],
78 | ],
79 | ];
80 |
81 | $manager = $this->getMockBuilder('ONGR\ElasticsearchBundle\Service\Manager')
82 | ->disableOriginalConstructor()
83 | ->getMock();
84 |
85 | $results = new DummyIterator($rawData, $manager);
86 |
87 | $expectedScores = [1, 2, null];
88 | $actualScores = [];
89 |
90 | foreach ($results as $item) {
91 | $actualScores[] = $results->getDocumentScore();
92 | }
93 |
94 | $this->assertEquals($expectedScores, $actualScores);
95 | }
96 |
97 | /**
98 | * Test for getDocumentScore() in case called when current iterator value is not valid.
99 | */
100 | public function testGetScoreException()
101 | {
102 | $this->expectException(\LogicException::class);
103 | $this->expectExceptionMessage('Document score is available only while iterating over results');
104 |
105 | $manager = $this->getMockBuilder('ONGR\ElasticsearchBundle\Service\Manager')
106 | ->disableOriginalConstructor()
107 | ->getMock();
108 |
109 | $results = new DummyIterator([], $manager);
110 | $results->getDocumentScore();
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Tests/Functional/Result/DocumentIteratorTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Result;
13 |
14 | use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
15 | use ONGR\ElasticsearchBundle\Service\Repository;
16 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
17 |
18 | class DocumentIteratorTest extends AbstractElasticsearchTestCase
19 | {
20 | /**
21 | * {@inheritdoc}
22 | */
23 | protected function getDataArray()
24 | {
25 | return [
26 | 'default' => [
27 | 'product' => [
28 | [
29 | '_id' => 'doc1',
30 | 'title' => 'Foo Product',
31 | 'related_categories' => [
32 | [
33 | 'title' => 'Acme',
34 | ],
35 | ],
36 | ],
37 | [
38 | '_id' => 'doc2',
39 | 'title' => 'Bar Product',
40 | 'related_categories' => [
41 | [
42 | 'title' => 'Acme',
43 | ],
44 | [
45 | 'title' => 'Bar',
46 | ],
47 | ],
48 | ],
49 | ],
50 | ],
51 | ];
52 | }
53 |
54 | /**
55 | * Iteration test.
56 | */
57 | public function testIteration()
58 | {
59 | /** @var Repository $repo */
60 | $repo = $this->getManager()->getRepository('TestBundle:Product');
61 | $match = new MatchAllQuery();
62 | $search = $repo->createSearch()->addQuery($match);
63 | $iterator = $repo->findDocuments($search);
64 |
65 | $this->assertInstanceOf('ONGR\ElasticsearchBundle\Result\DocumentIterator', $iterator);
66 |
67 | foreach ($iterator as $document) {
68 | $categories = $document->getRelatedCategories();
69 |
70 | $this->assertInstanceOf(
71 | 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product',
72 | $document
73 | );
74 | $this->assertInstanceOf('ONGR\ElasticsearchBundle\Result\ObjectIterator', $categories);
75 |
76 | foreach ($categories as $category) {
77 | $this->assertInstanceOf(
78 | 'ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\CategoryObject',
79 | $category
80 | );
81 | }
82 | }
83 | }
84 |
85 | /**
86 | * Tests if current() returns null when data doesn't exist.
87 | */
88 | public function testCurrentWithEmptyIterator()
89 | {
90 | $repo = $this->getManager()->getRepository('TestBundle:User');
91 | $search = $repo
92 | ->createSearch()
93 | ->addQuery(new MatchAllQuery());
94 | $result = $repo->findDocuments($search);
95 |
96 | $this->assertNull($result->current());
97 | }
98 |
99 | /**
100 | * Tests AbstractResultsIterator#first method.
101 | */
102 | public function testIteratorFirst()
103 | {
104 | $repo = $this->getManager()->getRepository('TestBundle:Product');
105 | $search = $repo
106 | ->createSearch()
107 | ->addQuery(new MatchAllQuery());
108 | $document = $repo->findDocuments($search)->first();
109 |
110 | $this->assertEquals('Foo Product', $document->getTitle());
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Result/Aggregation/AggregationValue.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Result\Aggregation;
13 |
14 | /**
15 | * This is the class for plain aggregation result with nested aggregations support.
16 | */
17 | class AggregationValue implements \ArrayAccess, \IteratorAggregate
18 | {
19 | /**
20 | * @var array
21 | */
22 | private $rawData;
23 |
24 | /**
25 | * Constructor.
26 | *
27 | * @param array $rawData
28 | */
29 | public function __construct($rawData)
30 | {
31 | $this->rawData = $rawData;
32 | }
33 |
34 | /**
35 | * Returns aggregation value by name.
36 | *
37 | * @param string $name
38 | *
39 | * @return array
40 | */
41 | public function getValue($name = 'key')
42 | {
43 | if (!isset($this->rawData[$name])) {
44 | return null;
45 | }
46 |
47 | return $this->rawData[$name];
48 | }
49 |
50 | /**
51 | * Returns the document count of the aggregation
52 | *
53 | * @return integer
54 | */
55 | public function getCount()
56 | {
57 | return $this->getValue('doc_count');
58 | }
59 |
60 | /**
61 | * Returns array of bucket values.
62 | *
63 | * @return AggregationValue[]|null
64 | */
65 | public function getBuckets()
66 | {
67 | if (!isset($this->rawData['buckets'])) {
68 | return null;
69 | }
70 |
71 | $buckets = [];
72 |
73 | foreach ($this->rawData['buckets'] as $bucket) {
74 | $buckets[] = new self($bucket);
75 | }
76 |
77 | return $buckets;
78 | }
79 |
80 | /**
81 | * Returns sub-aggregation.
82 | *
83 | * @param string $name
84 | *
85 | * @return AggregationValue|null
86 | */
87 | public function getAggregation($name)
88 | {
89 | if (!isset($this->rawData[$name])) {
90 | return null;
91 | }
92 |
93 | return new self($this->rawData[$name]);
94 | }
95 |
96 | /**
97 | * Applies path method to aggregations.
98 | *
99 | * @param string $path
100 | *
101 | * @return AggregationValue|null
102 | */
103 | public function find($path)
104 | {
105 | $name = explode('.', $path, 2);
106 | $aggregation = $this->getAggregation($name[0]);
107 |
108 | if ($aggregation === null || !isset($name[1])) {
109 | return $aggregation;
110 | }
111 |
112 | return $aggregation->find($name[1]);
113 | }
114 |
115 | /**
116 | * {@inheritdoc}
117 | */
118 | #[\ReturnTypeWillChange]
119 | public function offsetExists($offset)
120 | {
121 | return array_key_exists($offset, $this->rawData);
122 | }
123 |
124 | /**
125 | * {@inheritdoc}
126 | */
127 | #[\ReturnTypeWillChange]
128 | public function offsetGet($offset)
129 | {
130 | if (!isset($this->rawData[$offset])) {
131 | return null;
132 | }
133 |
134 | return $this->rawData[$offset];
135 | }
136 |
137 | /**
138 | * {@inheritdoc}
139 | */
140 | #[\ReturnTypeWillChange]
141 | public function offsetSet($offset, $value)
142 | {
143 | throw new \LogicException('Aggregation result can not be changed on runtime.');
144 | }
145 |
146 | /**
147 | * {@inheritdoc}
148 | */
149 | #[\ReturnTypeWillChange]
150 | public function offsetUnset($offset)
151 | {
152 | throw new \LogicException('Aggregation result can not be changed on runtime.');
153 | }
154 |
155 | /**
156 | * {@inheritdoc}
157 | */
158 | public function getIterator()
159 | {
160 | $buckets = $this->getBuckets();
161 |
162 | if ($buckets === null) {
163 | throw new \LogicException('Can not iterate over aggregation without buckets!');
164 | }
165 |
166 | return new \ArrayIterator($this->getBuckets());
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/Resources/doc/crud.md:
--------------------------------------------------------------------------------
1 | # CRUD actions
2 |
3 | > To proceed with steps bellow it is necessary to read [mapping](mapping.md) topic and have defined documents in the bundle.
4 |
5 | For all steps below we assume that there is an `AppBundle` with the `Content` document.
6 |
7 | ```php
8 | // src/AppBundle/Document/Content.php
9 |
10 | namespace AppBundle\Document;
11 |
12 | use ONGR\ElasticsearchBundle\Annotation as ES;
13 |
14 | /**
15 | * @ES\Document(type="content")
16 | */
17 | class Content
18 | {
19 | /**
20 | * @var string
21 | *
22 | * @ES\Id()
23 | */
24 | public $id;
25 |
26 | /**
27 | * @ES\Property(type="keyword")
28 | */
29 | public $title;
30 | }
31 | ```
32 |
33 | ## Manager
34 |
35 | Elasticsearch bundle provides managers able to handle several indexes to communicate with elasticsearch.
36 |
37 | Once you define managers in your `config.yml` file, you can use them in controllers and grab them from DI container via `es.manager` (alias for `es.manager.default`). If you define more than one manager, for example called `foo`, then it will be accessible via `es.manager.foo`.
38 |
39 | ```php
40 |
41 | $manager = $this->get('es.manager');
42 |
43 | ```
44 |
45 | ## Repositories
46 |
47 | In addition manager provides repository access, which enables direct access to the elasticsearch type.
48 | Repository represents a document. Whenever you need to do any action with a repository, you can access
49 | it like this:
50 |
51 | ```php
52 |
53 | $manager = $this->get('es.manager');
54 | $repo = $manager->getRepository('AppBundle:Content');
55 |
56 | ```
57 |
58 | Alternatively:
59 |
60 | ```php
61 |
62 | $repo = $this->get('es.manager.default.content');
63 |
64 | ```
65 |
66 | `default` - represents a manager name and `content` an elasticsearch type name.
67 |
68 | > Important: Document with the certain type name has to be mapped in the manager.
69 |
70 | You can also get a manager from the repo instance:
71 |
72 | ```php
73 |
74 | $manager = $repo->getManager();
75 |
76 | ```
77 |
78 | ## Create a document
79 |
80 | ```php
81 |
82 | $content = new Content();
83 | $content->id = 5; // Optional, if not set, elasticsearch will set a random.
84 | $content->title = 'Acme title';
85 | $manager->persist($content);
86 | $manager->commit();
87 |
88 | ```
89 |
90 | ## Update a document
91 |
92 | ```php
93 |
94 | $content = $manager->find('AppBundle:Content', 5);
95 | $content->title = 'changed Acme title';
96 | $manager->persist($content);
97 | $manager->commit();
98 |
99 | ```
100 |
101 | ## Partial update
102 |
103 | There is a quicker way to update a document field without creating object or fetching a whole document from elasticsearch. For this action we will use [partial update](https://www.elastic.co/guide/en/elasticsearch/guide/current/partial-updates.html) from elasticsearch.
104 |
105 | To update a field you need to know the document `ID` and fields to update. Here's an example:
106 |
107 | ```php
108 |
109 | $repo = $this->get('es.manager.default.content');
110 | $repo->update(1, ['title' => 'new title']);
111 |
112 | ```
113 |
114 | You can also update fields with script operation, lets say, you want to do some math:
115 |
116 |
117 | ```php
118 |
119 | $repo = $this->get('es.manager.default.product');
120 | $repo->update(1, [], 'ctx._source.stock+=1');
121 |
122 | ```
123 | > Important: when using script update fields cannot be updated, leave empty array, otherwise you will get 400 exception.
124 |
125 | `ctx._source` comes from groovy scripting and you have to enable it in elasticsearch config with: `script.groovy.sandbox.enabled: false`
126 |
127 |
128 | In addition you also can get other document fields with the response of update, lets say we also want a content field and a new title, so just add them separated by a comma:
129 |
130 |
131 | ```php
132 |
133 | $repo = $this->get('es.manager.default.content');
134 | $response = $repo->update(1, ['title' => 'new title'], null, ['fields' => 'title,content']);
135 |
136 | ```
137 |
138 |
139 | ## Delete a document
140 |
141 | Document removal can be performed similarly to create or update action:
142 |
143 | ```php
144 | $manager->remove($content);
145 | $manager->commit();
146 | ```
147 |
148 | Alternatively you can remove document by ID (requires to have repository service):
149 |
150 | ```php
151 |
152 | $repo = $this->get('es.manager.default.content');
153 | $content = $repo->remove(5);
154 |
155 | ```
156 |
--------------------------------------------------------------------------------
/Resources/config/services.yml:
--------------------------------------------------------------------------------
1 | parameters:
2 | es.app_root_class: 'App\Kernel'
3 | es.logging.path: "%kernel.logs_dir%/elasticsearch_%kernel.environment%.log"
4 |
5 | services:
6 | es.export:
7 | class: 'ONGR\ElasticsearchBundle\Service\ExportService'
8 | public: true
9 |
10 | es.import:
11 | class: 'ONGR\ElasticsearchBundle\Service\ImportService'
12 | public: true
13 |
14 | es.client.index_suffix_finder:
15 | class: 'ONGR\ElasticsearchBundle\Service\IndexSuffixFinder'
16 | public: true
17 |
18 | es.annotations.cached_reader:
19 | class: 'Doctrine\Common\Annotations\PsrCachedReader'
20 | public: true
21 | arguments: ["@es.annotations.reader", "@es.cache_engine", "%kernel.debug%"]
22 |
23 | es.document_finder:
24 | class: 'ONGR\ElasticsearchBundle\Mapping\DocumentFinder'
25 | public: true
26 | arguments: ["%kernel.bundles%", "%es.app_root_class%"]
27 |
28 | es.document_parser:
29 | class: 'ONGR\ElasticsearchBundle\Mapping\DocumentParser'
30 | public: true
31 | arguments: ["@es.annotations.cached_reader", "@es.document_finder"]
32 |
33 | es.metadata_collector:
34 | class: 'ONGR\ElasticsearchBundle\Mapping\MetadataCollector'
35 | public: true
36 | arguments: ["@es.document_finder", "@es.document_parser", "@es.cache_engine"]
37 | calls:
38 | - [setEnableCache, ["%es.cache%"]]
39 |
40 | es.logger.collection_handler:
41 | class: 'ONGR\ElasticsearchBundle\Profiler\Handler\CollectionHandler'
42 | public: false
43 |
44 | es.tracer:
45 | class: 'Monolog\Logger'
46 | public: true
47 | arguments: ['ongr']
48 | calls:
49 | - ['pushHandler', ["@es.logger.collection_handler"]]
50 |
51 | es.profiler:
52 | class: 'ONGR\ElasticsearchBundle\Profiler\ElasticsearchProfiler'
53 | public: true
54 | calls:
55 | - ['setManagers', ["%es.managers%"]]
56 | - ['addLogger', ["@es.tracer"]]
57 | tags:
58 | - { name: 'data_collector', template: "@ONGRElasticsearch/Profiler/profiler.html.twig", id: 'ongr.profiler' }
59 |
60 | es.result_converter:
61 | class: 'ONGR\ElasticsearchBundle\Result\Converter'
62 | public: true
63 | arguments: ["@es.metadata_collector"]
64 |
65 | es.terminate_listener:
66 | class: 'ONGR\ElasticsearchBundle\EventListener\TerminateListener'
67 | public: true
68 | arguments: ["@service_container", "%es.managers%"]
69 | tags:
70 | - { name: 'kernel.event_listener', event: 'kernel.terminate' }
71 |
72 | es.generator.document:
73 | class: 'ONGR\ElasticsearchBundle\Generator\DocumentGenerator'
74 | public: true
75 |
76 | es.generate:
77 | class: 'ONGR\ElasticsearchBundle\Service\GenerateService'
78 | public: true
79 | arguments: ["@es.generator.document", "@filesystem"]
80 |
81 | es.command.cache_clear:
82 | class: 'ONGR\ElasticsearchBundle\Command\CacheClearCommand'
83 | tags:
84 | - { name: 'console.command', command: 'ongr:es:cache:clear' }
85 |
86 | es.command.document_generate:
87 | class: 'ONGR\ElasticsearchBundle\Command\DocumentGenerateCommand'
88 | arguments:
89 | - '%kernel.bundles%'
90 | - '@es.generate'
91 | - '@es.metadata_collector'
92 | - '@es.annotations.cached_reader'
93 | tags:
94 | - { name: 'console.command', command: 'ongr:es:document:generate' }
95 |
96 | es.command.index_create:
97 | class: 'ONGR\ElasticsearchBundle\Command\IndexCreateCommand'
98 | arguments:
99 | - '@es.client.index_suffix_finder'
100 | tags:
101 | - { name: 'console.command', command: 'ongr:es:index:create' }
102 |
103 | es.command.index_export:
104 | class: 'ONGR\ElasticsearchBundle\Command\IndexExportCommand'
105 | arguments:
106 | - '@es.export'
107 | tags:
108 | - { name: 'console.command', command: 'ongr:es:index:export' }
109 |
110 | es.command.index_import:
111 | class: 'ONGR\ElasticsearchBundle\Command\IndexImportCommand'
112 | arguments:
113 | - '@es.import'
114 | tags:
115 | - { name: 'console.command', command: 'ongr:es:index:import' }
116 |
117 | es.command.index_drop:
118 | class: 'ONGR\ElasticsearchBundle\Command\IndexDropCommand'
119 | tags:
120 | - { name: 'console.command', command: 'ongr:es:index:drop' }
121 |
--------------------------------------------------------------------------------
/Resources/doc/results_parsing.md:
--------------------------------------------------------------------------------
1 | # Working with results
2 |
3 | > This chapter covers what comes from [find](find_functions.md) and [search](search.md) requests.
4 |
5 | For all chapters below we will use a data example inserted in the elasticsearch content type:
6 |
7 | ```
8 | // content type
9 |
10 | {
11 | "id": 1,
12 | "title": "Dr. Damien Yundt DVM"
13 | },
14 | {
15 | "id": 2,
16 | "title": "Zane Heidenreich IV"
17 | },
18 | {
19 | "id": 3,
20 | "title": "Hattie Shields MD"
21 | }
22 |
23 | ```
24 |
25 | ## Results iterator
26 |
27 | Usually when any search action is performed the `DocumentIterator` will be returned. It has plenty of helper functions to aggregate more efficiently with the results.
28 |
29 |
30 | Lets assume you search the index with:
31 |
32 | ```php
33 |
34 | $repo = $this->get('es.manager.default.content');
35 | $search = $repo->createSearch();
36 | $termQuery = new MatchAllQuery();
37 | $results = $repo->findDocuments($search);
38 |
39 | ```
40 |
41 | So all 3 content elements will be found. `DocumentIterator`implements [`\Countable`](http://php.net/manual/en/class.countable.php), [`\Iterator`](http://php.net/manual/en/class.iterator.php) interfaces functions. The results are traversable and you can run `foreach` cycle through it.
42 |
43 | ```php
44 |
45 | echo $results->count() . "\n";
46 |
47 | /** @var AppBundle:Content $document */
48 | foreach ($results as $document) {
49 | echo $document->title . "\n";
50 | }
51 |
52 | ```
53 |
54 | it will print:
55 |
56 | ```
57 | 3
58 | Dr. Damien Yundt DVM
59 | Zane Heidenreich IV
60 | Hattie Shields MD
61 | ```
62 |
63 | ### Getting Document Score
64 |
65 | In most cases Elasticsearch returns result score (`_score` field) for each document.
66 | As this score might be different per search it's not treated as document field and
67 | cannot be associated with document. You can get document's score from results iterator
68 | while iterating:
69 |
70 | ```php
71 | $results = $repository->findDocuments($search);
72 |
73 | foreach ($results as $document) {
74 | echo $document->title, $results->getDocumentScore();
75 | }
76 | ```
77 |
78 | Example above prints titles of all documents following search score.
79 |
80 | ### Getting Document Sort
81 |
82 | Similarly to Document score, during iteration you can get document sort, provided you
83 | added a sort to your search, you can retrieve your sort value while iterating the
84 | results:
85 |
86 | ```php
87 | $results = $repository->execute($search);
88 |
89 | foreach ($results as $document) {
90 | echo $document->title, $results->getDocumentSort();
91 | }
92 | ```
93 |
94 | #### Important notice
95 |
96 | `DocumentIterator` doesn't cache or store generated document object. `Converter` directly returns the instance after it's requested and will generate again if it will be requested.
97 |
98 | We highly recommend to `unset()` document instance after you don't need it or manage memory at your own way.
99 |
100 | There is a possibility to change the `DocumentIterator` behaviour. Take a look at the [overwriting bundle parts](http://docs.ongr.io/ElasticsearchBundle/overwriting_bundle).
101 |
102 | ## Aggregations
103 |
104 | If your search query includes aggregations you can reach calculated aggregations
105 | by calling `getAggregation()` method.
106 |
107 | In example below we show how to build query with aggregations and how to handle
108 | aggregation results:
109 |
110 | ```php
111 | $avgPriceAggregation = new AvgAggregation('avg_price');
112 | $avgPriceAggregation->setField('price');
113 |
114 | $brandTermAggregation = new TermsAggregation('brand');
115 | $brandTermAggregation->setField('manufacturer');
116 | $brandTermAggregation->addAggregation($avgPriceAggregation);
117 |
118 | $query = new Search();
119 | $query->addAggregation($brandTermAggregation);
120 |
121 | $result = $this->get('es.manager.default.content')->findDocuments($query);
122 |
123 | // Build a list of available choices
124 | $choices = [];
125 |
126 | foreach ($result->getAggregation('brand') as $bucket) {
127 | $choices[] = [
128 | 'brand' => $bucket['key'],
129 | 'count' => $bucket['doc_count'],
130 | 'avg_price' => $bucket->find('avg_price')['value'],
131 | ];
132 | }
133 |
134 | var_export($choices);
135 | ```
136 |
137 | The example above will print similar result:
138 |
139 | ```
140 | array (
141 | 0 =>
142 | array (
143 | 'brand' => 'Terre Cortesi Moncaro',
144 | 'count' => 7,
145 | 'avg_price' => 20.42714282444545,
146 | ),
147 | 1 =>
148 | array (
149 | 'brand' => 'Casella Wines',
150 | 'count' => 4,
151 | 'avg_price' => 10.47249972820282,
152 | )
153 | )
154 | ```
155 |
--------------------------------------------------------------------------------
/Tests/Unit/Result/ConverterTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Unit\Result;
13 |
14 | use ONGR\ElasticsearchBundle\Mapping\Caser;
15 | use ONGR\ElasticsearchBundle\Mapping\MetadataCollector;
16 | use ONGR\ElasticsearchBundle\Result\Converter;
17 | use ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product;
18 | use PHPUnit\Framework\TestCase;
19 |
20 | class ConverterTest extends TestCase
21 | {
22 | /**
23 | * @var MetadataCollector
24 | */
25 | private $metadataCollector;
26 |
27 | /**
28 | * @var Converter
29 | */
30 | private $converter;
31 |
32 | /**
33 | * @inheritdoc
34 | */
35 | protected function setUp(): void
36 | {
37 | $this->metadataCollector = $this->getMockBuilder('ONGR\ElasticsearchBundle\Mapping\MetadataCollector')
38 | ->disableOriginalConstructor()
39 | ->getMock();
40 |
41 | $this->converter = new Converter($this->metadataCollector);
42 | }
43 |
44 | public function dataProviderForAssignArrayToObjectWhenDateIsObject()
45 | {
46 | return [
47 | # Case 0.
48 | [
49 | [
50 | 'title' => 'Foo',
51 | ],
52 | ],
53 | # Case 1.
54 | [
55 | [
56 | 'title' => 'Boo',
57 | 'released' => new \DateTime(),
58 | ],
59 | ],
60 | # Case 2.
61 | [
62 | [
63 | 'title' => 'Bar',
64 | 'released' => null,
65 | ]
66 | ],
67 | # Case 3.
68 | [
69 | [
70 | 'limited' => null,
71 | ],
72 | [
73 | 'limited' => false,
74 | ]
75 | ],
76 | # Case 4.
77 | [
78 | [
79 | 'limited' => 1,
80 | ],
81 | [
82 | 'limited' => true,
83 | ]
84 | ],
85 | # Case 5.
86 | [
87 | [
88 | 'limited' => true,
89 | ],
90 | [
91 | 'limited' => true,
92 | ]
93 | ]
94 | ];
95 | }
96 |
97 | /**
98 | * Tests array conversion to the object.
99 | *
100 | * @param array $product
101 | * @param array $expected
102 | *
103 | * @dataProvider dataProviderForAssignArrayToObjectWhenDateIsObject
104 | */
105 | public function testAssignArrayToObjectWhenDateIsObject($product, $expected = [])
106 | {
107 | $aliases = [
108 | 'title' => [
109 | 'propertyName' => 'title',
110 | 'type' => 'text',
111 | 'hashmap' => false,
112 | 'methods' => [
113 | 'getter' => 'getTitle',
114 | 'setter' => 'setTitle',
115 | ],
116 | 'propertyType' => 'private',
117 | ],
118 | 'released' => [
119 | 'propertyName' => 'released',
120 | 'type' => 'datetime',
121 | 'hashmap' => false,
122 | 'methods' => [
123 | 'getter' => 'getReleased',
124 | 'setter' => 'setReleased',
125 | ],
126 | 'propertyType' => 'private',
127 |
128 | ],
129 | 'limited' => [
130 | 'propertyName' => 'limited',
131 | 'type' => 'boolean',
132 | 'hashmap' => false,
133 | 'methods' => [
134 | 'getter' => 'getLimited',
135 | 'setter' => 'setLimited',
136 | ],
137 | 'propertyType' => 'private',
138 |
139 | ]
140 | ];
141 | /** @var Product $productDocument */
142 | $productDocument = $this->converter->assignArrayToObject($product, new Product(), $aliases);
143 |
144 | foreach (array_keys($product) as $key) {
145 | $expect = isset($expected[$key]) ? $expected[$key] : $product[$key];
146 | $this->assertEquals($expect, $productDocument->{'get'.Caser::snake($key)}());
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/Tests/Functional/Command/IndexImportCommandTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Tests\Functional\Command;
13 |
14 | use ONGR\ElasticsearchBundle\Command\IndexImportCommand;
15 | use ONGR\ElasticsearchBundle\Tests\app\fixture\TestBundle\Document\Product;
16 | use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
17 | use ONGR\ElasticsearchBundle\Test\AbstractElasticsearchTestCase;
18 | use Symfony\Component\Console\Application;
19 | use Symfony\Component\Console\Tester\CommandTester;
20 |
21 | class IndexImportCommandTest extends AbstractElasticsearchTestCase
22 | {
23 | /**
24 | * Data provider for testIndexImport.
25 | *
26 | * @return array
27 | */
28 | public function bulkSizeProvider()
29 | {
30 | return [
31 | [10, 9, 'command_import_9.json'],
32 | [10, 10, 'command_import_10.json'],
33 | [10, 11, 'command_import_11.json'],
34 | [5, 20, 'command_import_20.json'],
35 | ];
36 | }
37 |
38 | /**
39 | * Compressed Data provider for testIndexImport.
40 | *
41 | * @return array
42 | */
43 | public function compressedDataProvider()
44 | {
45 | return [
46 | [10, 9, 'command_import_9.json.gz'],
47 | [10, 10, 'command_import_10.json.gz'],
48 | [10, 11, 'command_import_11.json.gz'],
49 | ];
50 | }
51 |
52 | /**
53 | * Test for index import command.
54 | *
55 | * @param int $bulkSize
56 | * @param int $realSize
57 | * @param string $filename
58 | *
59 | * @dataProvider bulkSizeProvider
60 | */
61 | public function testIndexImport($bulkSize, $realSize, $filename)
62 | {
63 | $manager = $this->getManager();
64 | $app = new Application();
65 | $app->add($this->getImportCommand());
66 |
67 | $command = $app->find('ongr:es:index:import');
68 | $commandTester = new CommandTester($command);
69 | $commandTester->execute(
70 | [
71 | 'command' => $command->getName(),
72 | 'filename' => __DIR__ . '/../../app/fixture/data/' . $filename,
73 | '--bulk-size' => $bulkSize,
74 | ]
75 | );
76 |
77 | $repo = $manager->getRepository('TestBundle:Product');
78 | $search = $repo
79 | ->createSearch()
80 | ->addQuery(new MatchAllQuery())
81 | ->setSize($realSize);
82 | $results = $repo->findDocuments($search);
83 |
84 | $ids = [];
85 | /** @var Product $doc */
86 | foreach ($results as $doc) {
87 | $ids[] = substr($doc->getId(), 3);
88 | }
89 | sort($ids);
90 | $data = range(1, $realSize);
91 | $this->assertEquals($data, $ids);
92 | }
93 |
94 | /**
95 | * Test for index import command with gzip option.
96 | *
97 | * @param int $bulkSize
98 | * @param int $realSize
99 | * @param string $filename
100 | *
101 | * @dataProvider compressedDataProvider
102 | */
103 | public function testIndexImportWithGzipOption($bulkSize, $realSize, $filename)
104 | {
105 | $manager = $this->getManager();
106 |
107 | $app = new Application();
108 | $app->add($this->getImportCommand());
109 |
110 | $command = $app->find('ongr:es:index:import');
111 | $commandTester = new CommandTester($command);
112 | $commandTester->execute(
113 | [
114 | 'command' => $command->getName(),
115 | 'filename' => __DIR__ . '/../../app/fixture/data/' . $filename,
116 | '--bulk-size' => $bulkSize,
117 | '--gzip' => null,
118 | ]
119 | );
120 |
121 | $repo = $manager->getRepository('TestBundle:Product');
122 | $search = $repo
123 | ->createSearch()
124 | ->addQuery(new MatchAllQuery())
125 | ->setSize($realSize);
126 | $results = $repo->findDocuments($search);
127 |
128 | $ids = [];
129 | /** @var Product $doc */
130 | foreach ($results as $doc) {
131 | $ids[] = substr($doc->getId(), 3);
132 | }
133 | sort($ids);
134 | $data = range(1, $realSize);
135 | $this->assertEquals($data, $ids);
136 | }
137 |
138 | /**
139 | * Returns import index command with assigned container.
140 | *
141 | * @return IndexImportCommand
142 | */
143 | private function getImportCommand()
144 | {
145 | return new IndexImportCommand(
146 | $this->getKernelContainer()->get('es.import'),
147 | ['es.manager.default' => $this->getManager()]
148 | );
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/Service/ExportService.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace ONGR\ElasticsearchBundle\Service;
13 |
14 | use Elasticsearch\Helper\Iterators\SearchHitIterator;
15 | use Elasticsearch\Helper\Iterators\SearchResponseIterator;
16 | use ONGR\ElasticsearchBundle\Result\RawIterator;
17 | use ONGR\ElasticsearchBundle\Service\Json\JsonWriter;
18 | use ONGR\ElasticsearchDSL\Query\MatchAllQuery;
19 | use ONGR\ElasticsearchDSL\Search;
20 | use ONGR\ElasticsearchDSL\Sort\FieldSort;
21 | use Symfony\Component\Console\Helper\ProgressBar;
22 | use Symfony\Component\Console\Output\OutputInterface;
23 |
24 | /**
25 | * ExportService class.
26 | */
27 | class ExportService
28 | {
29 | /**
30 | * Exports es index to provided file.
31 | *
32 | * @param Manager $manager
33 | * @param string $filename
34 | * @param array $types
35 | * @param int $chunkSize
36 | * @param int $maxLinesInFile
37 | * @param OutputInterface $output
38 | */
39 | public function exportIndex(
40 | Manager $manager,
41 | $filename,
42 | $types,
43 | $chunkSize,
44 | OutputInterface $output,
45 | $maxLinesInFile = 300000
46 | ) {
47 |
48 | $search = new Search();
49 | $search->addQuery(new MatchAllQuery());
50 | $search->setSize($chunkSize);
51 | $search->addSort(new FieldSort('_doc'));
52 |
53 | $queryParameters = [
54 | '_source' => true,
55 | 'scroll' => '10m',
56 | ];
57 |
58 | $searchResults = $manager->search($types, $search->toArray(), $queryParameters);
59 |
60 | $results = new RawIterator(
61 | $searchResults,
62 | $manager,
63 | [
64 | 'duration' => $queryParameters['scroll'],
65 | '_scroll_id' => $searchResults['_scroll_id'],
66 | ]
67 | );
68 |
69 | $progress = new ProgressBar($output, $results->count());
70 | $progress->setRedrawFrequency(100);
71 | $progress->start();
72 |
73 | $counter = $fileCounter = 0;
74 | $count = $this->getFileCount($results->count(), $maxLinesInFile, $fileCounter);
75 |
76 | $date = date(\DateTime::ISO8601);
77 | $metadata = [
78 | 'count' => $count,
79 | 'date' => $date,
80 | ];
81 |
82 | $filename = str_replace('.json', '', $filename);
83 | $writer = $this->getWriter($this->getFilePath($filename.'.json'), $metadata);
84 |
85 | foreach ($results as $data) {
86 | if ($counter >= $maxLinesInFile) {
87 | $writer->finalize();
88 | $writer = null;
89 | $fileCounter++;
90 | $count = $this->getFileCount($results->count(), $maxLinesInFile, $fileCounter);
91 | $metadata = [
92 | 'count' => $count,
93 | 'date' => $date,
94 | ];
95 | $writer = $this->getWriter($this->getFilePath($filename."_".$fileCounter.".json"), $metadata);
96 | $counter = 0;
97 | }
98 |
99 | $doc = array_intersect_key($data, array_flip(['_id', '_type', '_source']));
100 | $writer->push($doc);
101 | $progress->advance();
102 | $counter++;
103 | }
104 |
105 | $writer->finalize();
106 | $progress->finish();
107 | $output->writeln('');
108 | }
109 |
110 | /**
111 | * Returns real file path.
112 | *
113 | * @param string $filename
114 | *
115 | * @return string
116 | */
117 | protected function getFilePath($filename)
118 | {
119 | if ($filename[0] == '/' || strstr($filename, ':') !== false) {
120 | return $filename;
121 | }
122 |
123 | return getcwd() . '/' . $filename;
124 | }
125 |
126 | /**
127 | * Prepares JSON writer.
128 | *
129 | * @param string $filename
130 | * @param array $metadata
131 | *
132 | * @return JsonWriter
133 | */
134 | protected function getWriter($filename, $metadata)
135 | {
136 | return new JsonWriter($filename, $metadata);
137 | }
138 |
139 | /**
140 | * @param int $resultsCount
141 | * @param int $maxLinesInFile
142 | * @param int $fileCounter
143 | *
144 | * @return int
145 | */
146 | protected function getFileCount($resultsCount, $maxLinesInFile, $fileCounter)
147 | {
148 | $leftToInsert = $resultsCount - ($fileCounter * $maxLinesInFile);
149 | if ($leftToInsert <= $maxLinesInFile) {
150 | $count = $leftToInsert;
151 | } else {
152 | $count = $maxLinesInFile;
153 | }
154 |
155 | return $count;
156 | }
157 | }
158 |
--------------------------------------------------------------------------------