├── SolrException.php
├── Tests
├── Fixtures
│ ├── NotIndexedEntity.php
│ ├── InvalidEntityRepository.php
│ ├── ValidEntityRepository.php
│ ├── ValidTestEntityNoBoost.php
│ ├── ValidTestEntityFloatBoost.php
│ ├── ValidTestEntityWithInvalidBoost.php
│ ├── EntityWithRepository.php
│ ├── InvalidTestEntityFiltered.php
│ ├── EntityWithInvalidRepository.php
│ ├── ValidTestEntityNoTypes.php
│ ├── ValidTestEntityIndexProperty.php
│ ├── ValidTestEntityIndexHandler.php
│ ├── EntityWithCustomId.php
│ ├── PartialUpdateEntity.php
│ ├── ValidTestEntityNumericFields.php
│ ├── ValidTestEntityFiltered.php
│ ├── EntityCore0.php
│ ├── EntityCore1.php
│ ├── NestedEntity.php
│ ├── ValidTestEntityWithRelation.php
│ ├── EntityNestedProperty.php
│ ├── ValidTestEntityWithCollection.php
│ ├── ValidTestEntityAllCores.php
│ ├── ValidOdmTestDocument.php
│ └── ValidTestEntity.php
├── Doctrine
│ ├── Mapper
│ │ ├── SolrDocumentStub.php
│ │ └── MetaInformationTest.php
│ ├── Hydration
│ │ ├── NoDatabaseValueHydratorTest.php
│ │ ├── DoctrineValueHydratorTest.php
│ │ └── ValueHydratorTest.php
│ ├── Annotation
│ │ └── FieldTest.php
│ ├── ClassnameResolver
│ │ ├── KnownNamespaceAliasesTest.php
│ │ └── ClassnameResolverTest.php
│ └── ORM
│ │ └── Listener
│ │ └── EntityIndexerSubscriberTest.php
├── Integration
│ ├── SolariumClientFake.php
│ └── Entity
│ │ ├── Tag.php
│ │ └── Category.php
├── Util
│ ├── EntityIdentifier.php
│ ├── CommandFactoryStub.php
│ └── MetaTestInformationFactory.php
├── bootstrap.php
├── ResultFake.php
├── SolrResponseFake.php
├── DocumentStub.php
├── Query
│ ├── FindByIdentifierQueryTest.php
│ └── FindByDocumentNameQueryTest.php
├── SolrClientFake.php
├── Client
│ └── Solarium
│ │ └── SolariumClientBuilderTest.php
├── DependencyInjection
│ └── FSSolrExtensionTest.php
├── MulticoreSolrTest.php
└── AbstractSolrTest.php
├── Query
├── Exception
│ ├── QueryException.php
│ └── UnknownFieldException.php
├── DeleteDocumentQuery.php
├── FindByIdentifierQuery.php
├── FindByDocumentNameQuery.php
├── AbstractQuery.php
└── QueryBuilderInterface.php
├── Doctrine
├── Mapper
│ ├── SolrMappingException.php
│ ├── EntityMapperInterface.php
│ ├── EntityMapper.php
│ └── MetaInformationInterface.php
├── Annotation
│ ├── AnnotationReaderException.php
│ ├── Nested.php
│ ├── SynchronizationFilter.php
│ ├── Id.php
│ ├── Document.php
│ └── Field.php
├── Hydration
│ ├── PropertyAccessor
│ │ ├── PropertyAccessorInterface.php
│ │ ├── MethodCallPropertyAccessor.php
│ │ └── PrivatePropertyAccessor.php
│ ├── HydrationModes.php
│ ├── NoDatabaseValueHydrator.php
│ ├── DoctrineValueHydrator.php
│ ├── HydratorInterface.php
│ ├── IndexHydrator.php
│ ├── DoctrineHydratorFactory.php
│ ├── DoctrineHydrator.php
│ └── ValueHydrator.php
├── ClassnameResolver
│ ├── ClassnameResolverException.php
│ ├── KnownNamespaceAliases.php
│ └── ClassnameResolver.php
├── ODM
│ └── Listener
│ │ └── DocumentIndexerSubscriber.php
├── AbstractIndexingListener.php
└── ORM
│ └── Listener
│ └── EntityIndexerSubscriber.php
├── Client
├── Builder.php
└── Solarium
│ ├── Plugin
│ ├── RequestDebugger.php
│ └── LoggerPlugin.php
│ ├── SolariumMulticoreClient.php
│ └── SolariumClientBuilder.php
├── .gitignore
├── Logging
├── SolrLoggerInterface.php
└── DebugLogger.php
├── Event
├── Listener
│ ├── ClearIndexLogListener.php
│ ├── InsertLogListener.php
│ ├── DeleteLogListener.php
│ ├── UpdateLogListener.php
│ ├── ErrorLogListener.php
│ └── AbstractLogListener.php
├── Events.php
├── ErrorEvent.php
└── Event.php
├── phpunit.xml.dist
├── FSSolrBundle.php
├── Repository
├── RepositoryInterface.php
└── Repository.php
├── .travis.yml
├── Resources
├── views
│ └── Profiler
│ │ └── icon.svg
├── config
│ ├── event_listener.xml
│ └── log_listener.xml
└── doc
│ ├── index_relations.md
│ └── indexing.md
├── LICENSE
├── DependencyInjection
├── Compiler
│ └── AddSolariumPluginsPass.php
├── Configuration.php
└── FSSolrExtension.php
├── Command
├── ClearIndexCommand.php
└── ShowSchemaCommand.php
├── composer.json
├── SolrInterface.php
├── Helper
└── DocumentHelper.php
└── DataCollector
└── RequestCollector.php
/SolrException.php:
--------------------------------------------------------------------------------
1 | logger->debug(sprintf('clear index'));
18 | }
19 | }
--------------------------------------------------------------------------------
/Doctrine/Hydration/NoDatabaseValueHydrator.php:
--------------------------------------------------------------------------------
1 | id;
30 | }
31 | }
--------------------------------------------------------------------------------
/Tests/ResultFake.php:
--------------------------------------------------------------------------------
1 | data = $data;
13 | }
14 |
15 | public function getIterator()
16 | {
17 | return new \ArrayIterator($this->data);
18 | }
19 |
20 | public function count()
21 | {
22 | return count($this->data);
23 | }
24 |
25 | public function getNumFound()
26 | {
27 | return $this->count();
28 | }
29 | }
--------------------------------------------------------------------------------
/Tests/SolrResponseFake.php:
--------------------------------------------------------------------------------
1 | response = $response;
11 | }
12 |
13 | /**
14 | * @return array
15 | */
16 | public function getResponse()
17 | {
18 | return $this->response;
19 | }
20 |
21 | /**
22 | * @param array
23 | */
24 | public function setResponse($response)
25 | {
26 | $this->response = $response;
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/Event/Events.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 |
17 | ./Tests
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Doctrine/Hydration/PropertyAccessor/MethodCallPropertyAccessor.php:
--------------------------------------------------------------------------------
1 | setterName = $setterName;
18 | }
19 |
20 | /**
21 | * {@inheritdoc}
22 | */
23 | public function setValue($targetObject, $value)
24 | {
25 | $targetObject->{$this->setterName}($value);
26 | }
27 | }
--------------------------------------------------------------------------------
/Tests/Fixtures/PartialUpdateEntity.php:
--------------------------------------------------------------------------------
1 | subtitle;
22 | }
23 |
24 | /**
25 | * @param string $subtitle
26 | */
27 | public function setSubtitle($subtitle)
28 | {
29 | $this->subtitle = $subtitle;
30 | }
31 | }
--------------------------------------------------------------------------------
/FSSolrBundle.php:
--------------------------------------------------------------------------------
1 | addCompilerPass(new AddSolariumPluginsPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Repository/RepositoryInterface.php:
--------------------------------------------------------------------------------
1 | getMetaInformation();
18 |
19 | $nameWithId = $this->createDocumentNameWithId($metaInformation);
20 | $fieldList = $this->createFieldList($metaInformation);
21 |
22 | $this->logger->debug(
23 | sprintf('document %s with fields %s was added', $nameWithId, $fieldList)
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Doctrine/Hydration/DoctrineValueHydrator.php:
--------------------------------------------------------------------------------
1 | getField($fieldName) && $metaInformation->getField($fieldName)->getter) {
21 | return false;
22 | }
23 |
24 | return true;
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/Event/Listener/DeleteLogListener.php:
--------------------------------------------------------------------------------
1 | getMetaInformation();
19 |
20 | $nameWithId = $this->createDocumentNameWithId($metaInformation);
21 | $fieldList = $this->createFieldList($metaInformation);
22 |
23 | $this->logger->debug(
24 | sprintf('document %s with fields %s was deleted', $nameWithId, $fieldList)
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Event/Listener/UpdateLogListener.php:
--------------------------------------------------------------------------------
1 | getMetaInformation();
19 |
20 | $nameWithId = $this->createDocumentNameWithId($metaInformation);
21 | $fieldList = $this->createFieldList($metaInformation);
22 |
23 | $this->logger->debug(
24 | sprintf('document %s with fields %s was updated', $nameWithId, $fieldList)
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | sudo: false
3 |
4 | php:
5 | - 7.0
6 | - 7.1
7 | - 7.2
8 | - 7.3
9 |
10 | before_script:
11 | - curl -sSL https://raw.githubusercontent.com/floriansemm/travis-solr/master/travis-solr.sh | SOLR_CORE=core0 DEBUG=1 SOLR_VERSION=4.8.0 SOLR_CONFS="Tests/Resources/config/schema.xml" bash
12 |
13 | install:
14 | - composer self-update
15 | - if [[ ${TRAVIS_PHP_VERSION:0:2} == "5." ]]; then yes '' | pecl -q install -f mongo; fi
16 | - if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then pecl install -f mongodb; fi
17 | - if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then composer config "platform.ext-mongo" "1.6.16" && composer require alcaeus/mongo-php-adapter; fi
18 | - composer update
19 |
20 | script:
21 | - ./bin/phpunit
22 |
--------------------------------------------------------------------------------
/Doctrine/ClassnameResolver/ClassnameResolverException.php:
--------------------------------------------------------------------------------
1 | classProperty = $classProperty;
19 | }
20 |
21 | /**
22 | * {@inheritdoc}
23 | */
24 | public function setValue($targetObject, $value)
25 | {
26 | $this->classProperty->setAccessible(true);
27 | $this->classProperty->setValue($targetObject, $value);
28 | }
29 | }
--------------------------------------------------------------------------------
/Tests/Fixtures/ValidTestEntityFiltered.php:
--------------------------------------------------------------------------------
1 | shouldBeIndexedWasCalled = true;
27 |
28 | return $this->shouldIndex;
29 | }
30 |
31 | public function getShouldBeIndexedWasCalled()
32 | {
33 | return $this->shouldBeIndexedWasCalled;
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Event/ErrorEvent.php:
--------------------------------------------------------------------------------
1 | exception;
22 | }
23 |
24 | /**
25 | * @param \Exception $exception
26 | */
27 | public function setException($exception)
28 | {
29 | $this->exception = $exception;
30 | }
31 |
32 | /**
33 | * @return string
34 | */
35 | public function getExceptionMessage()
36 | {
37 | if (!$this->exception) {
38 | return '';
39 | }
40 |
41 | return $this->exception->getMessage();
42 | }
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/Doctrine/Annotation/Document.php:
--------------------------------------------------------------------------------
1 | boost;
39 | }
40 |
41 | /**
42 | * @return string
43 | */
44 | public function getIndex()
45 | {
46 | return $this->index;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Doctrine/Hydration/HydratorInterface.php:
--------------------------------------------------------------------------------
1 | getExceptionMessage();
21 | }
22 |
23 | $this->logger->error(
24 | sprintf('the error "%s" occure while executing event %s', $exceptionMessage, $event->getSolrAction())
25 | );
26 |
27 | if ($event->hasSourceEvent()) {
28 | $event->getSourceEvent()->stopPropagation();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Query/DeleteDocumentQuery.php:
--------------------------------------------------------------------------------
1 | documentKey = $documentKey;
20 | }
21 |
22 | /**
23 | * @return string
24 | *
25 | * @throws QueryException when id or document_name is null
26 | */
27 | public function getQuery()
28 | {
29 | $idField = $this->documentKey;
30 |
31 | if ($idField == null) {
32 | throw new QueryException('id should not be null');
33 | }
34 |
35 | $this->setQuery(sprintf('id:%s', $idField));
36 |
37 | return parent::getQuery();
38 | }
39 | }
--------------------------------------------------------------------------------
/Client/Solarium/Plugin/RequestDebugger.php:
--------------------------------------------------------------------------------
1 | logger = $logger;
27 |
28 | parent::__construct();
29 | }
30 |
31 | /**
32 | * @param PreExecuteRequest $event
33 | */
34 | public function preExecuteRequest(PreExecuteRequest $event)
35 | {
36 | $this->logger->info(sprintf('run request: %s', urldecode($event->getRequest()->getUri())));
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/Resources/views/Profiler/icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Doctrine/Mapper/EntityMapperInterface.php:
--------------------------------------------------------------------------------
1 | add(new MapAllFieldsCommand(new MetaInformationFactory($reader)), 'all');
22 | $commandFactory->add(new MapIdentifierCommand(), 'identifier');
23 |
24 | return $commandFactory;
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/Tests/Fixtures/EntityCore0.php:
--------------------------------------------------------------------------------
1 | id;
30 | }
31 |
32 | /**
33 | * @return string
34 | */
35 | public function getText()
36 | {
37 | return $this->text;
38 | }
39 |
40 | /**
41 | * @param mixed $id
42 | */
43 | public function setId($id)
44 | {
45 | $this->id = $id;
46 | }
47 |
48 | /**
49 | * @param string $text
50 | */
51 | public function setText($text)
52 | {
53 | $this->text = $text;
54 | }
55 |
56 |
57 | }
--------------------------------------------------------------------------------
/Tests/Fixtures/EntityCore1.php:
--------------------------------------------------------------------------------
1 | id;
30 | }
31 |
32 | /**
33 | * @return string
34 | */
35 | public function getText()
36 | {
37 | return $this->text;
38 | }
39 |
40 | /**
41 | * @param mixed $id
42 | */
43 | public function setId($id)
44 | {
45 | $this->id = $id;
46 | }
47 |
48 | /**
49 | * @param string $text
50 | */
51 | public function setText($text)
52 | {
53 | $this->text = $text;
54 | }
55 |
56 |
57 | }
--------------------------------------------------------------------------------
/Doctrine/Hydration/IndexHydrator.php:
--------------------------------------------------------------------------------
1 | valueHydrator = $valueHydrator;
23 | }
24 |
25 | /**
26 | * {@inheritdoc}
27 | */
28 | public function hydrate($document, MetaInformationInterface $metaInformation)
29 | {
30 | $sourceTargetEntity = $metaInformation->getEntity();
31 | $targetEntity = clone $sourceTargetEntity;
32 |
33 | $metaInformation->setEntity($targetEntity);
34 |
35 | return $this->valueHydrator->hydrate($document, $metaInformation);
36 | }
37 | }
--------------------------------------------------------------------------------
/Tests/Doctrine/Mapper/MetaInformationTest.php:
--------------------------------------------------------------------------------
1 | name = $name;
16 | $value->value = $value;
17 |
18 | return $value;
19 | }
20 |
21 | public function testHasCallback_CallbackSet()
22 | {
23 | $information = new MetaInformation();
24 | $information->setSynchronizationCallback('function');
25 |
26 | $this->assertTrue($information->hasSynchronizationFilter(), 'has callback');
27 | }
28 |
29 | public function testHasCallback_NoCallbackSet()
30 | {
31 | $information = new MetaInformation();
32 |
33 | $this->assertFalse($information->hasSynchronizationFilter(), 'has no callback');
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Tests/DocumentStub.php:
--------------------------------------------------------------------------------
1 | $this->id, 'document_name' => $this->document_name_s);
31 |
32 | return $fields[$fieldName];
33 | }
34 |
35 | /**
36 | * @return array
37 | */
38 | public function getFields()
39 | {
40 | return array('id' => $this->id, 'document_name' => $this->document_name_s);
41 | }
42 | }
--------------------------------------------------------------------------------
/Tests/Fixtures/NestedEntity.php:
--------------------------------------------------------------------------------
1 | id;
32 | }
33 |
34 | /**
35 | * @param mixed $id
36 | */
37 | public function setId($id)
38 | {
39 | $this->id = $id;
40 | }
41 |
42 | /**
43 | * @return string
44 | */
45 | public function getName()
46 | {
47 | return $this->name;
48 | }
49 |
50 | /**
51 | * @param string $name
52 | */
53 | public function setName($name)
54 | {
55 | $this->name = $name;
56 | }
57 |
58 |
59 | }
--------------------------------------------------------------------------------
/Query/FindByIdentifierQuery.php:
--------------------------------------------------------------------------------
1 | documentKey = $documentKey;
20 | }
21 |
22 | /**
23 | * @return string
24 | *
25 | * @throws QueryException when id or document_name is null
26 | */
27 | public function getQuery()
28 | {
29 | $idField = $this->documentKey;
30 |
31 | if ($idField == null) {
32 | throw new QueryException('id should not be null');
33 | }
34 |
35 | $documentLimitation = $this->createFilterQuery('id')->setQuery(sprintf('id:%s', $idField));
36 | $this->addFilterQuery($documentLimitation);
37 |
38 | $this->setQuery('*:*');
39 |
40 | return parent::getQuery();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Florian Semm
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 |
--------------------------------------------------------------------------------
/DependencyInjection/Compiler/AddSolariumPluginsPass.php:
--------------------------------------------------------------------------------
1 | findTaggedServiceIds('solarium.client.plugin');
20 |
21 | $clientBuilder = $container->getDefinition('solr.client.adapter.builder');
22 | foreach ($plugins as $service => $definition) {
23 | $clientBuilder->addMethodCall(
24 | 'addPlugin',
25 | array(
26 | $definition[0]['plugin-name'],
27 | new Reference($service)
28 | )
29 | );
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/Doctrine/Hydration/DoctrineHydratorFactory.php:
--------------------------------------------------------------------------------
1 | container = $container;
20 | }
21 |
22 | /**
23 | * @return DoctrineHydrator
24 | */
25 | public function factory()
26 | {
27 | $valueHydrator = $this->container->get('solr.doctrine.hydration.doctrine_value_hydrator');
28 |
29 | $hydrator = new DoctrineHydrator($valueHydrator);
30 | if ($this->container->has('doctrine')) {
31 | $hydrator->setOrmManager($this->container->get('doctrine'));
32 | }
33 |
34 | if ($this->container->has('doctrine_mongodb')) {
35 | $hydrator->setOdmManager($this->container->get('doctrine_mongodb'));
36 | }
37 |
38 | return $hydrator;
39 | }
40 | }
--------------------------------------------------------------------------------
/Logging/DebugLogger.php:
--------------------------------------------------------------------------------
1 | queries;
31 | }
32 |
33 | /**
34 | * {@inheritdoc}
35 | */
36 | public function startRequest(array $request)
37 | {
38 | $this->start = microtime(true);
39 | $this->queries[++$this->currentQuery] = [
40 | 'request' => $request,
41 | 'executionMS' => 0
42 | ];
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | public function stopRequest()
49 | {
50 | $this->queries[$this->currentQuery]['executionMS'] = microtime(true) - $this->start;
51 | }
52 | }
--------------------------------------------------------------------------------
/Event/Listener/AbstractLogListener.php:
--------------------------------------------------------------------------------
1 | logger = $logger;
21 | }
22 |
23 | /**
24 | * @param MetaInformationInterface $metaInformation
25 | *
26 | * @return string
27 | */
28 | protected function createDocumentNameWithId(MetaInformationInterface $metaInformation)
29 | {
30 | return $metaInformation->getDocumentName() . ':' . $metaInformation->getEntityId();
31 | }
32 |
33 | /**
34 | * @param MetaInformationInterface $metaInformation
35 | *
36 | * @return string
37 | */
38 | protected function createFieldList(MetaInformationInterface $metaInformation)
39 | {
40 | return implode(', ', $metaInformation->getFields());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Tests/Query/FindByIdentifierQueryTest.php:
--------------------------------------------------------------------------------
1 | setKey('id', 'validtestentity_1');
18 |
19 | $query = new FindByIdentifierQuery();
20 | $query->setDocumentKey('validtestentity_1');
21 | $query->setDocument($document);
22 |
23 | $this->assertEquals('*:*', $query->getQuery());
24 | $this->assertEquals('id:validtestentity_1', $query->getFilterQuery('id')->getQuery());
25 | }
26 |
27 | /**
28 | * @expectedException FS\SolrBundle\Query\Exception\QueryException
29 | * @expectedExceptionMessage id should not be null
30 | */
31 | public function testGetQuery_IdMissing()
32 | {
33 | $query = new FindByIdentifierQuery();
34 | $query->setDocument(new Document());
35 |
36 | $query->getQuery();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Query/FindByDocumentNameQuery.php:
--------------------------------------------------------------------------------
1 | documentName = $documentName;
25 | }
26 |
27 | /**
28 | * @return string
29 | *
30 | * @throws QueryException if documentName is null
31 | */
32 | public function getQuery()
33 | {
34 | $documentName = $this->documentName;
35 |
36 | if ($documentName == null) {
37 | throw new QueryException('documentName should not be null');
38 | }
39 |
40 | $documentLimitation = $this->createFilterQuery('id')->setQuery(sprintf('id:%s_*', $documentName));
41 | $this->addFilterQuery($documentLimitation);
42 |
43 | $this->setQuery('*:*');
44 |
45 | return parent::getQuery();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Resources/config/event_listener.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Command/ClearIndexCommand.php:
--------------------------------------------------------------------------------
1 | setName('solr:index:clear')
24 | ->setDescription('Clear the whole index');
25 | }
26 |
27 | /**
28 | * {@inheritdoc}
29 | */
30 | protected function execute(InputInterface $input, OutputInterface $output)
31 | {
32 | $solr = $this->getContainer()->get('solr.client');
33 |
34 | try {
35 | $solr->clearIndex();
36 | } catch (SolrException $e) {
37 | $output->writeln(sprintf('A error occurs: %s', $e->getMessage()));
38 | }
39 |
40 | $output->writeln('Index successful cleared.');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Tests/Query/FindByDocumentNameQueryTest.php:
--------------------------------------------------------------------------------
1 | addField('id', 'validtestentity_1');
20 |
21 | $query = new FindByDocumentNameQuery();
22 | $query->setDocumentName('validtestentity');
23 | $query->setDocument($document);
24 |
25 | $this->assertEquals('*:*', $query->getQuery(), 'filter query');
26 | $this->assertEquals('id:validtestentity_*', $query->getFilterQuery('id')->getQuery());
27 | }
28 |
29 | /**
30 | * @expectedException FS\SolrBundle\Query\Exception\QueryException
31 | * @expectedExceptionMessage documentName should not be null
32 | */
33 | public function testGetQuery_DocumentnameMissing()
34 | {
35 | $query = new FindByDocumentNameQuery();
36 | $query->setDocument(new Document());
37 |
38 | $query->getQuery();
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/Tests/Doctrine/Hydration/NoDatabaseValueHydratorTest.php:
--------------------------------------------------------------------------------
1 | '0003115-2231_S',
24 | 'title_t' => 'fooo_bar'
25 | ));
26 |
27 | $entity = new ValidTestEntity();
28 |
29 | $metainformations = new MetaInformationFactory($reader);
30 | $metainformations = $metainformations->loadInformation($entity);
31 |
32 | $entity = $hydrator->hydrate($document, $metainformations);
33 |
34 | $this->assertEquals('0003115-2231_S', $entity->getId());
35 | $this->assertEquals('fooo_bar', $entity->getTitle());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "floriansemm/solr-bundle",
3 | "type": "symfony-bundle",
4 | "description": "Symfony Solr integration bundle",
5 | "keywords": [
6 | "search",
7 | "index",
8 | "symfony",
9 | "solr"
10 | ],
11 | "homepage": "https://github.com/floriansemm/SolrBundle",
12 | "license": "MIT",
13 | "require": {
14 | "php": "^7.0",
15 | "solarium/solarium": "^4.0",
16 | "symfony/dependency-injection": "^2.3|^3.0|^4.0",
17 | "symfony/http-kernel": "^2.3|^3.0|^4.0",
18 | "symfony/config": "^2.3|^3.0|^4.0",
19 | "symfony/doctrine-bridge": "^2.3|^3.0|^4.0",
20 | "minimalcode/search": "^1.0",
21 | "ramsey/uuid": "^3.5",
22 | "myclabs/deep-copy": "^1.6",
23 | "doctrine/annotations": "^1.4"
24 | },
25 | "require-dev": {
26 | "doctrine/mongodb-odm-bundle": "*",
27 | "doctrine/orm": "^2.3",
28 | "phpunit/phpunit": "^5.4"
29 | },
30 | "minimum-stability": "RC",
31 | "autoload": {
32 | "psr-0": {
33 | "FS\\SolrBundle": ""
34 | }
35 | },
36 | "config": {
37 | "bin-dir": "bin"
38 | },
39 | "extras": {
40 | "branch-alias": {
41 | "dev-master": "2.0.x-dev"
42 | }
43 | },
44 | "suggest": {
45 | "doctrine/orm": "Required if you want to use the Doctrine ORM",
46 | "doctrine/mongodb-odm-bundle": "Required if you want to use the MongoDB ODM"
47 | },
48 | "target-dir": "FS/SolrBundle"
49 | }
50 |
--------------------------------------------------------------------------------
/DependencyInjection/Configuration.php:
--------------------------------------------------------------------------------
1 | root('fs_solr');
18 | $rootNode->children()
19 | ->arrayNode('endpoints')
20 | ->useAttributeAsKey('name')
21 | ->prototype('array')
22 | ->children()
23 | ->scalarNode('dsn')->end()
24 | ->scalarNode('scheme')->end()
25 | ->scalarNode('host')->end()
26 | ->scalarNode('port')->end()
27 | ->scalarNode('path')->end()
28 | ->scalarNode('core')->end()
29 | ->scalarNode('timeout')->end()
30 | ->booleanNode('active')->defaultValue(true)->end()
31 | ->end()
32 | ->end()
33 | ->end()
34 | ->booleanNode('auto_index')->defaultValue(true)->end()
35 | ->end();
36 |
37 | return $treeBuilder;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/SolrInterface.php:
--------------------------------------------------------------------------------
1 | assertFalse($hydrator->mapValue('createdAt', array(), new MetaInformation()));
21 | }
22 |
23 | /**
24 | * @test
25 | */
26 | public function skipObjects()
27 | {
28 | $hydrator = new DoctrineValueHydrator();
29 |
30 | $field = new Field(array('type' => 'datetime'));
31 | $field->name = 'createdAt';
32 | $field->getter = 'format(\'Y-m-d\TH:i:s.z\Z\')';
33 |
34 | $metaInformation = new MetaInformation();
35 | $metaInformation->setFields(array($field));
36 |
37 | $this->assertFalse($hydrator->mapValue('createdAt', new \DateTime(), $metaInformation));
38 | }
39 |
40 | /**
41 | * @test
42 | */
43 | public function mapCommonType()
44 | {
45 | $hydrator = new DoctrineValueHydrator();
46 |
47 | $field = new Field(array('type' => 'string'));
48 | $field->name = 'title';
49 |
50 | $metaInformation = new MetaInformation();
51 | $metaInformation->setFields(array($field));
52 |
53 | $this->assertTrue($hydrator->mapValue('title_s', 'a title', $metaInformation));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Tests/Util/MetaTestInformationFactory.php:
--------------------------------------------------------------------------------
1 | setId(2);
22 |
23 | $metaInformation = new MetaInformation();
24 |
25 | $title = new Field(array('name' => 'title', 'boost' => '1.8', 'value' => 'A title'));
26 | $text = new Field(array('name' => 'text', 'type' => 'text', 'value' => 'A text'));
27 | $createdAt = new Field(array('name' => 'created_at', 'type' => 'date', 'boost' => '1', 'value' => 'A created at'));
28 |
29 | $metaInformation->setFields(array($title, $text, $createdAt));
30 |
31 | $fieldMapping = array(
32 | 'id' => 'id',
33 | 'title_s' => 'title',
34 | 'text_t' => 'text',
35 | 'created_at_dt' => 'created_at'
36 | );
37 | $metaInformation->setIdentifier(new Id(array()));
38 | $metaInformation->setBoost(1);
39 | $metaInformation->setFieldMapping($fieldMapping);
40 | $metaInformation->setEntity($entity);
41 | $metaInformation->setDocumentName('validtestentity');
42 | $metaInformation->setClassName(get_class($entity));
43 | $metaInformation->setIndex(null);
44 |
45 | return $metaInformation;
46 | }
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/Event/Event.php:
--------------------------------------------------------------------------------
1 | client = $client;
46 | $this->metainformation = $metainformation;
47 | $this->solrAction = $solrAction;
48 | $this->sourceEvent = $sourceEvent;
49 | }
50 |
51 | /**
52 | * @return MetaInformationInterface
53 | */
54 | public function getMetaInformation()
55 | {
56 | return $this->metainformation;
57 | }
58 |
59 | /**
60 | * @return string
61 | */
62 | public function getSolrAction()
63 | {
64 | return $this->solrAction;
65 | }
66 |
67 | /**
68 | * @return Event
69 | */
70 | public function getSourceEvent()
71 | {
72 | return $this->sourceEvent;
73 | }
74 |
75 | /**
76 | * @return bool
77 | */
78 | public function hasSourceEvent()
79 | {
80 | return $this->sourceEvent !== null;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Doctrine/ODM/Listener/DocumentIndexerSubscriber.php:
--------------------------------------------------------------------------------
1 | getDocument();
27 |
28 | try {
29 | $doctrineChangeSet = $args->getDocumentManager()->getUnitOfWork()->getDocumentChangeSet($document);
30 |
31 | if ($this->hasChanged($doctrineChangeSet, $document) == false) {
32 | return;
33 | }
34 |
35 | $this->solr->updateDocument($document);
36 | } catch (\RuntimeException $e) {
37 | $this->logger->debug($e->getMessage());
38 | }
39 | }
40 |
41 | /**
42 | * @param LifecycleEventArgs $args
43 | */
44 | public function preRemove(LifecycleEventArgs $args)
45 | {
46 | $entity = $args->getDocument();
47 |
48 | try {
49 | $this->solr->removeDocument($entity);
50 | } catch (\RuntimeException $e) {
51 | $this->logger->debug($e->getMessage());
52 | }
53 | }
54 |
55 | /**
56 | * @param LifecycleEventArgs $args
57 | */
58 | public function postPersist(LifecycleEventArgs $args)
59 | {
60 | $entity = $args->getDocument();
61 |
62 | try {
63 | $this->solr->addDocument($entity);
64 | } catch (\RuntimeException $e) {
65 | $this->logger->debug($e->getMessage());
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/Client/Solarium/Plugin/LoggerPlugin.php:
--------------------------------------------------------------------------------
1 | logger = $logger;
27 | }
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | protected function initPluginType()
33 | {
34 | $dispatcher = $this->client->getEventDispatcher();
35 | $dispatcher->addListener(Events::PRE_EXECUTE_REQUEST, [$this, 'preExecuteRequest']);
36 | $dispatcher->addListener(Events::POST_EXECUTE_REQUEST, [$this, 'postExecuteRequest']);
37 | }
38 |
39 | /**
40 | * @param PreExecuteRequest $event
41 | */
42 | public function preExecuteRequest(PreExecuteRequest $event)
43 | {
44 | $endpoint = $event->getEndpoint();
45 | $request = $event->getRequest();
46 | $uri = $request->getUri();
47 |
48 | $path = sprintf('%s://%s:%s%s/%s', $endpoint->getScheme(), $endpoint->getHost(), $endpoint->getPort(), $endpoint->getPath(), urldecode($uri));
49 |
50 | $requestInformation = [
51 | 'uri' => $path,
52 | 'method' => $request->getMethod(),
53 | 'raw_data' => $request->getRawData()
54 | ];
55 |
56 | $this->logger->startRequest($requestInformation);
57 | }
58 |
59 | /**
60 | * Issue stop logger
61 | */
62 | public function postExecuteRequest()
63 | {
64 | $this->logger->stopRequest();
65 | }
66 |
67 | /**
68 | * @return Client
69 | */
70 | public function getClient()
71 | {
72 | return $this->client;
73 | }
74 | }
--------------------------------------------------------------------------------
/Helper/DocumentHelper.php:
--------------------------------------------------------------------------------
1 | solariumClient = $solr->getClient();
28 | $this->metaInformationFactory = $solr->getMetaFactory();
29 | }
30 |
31 | /**
32 | * @param mixed $entity
33 | *
34 | * @return int
35 | */
36 | public function getLastInsertDocumentId($entity)
37 | {
38 | $metaInformation = $this->metaInformationFactory->loadInformation($entity);
39 |
40 | /** @var Query $select */
41 | $select = $this->solariumClient->createQuery(SolariumClient::QUERY_SELECT);
42 | $select->setQuery(sprintf('id:%s*', $metaInformation->getDocumentKey()));
43 | $select->setRows($this->getNumberOfDocuments($metaInformation->getDocumentName()));
44 | $select->addFields(array('id'));
45 |
46 | $result = $this->solariumClient->select($select);
47 |
48 | if ($result->count() == 0) {
49 | return 0;
50 | }
51 |
52 | $ids = array_map(function ($document) {
53 | return substr($document->id, stripos($document->id, '_') + 1);
54 | }, $result->getIterator()->getArrayCopy());
55 |
56 | return intval(max($ids));
57 | }
58 |
59 | /**
60 | * @param string $documentKey
61 | *
62 | * @return int
63 | */
64 | private function getNumberOfDocuments($documentKey)
65 | {
66 | $select = $this->solariumClient->createQuery(SolariumClient::QUERY_SELECT);
67 | $select->setQuery(sprintf('id:%s_*', $documentKey));
68 |
69 | $result = $this->solariumClient->select($select);
70 |
71 | return $result->getNumFound();
72 | }
73 | }
--------------------------------------------------------------------------------
/Tests/Integration/Entity/Tag.php:
--------------------------------------------------------------------------------
1 | name = $name;
52 | }
53 |
54 | /**
55 | * Get id
56 | *
57 | * @return integer
58 | */
59 | public function getId()
60 | {
61 | return $this->id;
62 | }
63 |
64 | /**
65 | * @param int $id
66 | */
67 | public function setId(int $id)
68 | {
69 | $this->id = $id;
70 | }
71 |
72 | /**
73 | * Set name
74 | *
75 | * @param string $name
76 | *
77 | * @return Tag
78 | */
79 | public function setName($name)
80 | {
81 | $this->name = $name;
82 |
83 | return $this;
84 | }
85 |
86 | /**
87 | * Get name
88 | *
89 | * @return string
90 | */
91 | public function getName()
92 | {
93 | return $this->name;
94 | }
95 |
96 | /**
97 | * @return Post
98 | */
99 | public function getPost()
100 | {
101 | return $this->post;
102 | }
103 |
104 | /**
105 | * @param Post $post
106 | */
107 | public function setPost($post)
108 | {
109 | $this->post = $post;
110 | }
111 |
112 |
113 | }
114 |
115 |
--------------------------------------------------------------------------------
/Doctrine/ClassnameResolver/KnownNamespaceAliases.php:
--------------------------------------------------------------------------------
1 | Full Entity/Documentnamespace
15 | */
16 | private $knownNamespaceAlias = array();
17 |
18 | /**
19 | * @var array
20 | */
21 | private $entityClassnames = array();
22 |
23 | /**
24 | * @param OdmConfiguration $configuration
25 | */
26 | public function addDocumentNamespaces(OdmConfiguration $configuration)
27 | {
28 | $this->knownNamespaceAlias = array_merge($this->knownNamespaceAlias, $configuration->getDocumentNamespaces());
29 |
30 | if ($configuration->getMetadataDriverImpl()) {
31 | $this->entityClassnames = array_merge($this->entityClassnames, $configuration->getMetadataDriverImpl()->getAllClassNames());
32 | }
33 | }
34 |
35 | /**
36 | * @param OrmConfiguration $configuration
37 | */
38 | public function addEntityNamespaces(OrmConfiguration $configuration)
39 | {
40 | $this->knownNamespaceAlias = array_merge($this->knownNamespaceAlias, $configuration->getEntityNamespaces());
41 |
42 | if ($configuration->getMetadataDriverImpl()) {
43 | $this->entityClassnames = array_merge($this->entityClassnames, $configuration->getMetadataDriverImpl()->getAllClassNames());
44 | }
45 | }
46 |
47 | /**
48 | * @param string $alias
49 | *
50 | * @return bool
51 | */
52 | public function isKnownNamespaceAlias($alias)
53 | {
54 | return isset($this->knownNamespaceAlias[$alias]);
55 | }
56 |
57 | /**
58 | * @param string $alias
59 | *
60 | * @return string
61 | */
62 | public function getFullyQualifiedNamespace($alias)
63 | {
64 | if ($this->isKnownNamespaceAlias($alias)) {
65 | return $this->knownNamespaceAlias[$alias];
66 | }
67 |
68 | return '';
69 | }
70 |
71 | /**
72 | * @return array
73 | */
74 | public function getAllNamespaceAliases()
75 | {
76 | return array_keys($this->knownNamespaceAlias);
77 | }
78 |
79 | /**
80 | * @return array
81 | */
82 | public function getEntityClassnames()
83 | {
84 | return $this->entityClassnames;
85 | }
86 | }
--------------------------------------------------------------------------------
/Doctrine/Hydration/DoctrineHydrator.php:
--------------------------------------------------------------------------------
1 | valueHydrator = $valueHydrator;
38 | }
39 |
40 | /**
41 | * @param ManagerRegistry $ormManager
42 | */
43 | public function setOrmManager($ormManager)
44 | {
45 | $this->ormManager = $ormManager;
46 | }
47 |
48 | /**
49 | * @param ManagerRegistry $odmManager
50 | */
51 | public function setOdmManager($odmManager)
52 | {
53 | $this->odmManager = $odmManager;
54 | }
55 |
56 | /**
57 | * {@inheritdoc}
58 | */
59 | public function hydrate($document, MetaInformationInterface $metaInformation)
60 | {
61 | $entityId = $this->valueHydrator->removePrefixedKeyValues($document['id']);
62 |
63 | $doctrineEntity = null;
64 | if ($metaInformation->getDoctrineMapperType() == MetaInformationInterface::DOCTRINE_MAPPER_TYPE_RELATIONAL) {
65 | $doctrineEntity = $this->ormManager
66 | ->getManager()
67 | ->getRepository($metaInformation->getClassName())
68 | ->find($entityId);
69 | } elseif ($metaInformation->getDoctrineMapperType() == MetaInformationInterface::DOCTRINE_MAPPER_TYPE_DOCUMENT) {
70 | $doctrineEntity = $this->odmManager
71 | ->getManager()
72 | ->getRepository($metaInformation->getClassName())
73 | ->find($entityId);
74 | }
75 |
76 | if ($doctrineEntity !== null) {
77 | $metaInformation->setEntity($doctrineEntity);
78 | }
79 |
80 | return $this->valueHydrator->hydrate($document, $metaInformation);
81 | }
82 | }
--------------------------------------------------------------------------------
/Tests/Integration/Entity/Category.php:
--------------------------------------------------------------------------------
1 | posts = new ArrayCollection();
59 | }
60 |
61 | /**
62 | * @return int
63 | */
64 | public function getId()
65 | {
66 | return $this->id;
67 | }
68 |
69 | /**
70 | * @param int $id
71 | */
72 | public function setId($id)
73 | {
74 | $this->id = $id;
75 | }
76 |
77 | /**
78 | * @return string
79 | */
80 | public function getTitle()
81 | {
82 | return $this->title;
83 | }
84 |
85 | /**
86 | * @param string $title
87 | */
88 | public function setTitle($title)
89 | {
90 | $this->title = $title;
91 | }
92 |
93 | /**
94 | * @return Post[]
95 | */
96 | public function getPosts()
97 | {
98 | return $this->posts;
99 | }
100 |
101 | /**
102 | * @param Post[] $posts
103 | */
104 | public function setPosts($posts)
105 | {
106 | $this->posts = $posts;
107 | }
108 |
109 | public function addPost($post)
110 | {
111 | $this->posts->add($post);
112 | }
113 |
114 | /**
115 | * @param string $info
116 | */
117 | public function setInfo($info)
118 | {
119 | $this->info = $info;
120 | }
121 |
122 | }
--------------------------------------------------------------------------------
/Doctrine/AbstractIndexingListener.php:
--------------------------------------------------------------------------------
1 | solr = $solr;
36 | $this->metaInformationFactory = $metaInformationFactory;
37 | $this->logger = $logger;
38 | }
39 |
40 | /**
41 | * @param array $doctrineChangeSet
42 | * @param object $entity
43 | *
44 | * @return bool
45 | */
46 | protected function hasChanged($doctrineChangeSet, $entity)
47 | {
48 | if (empty($doctrineChangeSet)) {
49 | return false;
50 | }
51 |
52 | $metaInformation = $this->metaInformationFactory->loadInformation($entity);
53 |
54 | $documentChangeSet = array();
55 |
56 | /* Check all Solr fields on this entity and check if this field is in the change set */
57 | foreach ($metaInformation->getFields() as $field) {
58 | if (array_key_exists($field->name, $doctrineChangeSet)) {
59 | $documentChangeSet[] = $field->name;
60 | }
61 | }
62 |
63 | return count($documentChangeSet) > 0;
64 | }
65 |
66 | /**
67 | * @param object $entity
68 | *
69 | * @return bool
70 | */
71 | protected function isNested($entity)
72 | {
73 | $metaInformation = $this->metaInformationFactory->loadInformation($entity);
74 |
75 | return $metaInformation->isNested();
76 | }
77 |
78 | /**
79 | * @param object $entity
80 | *
81 | * @return bool
82 | */
83 | protected function isAbleToIndex($entity)
84 | {
85 | try {
86 | $metaInformation = $this->metaInformationFactory->loadInformation($entity);
87 | } catch (SolrMappingException $e) {
88 | return false;
89 | }
90 |
91 | return true;
92 | }
93 | }
--------------------------------------------------------------------------------
/Tests/Doctrine/Annotation/FieldTest.php:
--------------------------------------------------------------------------------
1 | 'test', 'type' => 'string'));
15 | $this->assertEquals('test_s', $field->getNameWithAlias());
16 | }
17 |
18 | public function testGetNameWithAlias_Text()
19 | {
20 | $field = new Field(array('name' => 'test', 'type' => 'text'));
21 | $this->assertEquals('test_t', $field->getNameWithAlias());
22 | }
23 |
24 | public function testGetNameWithAlias_Date()
25 | {
26 | $field = new Field(array('name' => 'test', 'type' => 'date'));
27 | $this->assertEquals('test_dt', $field->getNameWithAlias());
28 | }
29 |
30 | public function testGetNameWithAlias_Boolean()
31 | {
32 | $field = new Field(array('name' => 'test', 'type' => 'boolean'));
33 | $this->assertEquals('test_b', $field->getNameWithAlias());
34 | }
35 |
36 | public function testGetNameWithAlias_NoFieldType()
37 | {
38 | $field = new Field(array('name' => 'title'));
39 | $this->assertEquals('title', $field->getNameWithAlias());
40 | }
41 |
42 | public function testGetNameWithAlias_Integer()
43 | {
44 | $field = new Field(array('name' => 'test', 'type' => 'integer'));
45 | $this->assertEquals('test_i', $field->getNameWithAlias());
46 | }
47 |
48 | public function testNormalizeName_CamelCase()
49 | {
50 | $field = new Field(array('name' => 'testCamelCase', 'type' => 'string'));
51 |
52 | $meta = new \ReflectionClass($field);
53 | $method = $meta->getMethod('normalizeName');
54 | $method->setAccessible(true);
55 | $result = $method->invoke($field, $field->name);
56 |
57 | $this->assertEquals('test_camel_case', $result);
58 | }
59 |
60 | public function testNormalizeName_Underscore()
61 | {
62 | $field = new Field(array('name' => 'test_underscore', 'type' => 'string'));
63 |
64 | $meta = new \ReflectionClass($field);
65 | $method = $meta->getMethod('normalizeName');
66 | $method->setAccessible(true);
67 | $result = $method->invoke($field, $field->name);
68 |
69 | $this->assertEquals('test_underscore', $result);
70 | }
71 |
72 | public function testCostomFieldType()
73 | {
74 | $field = new Field(array('name' => 'costumtype', 'type' => 'my_special_type'));
75 |
76 | $this->assertEquals('costumtype', $field->getNameWithAlias());
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/Doctrine/ClassnameResolver/ClassnameResolver.php:
--------------------------------------------------------------------------------
1 | knownNamespaceAliases = $knownNamespaceAliases;
22 | }
23 |
24 | /**
25 | * @param string $entityAlias
26 | *
27 | * @return string
28 | *
29 | * @throws ClassnameResolverException if the entityAlias could not find in any configured namespace or the class
30 | * does not exist
31 | */
32 | public function resolveFullQualifiedClassname($entityAlias)
33 | {
34 | $entityNamespaceAlias = $this->getNamespaceAlias($entityAlias);
35 |
36 | if ($this->knownNamespaceAliases->isKnownNamespaceAlias($entityNamespaceAlias) === false) {
37 | $e = ClassnameResolverException::fromKnownNamespaces(
38 | $entityNamespaceAlias,
39 | $this->knownNamespaceAliases->getAllNamespaceAliases()
40 | );
41 |
42 | throw $e;
43 | }
44 |
45 | $foundNamespace = $this->knownNamespaceAliases->getFullyQualifiedNamespace($entityNamespaceAlias);
46 |
47 | $realClassName = $this->getFullyQualifiedClassname($foundNamespace, $entityAlias);
48 | if (class_exists($realClassName) === false) {
49 | throw new ClassnameResolverException(sprintf('class %s does not exist', $realClassName));
50 | }
51 |
52 | return $realClassName;
53 | }
54 |
55 | /**
56 | * @param string $entity
57 | *
58 | * @return string
59 | */
60 | public function getNamespaceAlias($entity)
61 | {
62 | list($namespaceAlias, $simpleClassName) = explode(':', $entity);
63 |
64 | return $namespaceAlias;
65 | }
66 |
67 | /**
68 | * @param string $entity
69 | *
70 | * @return string
71 | */
72 | public function getClassname($entity)
73 | {
74 | list($namespaceAlias, $simpleClassName) = explode(':', $entity);
75 |
76 | return $simpleClassName;
77 | }
78 |
79 | /**
80 | * @param string $namespace
81 | * @param string $entityAlias
82 | *
83 | * @return string
84 | */
85 | private function getFullyQualifiedClassname($namespace, $entityAlias)
86 | {
87 | $realClassName = $namespace . '\\' . $this->getClassname($entityAlias);
88 |
89 | return $realClassName;
90 | }
91 |
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/Client/Solarium/SolariumMulticoreClient.php:
--------------------------------------------------------------------------------
1 | solariumClient = $solariumClient;
28 | }
29 |
30 | /**
31 | * @param DocumentInterface $doc
32 | * @param string $index
33 | */
34 | public function update(DocumentInterface $doc, $index)
35 | {
36 | $update = $this->solariumClient->createUpdate();
37 | $update->addDocument($doc);
38 | $update->addCommit();
39 |
40 | $this->applyQuery($update, $index);
41 | }
42 |
43 | /**
44 | * @param DocumentInterface $document
45 | * @param string $index
46 | */
47 | public function delete(DocumentInterface $document, $index)
48 | {
49 | $documentFields = $document->getFields();
50 | $documentKey = $documentFields[MetaInformationInterface::DOCUMENT_KEY_FIELD_NAME];
51 |
52 | $deleteQuery = new DeleteDocumentQuery();
53 | $deleteQuery->setDocument($document);
54 | $deleteQuery->setDocumentKey($documentKey);
55 |
56 | $delete = $this->solariumClient->createUpdate();
57 | $delete->addDeleteQuery($deleteQuery->getQuery());
58 | $delete->addCommit();
59 |
60 | $this->applyQuery($delete, $index);
61 | }
62 |
63 | /**
64 | * Runs a *:* delete query on all cores
65 | */
66 | public function clearCores()
67 | {
68 | $delete = $this->solariumClient->createUpdate();
69 | $delete->addDeleteQuery('*:*');
70 | $delete->addCommit();
71 |
72 | $this->applyOnAllCores($delete);
73 | }
74 |
75 | /**
76 | * @param QueryInterface $query
77 | * @param string $index
78 | */
79 | private function applyQuery(QueryInterface $query, $index)
80 | {
81 | if ($index == '*') {
82 | $this->applyOnAllCores($query);
83 | } else {
84 | $this->solariumClient->update($query, $index);
85 | }
86 | }
87 |
88 | /**
89 | * @param QueryInterface $query
90 | */
91 | private function applyOnAllCores(QueryInterface $query)
92 | {
93 | foreach ($this->solariumClient->getEndpoints() as $endpointName => $endpoint) {
94 | $this->solariumClient->update($query, $endpointName);
95 | }
96 | }
97 | }
--------------------------------------------------------------------------------
/Resources/config/log_listener.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 | FS\SolrBundle\Event\Listener\InsertLogListener
9 | FS\SolrBundle\Event\Listener\UpdateLogListener
10 | FS\SolrBundle\Event\Listener\DeleteLogListener
11 | FS\SolrBundle\Event\Listener\ErrorLogListener
12 | FS\SolrBundle\Event\Listener\ClearIndexLogListener
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Client/Solarium/SolariumClientBuilder.php:
--------------------------------------------------------------------------------
1 | settings = $settings;
37 | $this->eventDispatcher = $eventDispatcher;
38 | }
39 |
40 | /**
41 | * @param string $pluginName
42 | * @param AbstractPlugin $plugin
43 | */
44 | public function addPlugin($pluginName, AbstractPlugin $plugin)
45 | {
46 | $this->plugins[$pluginName] = $plugin;
47 | }
48 |
49 | /**
50 | * {@inheritdoc}
51 | *
52 | * @return Client
53 | */
54 | public function build()
55 | {
56 | $settings = [];
57 | foreach ($this->settings as $name => $options) {
58 | if (isset($options['dsn'])) {
59 | unset(
60 | $options['scheme'],
61 | $options['host'],
62 | $options['port'],
63 | $options['path']
64 | );
65 |
66 | $parsedDsn = parse_url($options['dsn']);
67 | unset($options['dsn']);
68 | if ($parsedDsn) {
69 | $options['scheme'] = isset($parsedDsn['scheme']) ? $parsedDsn['scheme'] : 'http';
70 | if (isset($parsedDsn['host'])) {
71 | $options['host'] = $parsedDsn['host'];
72 | }
73 | if (isset($parsedDsn['user'])) {
74 | $auth = $parsedDsn['user'] . (isset($parsedDsn['pass']) ? ':' . $parsedDsn['pass'] : '');
75 | $options['host'] = $auth . '@' . $options['host'];
76 | }
77 | $options['port'] = isset($parsedDsn['port']) ? $parsedDsn['port'] : 80;
78 | $options['path'] = isset($parsedDsn['path']) ? $parsedDsn['path'] : '';
79 | }
80 | }
81 |
82 | $settings[$name] = $options;
83 | }
84 |
85 | $solariumClient = new Client(array('endpoint' => $settings), $this->eventDispatcher);
86 | foreach ($this->plugins as $pluginName => $plugin) {
87 | $solariumClient->registerPlugin($pluginName, $plugin);
88 | }
89 |
90 | return $solariumClient;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/DataCollector/RequestCollector.php:
--------------------------------------------------------------------------------
1 | logger = $logger;
25 | }
26 |
27 | /**
28 | * {@inheritdoc}
29 | */
30 | public function collect(Request $request, Response $response, \Exception $exception = null)
31 | {
32 | $this->data = [
33 | 'queries' => array_map(function ($query) {
34 | return $this->parseQuery($query);
35 | }, $this->logger->getQueries())
36 | ];
37 | }
38 |
39 | /**
40 | * @return int
41 | */
42 | public function getQueryCount()
43 | {
44 | return count($this->data['queries']);
45 | }
46 |
47 | /**
48 | * @return array
49 | */
50 | public function getQueries()
51 | {
52 | return $this->data['queries'];
53 | }
54 |
55 | /**
56 | * @return int
57 | */
58 | public function getTime()
59 | {
60 | $time = 0;
61 | foreach ($this->data['queries'] as $query) {
62 | $time += $query['executionMS'];
63 | }
64 |
65 | return $time;
66 | }
67 |
68 | /**
69 | * @param array $request
70 | *
71 | * @return array
72 | */
73 | public function parseQuery($request)
74 | {
75 | list($endpoint, $params) = explode('?', $request['request']['uri']);
76 |
77 | $request['endpoint'] = $endpoint;
78 | $request['params'] = $params;
79 | $request['method'] = $request['request']['method'];
80 | $request['raw_data'] = $request['request']['raw_data'];
81 |
82 | if (class_exists(VarCloner::class)) {
83 | $varCloner = new VarCloner();
84 |
85 | parse_str($params, $stub);
86 | $request['stub'] = Kernel::VERSION_ID >= 30200 ? $varCloner->cloneVar($stub) : $stub;
87 | }
88 |
89 | return $request;
90 | }
91 |
92 | /**
93 | * @param DebugLogger $logger
94 | */
95 | public function setLogger(DebugLogger $logger)
96 | {
97 | $this->logger = $logger;
98 | }
99 |
100 | /**
101 | * {@inheritdoc}
102 | */
103 | public function getName()
104 | {
105 | return 'solr';
106 | }
107 |
108 | /**
109 | * {@inheritdoc}
110 | */
111 | public function reset()
112 | {
113 | $this->data = [
114 | 'queries' => [],
115 | ];
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/Query/AbstractQuery.php:
--------------------------------------------------------------------------------
1 | metaInformation;
46 | }
47 |
48 | /**
49 | * @param MetaInformationInterface $metaInformation
50 | */
51 | public function setMetaInformation($metaInformation)
52 | {
53 | $this->metaInformation = $metaInformation;
54 |
55 | $this->entity = $metaInformation->getClassName();
56 | $this->index = $metaInformation->getIndex();
57 | }
58 |
59 | /**
60 | * @return string
61 | */
62 | public function getEntity()
63 | {
64 | return $this->entity;
65 | }
66 |
67 | /**
68 | * @param string $entity
69 | */
70 | public function setEntity($entity)
71 | {
72 | $this->entity = $entity;
73 | }
74 |
75 | /**
76 | * @param Document $document
77 | */
78 | public function setDocument($document)
79 | {
80 | $this->document = $document;
81 | }
82 |
83 | /**
84 | * @return Document
85 | */
86 | public function getDocument()
87 | {
88 | return $this->document;
89 | }
90 |
91 | /**
92 | * @param SolrInterface $solr
93 | */
94 | public function setSolr(SolrInterface $solr)
95 | {
96 | $this->solr = $solr;
97 | }
98 |
99 | /**
100 | * @return SolrInterface
101 | */
102 | public function getSolr()
103 | {
104 | return $this->solr;
105 | }
106 |
107 | /**
108 | * modes defined in FS\SolrBundle\Doctrine\Hydration\HydrationModes
109 | *
110 | * @param string $mode
111 | */
112 | public function setHydrationMode($mode)
113 | {
114 | $this->getSolr()->getMapper()->setHydrationMode($mode);
115 | }
116 |
117 | /**
118 | * @return string
119 | */
120 | public function getIndex()
121 | {
122 | return $this->index;
123 | }
124 |
125 | /**
126 | * @param string $index
127 | */
128 | public function setIndex($index)
129 | {
130 | $this->index = $index;
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/Tests/SolrClientFake.php:
--------------------------------------------------------------------------------
1 | mapper;
28 | }
29 |
30 | public function getCommandFactory()
31 | {
32 | return $this->commandFactory;
33 | }
34 |
35 | public function getMetaFactory()
36 | {
37 | return $this->metaFactory;
38 | }
39 |
40 | public function addDocument($doc): bool
41 | {
42 | return true;
43 | }
44 |
45 | public function deleteByQuery($query)
46 | {
47 | }
48 |
49 | public function commit()
50 | {
51 | $this->commit = true;
52 | }
53 |
54 | public function isCommited()
55 | {
56 | return $this->commit;
57 | }
58 |
59 | public function query(AbstractQuery $query): array
60 | {
61 | $this->query = $query;
62 |
63 | return $this->response;
64 | }
65 |
66 | public function setResponse(SolrResponseFake $response)
67 | {
68 | $this->response = $response;
69 | }
70 |
71 | public function getOptions()
72 | {
73 | return array();
74 | }
75 |
76 | public function createQuery($entity)
77 | {
78 | $metaInformation = $this->metaFactory->loadInformation($entity);
79 | $class = $metaInformation->getClassName();
80 | $entity = new $class;
81 |
82 | $query = new SolrQuery();
83 | $query->setSolr($this);
84 | $query->setEntity($entity);
85 | $query->setIndex($metaInformation->getIndex());
86 | $query->setMetaInformation($metaInformation);
87 | $query->setMappedFields($metaInformation->getFieldMapping());
88 |
89 | return $query;
90 | }
91 |
92 | public function removeDocument($entity)
93 | {
94 | // TODO: Implement removeDocument() method.
95 | }
96 |
97 | public function updateDocument($entity): bool
98 | {
99 | // TODO: Implement updateDocument() method.
100 | }
101 |
102 | public function getRepository($entity): RepositoryInterface
103 | {
104 | // TODO: Implement getRepository() method.
105 | }
106 |
107 | public function computeChangeSet(array $doctrineChangeSet, $entity)
108 | {
109 | // TODO: Implement computeChangeSet() method.
110 | }
111 |
112 | public function createQueryBuilder($entity): QueryBuilderInterface
113 | {
114 | // TODO: Implement createQueryBuilder() method.
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Doctrine/Mapper/EntityMapper.php:
--------------------------------------------------------------------------------
1 | doctrineHydrator = $doctrineHydrator;
46 | $this->indexHydrator = $indexHydrator;
47 | $this->metaInformationFactory = $metaInformationFactory;
48 | $this->documentFactory = new DocumentFactory($metaInformationFactory);
49 |
50 | $this->hydrationMode = HydrationModes::HYDRATE_DOCTRINE;
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | */
56 | public function toDocument(MetaInformationInterface $metaInformation)
57 | {
58 | return $this->documentFactory->createDocument($metaInformation);
59 | }
60 |
61 | /**
62 | * {@inheritdoc}
63 | */
64 | public function toEntity(\ArrayAccess $document, $sourceTargetEntity)
65 | {
66 | if (null === $sourceTargetEntity) {
67 | throw new SolrMappingException('$sourceTargetEntity should not be null');
68 | }
69 |
70 | $metaInformation = $this->metaInformationFactory->loadInformation($sourceTargetEntity);
71 |
72 | if ($metaInformation->isDoctrineEntity() === false && $this->hydrationMode == HydrationModes::HYDRATE_DOCTRINE) {
73 | throw new SolrMappingException(sprintf('Please check your config. Given entity is not a Doctrine entity, but Doctrine hydration is enabled. Use setHydrationMode(HydrationModes::HYDRATE_DOCTRINE) to fix this.'));
74 | }
75 |
76 | if ($this->hydrationMode == HydrationModes::HYDRATE_INDEX) {
77 | return $this->indexHydrator->hydrate($document, $metaInformation);
78 | }
79 |
80 | if ($this->hydrationMode == HydrationModes::HYDRATE_DOCTRINE) {
81 | return $this->doctrineHydrator->hydrate($document, $metaInformation);
82 | }
83 | }
84 |
85 | /**
86 | * @param string $mode
87 | */
88 | public function setHydrationMode($mode)
89 | {
90 | $this->hydrationMode = $mode;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Tests/Doctrine/ClassnameResolver/KnownNamespaceAliasesTest.php:
--------------------------------------------------------------------------------
1 | createMock(OrmConfiguration::class);
19 | $config1->expects($this->once())
20 | ->method('getEntityNamespaces')
21 | ->will($this->returnValue(array('AcmeDemoBundle')));
22 |
23 | $config2 = $this->createMock(OrmConfiguration::class);
24 | $config2->expects($this->once())
25 | ->method('getEntityNamespaces')
26 | ->will($this->returnValue(array('AcmeBlogBundle')));
27 |
28 | $knownAliases = new KnownNamespaceAliases();
29 | $knownAliases->addEntityNamespaces($config1);
30 | $knownAliases->addEntityNamespaces($config2);
31 |
32 | $this->assertTrue(in_array('AcmeDemoBundle', $knownAliases->getAllNamespaceAliases()));
33 | $this->assertTrue(in_array('AcmeBlogBundle', $knownAliases->getAllNamespaceAliases()));
34 | }
35 |
36 | /**
37 | * @test
38 | */
39 | public function addAliasFromMultipleOdmConfigurations()
40 | {
41 | $config1 = $this->createMock(OdmConfiguration::class);
42 | $config1->expects($this->once())
43 | ->method('getDocumentNamespaces')
44 | ->will($this->returnValue(array('AcmeDemoBundle')));
45 |
46 | $config2 = $this->createMock(OdmConfiguration::class);
47 | $config2->expects($this->once())
48 | ->method('getDocumentNamespaces')
49 | ->will($this->returnValue(array('AcmeBlogBundle')));
50 |
51 | $knownAliases = new KnownNamespaceAliases();
52 | $knownAliases->addDocumentNamespaces($config1);
53 | $knownAliases->addDocumentNamespaces($config2);
54 |
55 | $this->assertTrue(in_array('AcmeDemoBundle', $knownAliases->getAllNamespaceAliases()));
56 | $this->assertTrue(in_array('AcmeBlogBundle', $knownAliases->getAllNamespaceAliases()));
57 | }
58 |
59 | /**
60 | * @test
61 | */
62 | public function knowAliasHasAValidNamespace()
63 | {
64 | $config1 = $this->createMock(OdmConfiguration::class);
65 | $config1->expects($this->once())
66 | ->method('getDocumentNamespaces')
67 | ->will($this->returnValue(array('AcmeDemoBundle' => 'Acme\DemoBundle\Document')));
68 |
69 | $config2 = $this->createMock(OdmConfiguration::class);
70 | $config2->expects($this->once())
71 | ->method('getDocumentNamespaces')
72 | ->will($this->returnValue(array('AcmeBlogBundle' => 'Acme\BlogBundle\Document')));
73 |
74 | $knownAliases = new KnownNamespaceAliases();
75 | $knownAliases->addDocumentNamespaces($config1);
76 | $knownAliases->addDocumentNamespaces($config2);
77 |
78 | $this->assertEquals('Acme\DemoBundle\Document', $knownAliases->getFullyQualifiedNamespace('AcmeDemoBundle'));
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Tests/Fixtures/ValidTestEntityWithRelation.php:
--------------------------------------------------------------------------------
1 | id;
63 | }
64 |
65 | public function setId($id)
66 | {
67 | $this->id = $id;
68 | }
69 |
70 | /**
71 | * @param string $costomField
72 | */
73 | public function setCostomField($costomField)
74 | {
75 | $this->costomField = $costomField;
76 | }
77 |
78 | /**
79 | * @return string
80 | */
81 | public function getCostomField()
82 | {
83 | return $this->costomField;
84 | }
85 |
86 | /**
87 | * @return object
88 | */
89 | public function getRelation()
90 | {
91 | return $this->relation;
92 | }
93 |
94 | /**
95 | * @param object $relation
96 | */
97 | public function setRelation($relation)
98 | {
99 | $this->relation = $relation;
100 | }
101 |
102 | /**
103 | * @return object
104 | */
105 | public function getPosts()
106 | {
107 | return $this->posts;
108 | }
109 |
110 | /**
111 | * @param object $posts
112 | */
113 | public function setPosts($posts)
114 | {
115 | $this->posts = $posts;
116 | }
117 |
118 | /**
119 | * @return string
120 | */
121 | public function getText()
122 | {
123 | return $this->text;
124 | }
125 |
126 | /**
127 | * @param string $text
128 | */
129 | public function setText($text)
130 | {
131 | $this->text = $text;
132 | }
133 |
134 | /**
135 | * @return string
136 | */
137 | public function getTitle()
138 | {
139 | return $this->title;
140 | }
141 |
142 | /**
143 | * @param string $title
144 | */
145 | public function setTitle($title)
146 | {
147 | $this->title = $title;
148 | }
149 |
150 | /**
151 | * @return \DateTime
152 | */
153 | public function getCreatedAt()
154 | {
155 | return $this->created_at;
156 | }
157 |
158 | /**
159 | * @param \DateTime $created_at
160 | */
161 | public function setCreatedAt($created_at)
162 | {
163 | $this->created_at = $created_at;
164 | }
165 | }
166 |
167 |
--------------------------------------------------------------------------------
/Tests/Fixtures/EntityNestedProperty.php:
--------------------------------------------------------------------------------
1 | id;
70 | }
71 |
72 | /**
73 | * @param mixed $id
74 | */
75 | public function setId($id)
76 | {
77 | $this->id = $id;
78 | }
79 |
80 | public function sliceCollection()
81 | {
82 | return [$this->collectionValidGetter[0]];
83 | }
84 |
85 | /**
86 | * @param string $name
87 | */
88 | public function setName($name)
89 | {
90 | $this->name = $name;
91 | }
92 |
93 | /**
94 |
95 | /**
96 | * @param array $collection
97 | */
98 | public function setCollection($collection)
99 | {
100 | $this->collection = $collection;
101 | }
102 |
103 | /**
104 | * @param object $nestedProperty
105 | */
106 | public function setNestedProperty($nestedProperty)
107 | {
108 | $this->nestedProperty = $nestedProperty;
109 | }
110 |
111 | /**
112 | * @param array $collectionValidGetter
113 | */
114 | public function setCollectionValidGetter($collectionValidGetter)
115 | {
116 | $this->collectionValidGetter = $collectionValidGetter;
117 | }
118 |
119 | /**
120 | * @param array $collectionInvalidGetter
121 | */
122 | public function setCollectionInvalidGetter($collectionInvalidGetter)
123 | {
124 | $this->collectionInvalidGetter = $collectionInvalidGetter;
125 | }
126 |
127 | /**
128 | * @param mixed $objectToSimpleFormat
129 | */
130 | public function setGetterWithParameters($getterWithParameters)
131 | {
132 | $this->getterWithParameters = $getterWithParameters;
133 | }
134 |
135 | /**
136 | * @param mixed $simpleGetter
137 | */
138 | public function setSimpleGetter($simpleGetter)
139 | {
140 | $this->simpleGetter = $simpleGetter;
141 | }
142 | }
--------------------------------------------------------------------------------
/Tests/Doctrine/ClassnameResolver/ClassnameResolverTest.php:
--------------------------------------------------------------------------------
1 | knownAliases = $this->createMock(KnownNamespaceAliases::class);
22 | }
23 |
24 | /**
25 | * @test
26 | */
27 | public function resolveClassnameOfCommonEntity()
28 | {
29 | $resolver = $this->getResolverWithKnowNamespace(self::ENTITY_NAMESPACE);
30 |
31 | $this->assertEquals(ValidTestEntity::class, $resolver->resolveFullQualifiedClassname('FSTest:ValidTestEntity'));
32 | }
33 |
34 | /**
35 | * @test
36 | * @expectedException \FS\SolrBundle\Doctrine\ClassnameResolver\ClassnameResolverException
37 | */
38 | public function cantResolveClassnameFromUnknowClassWithValidNamespace()
39 | {
40 | $resolver = $this->getResolverWithOrmAndOdmConfigBothHasEntity(self::ENTITY_NAMESPACE);
41 |
42 | $resolver->resolveFullQualifiedClassname('FSTest:UnknownEntity');
43 | }
44 |
45 | /**
46 | * @test
47 | * @expectedException \FS\SolrBundle\Doctrine\ClassnameResolver\ClassnameResolverException
48 | */
49 | public function cantResolveClassnameIfEntityNamespaceIsUnknown()
50 | {
51 | $resolver = $this->getResolverWithOrmConfigPassedInvalidNamespace(self::UNKNOW_ENTITY_NAMESPACE);
52 |
53 | $resolver->resolveFullQualifiedClassname('FStest:entity');
54 | }
55 |
56 | /**
57 | * both has a namespace
58 | *
59 | * @param string $knownNamespace
60 | * @return ClassnameResolver
61 | */
62 | private function getResolverWithOrmAndOdmConfigBothHasEntity($knownNamespace)
63 | {
64 | $this->knownAliases->expects($this->once())
65 | ->method('isKnownNamespaceAlias')
66 | ->will($this->returnValue(true));
67 |
68 | $this->knownAliases->expects($this->once())
69 | ->method('getFullyQualifiedNamespace')
70 | ->will($this->returnValue($knownNamespace));
71 |
72 | $resolver = new ClassnameResolver($this->knownAliases);
73 |
74 | return $resolver;
75 | }
76 |
77 | private function getResolverWithOrmConfigPassedInvalidNamespace($knownNamespace)
78 | {
79 | $this->knownAliases->expects($this->once())
80 | ->method('isKnownNamespaceAlias')
81 | ->will($this->returnValue(false));
82 |
83 | $this->knownAliases->expects($this->once())
84 | ->method('getAllNamespaceAliases')
85 | ->will($this->returnValue(array('FSTest')));
86 |
87 | $resolver = new ClassnameResolver($this->knownAliases);
88 |
89 | return $resolver;
90 | }
91 |
92 | private function getResolverWithKnowNamespace($knownNamespace)
93 | {
94 | $this->knownAliases->expects($this->once())
95 | ->method('isKnownNamespaceAlias')
96 | ->will($this->returnValue(true));
97 |
98 | $this->knownAliases->expects($this->once())
99 | ->method('getFullyQualifiedNamespace')
100 | ->will($this->returnValue($knownNamespace));
101 |
102 | $resolver = new ClassnameResolver($this->knownAliases);
103 |
104 | return $resolver;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/Tests/Fixtures/ValidTestEntityWithCollection.php:
--------------------------------------------------------------------------------
1 | id;
67 | }
68 |
69 | public function setId($id)
70 | {
71 | $this->id = $id;
72 | }
73 |
74 | /**
75 | * @param string $costomField
76 | */
77 | public function setCostomField($costomField)
78 | {
79 | $this->costomField = $costomField;
80 | }
81 |
82 | /**
83 | * @return string
84 | */
85 | public function getCostomField()
86 | {
87 | return $this->costomField;
88 | }
89 |
90 | /**
91 | * @return ArrayCollection
92 | */
93 | public function getCollection()
94 | {
95 | return $this->collection;
96 | }
97 |
98 | /**
99 | * @param ArrayCollection $collection
100 | */
101 | public function setCollection($collection)
102 | {
103 | $this->collection = $collection;
104 | }
105 |
106 | /**
107 | * @return string
108 | */
109 | public function getText()
110 | {
111 | return $this->text;
112 | }
113 |
114 | /**
115 | * @param string $text
116 | */
117 | public function setText($text)
118 | {
119 | $this->text = $text;
120 | }
121 |
122 | /**
123 | * @return string
124 | */
125 | public function getTitle()
126 | {
127 | return $this->title;
128 | }
129 |
130 | /**
131 | * @param string $title
132 | */
133 | public function setTitle($title)
134 | {
135 | $this->title = $title;
136 | }
137 |
138 | /**
139 | * @return \DateTime
140 | */
141 | public function getCreatedAt()
142 | {
143 | return $this->created_at;
144 | }
145 |
146 | /**
147 | * @param \DateTime $created_at
148 | */
149 | public function setCreatedAt($created_at)
150 | {
151 | $this->created_at = $created_at;
152 | }
153 |
154 | /**
155 | * @return ArrayCollection
156 | */
157 | public function getCollectionNoGetter()
158 | {
159 | return $this->collectionNoGetter;
160 | }
161 |
162 | /**
163 | * @param ArrayCollection $collectionNoGetter
164 | */
165 | public function setCollectionNoGetter(ArrayCollection $collectionNoGetter)
166 | {
167 | $this->collectionNoGetter = $collectionNoGetter;
168 | }
169 | }
170 |
171 |
--------------------------------------------------------------------------------
/Doctrine/Mapper/MetaInformationInterface.php:
--------------------------------------------------------------------------------
1 | exampleentity
57 | *
58 | * @return string
59 | */
60 | public function getDocumentName();
61 |
62 | /**
63 | * @return Field[]
64 | */
65 | public function getFields();
66 |
67 | /**
68 | * Returns full qualified classname of repository-class
69 | *
70 | * @return string
71 | */
72 | public function getRepository();
73 |
74 | /**
75 | * Source/target entity instance
76 | *
77 | * @return object
78 | */
79 | public function getEntity();
80 |
81 | /**
82 | * @param string $fieldName
83 | *
84 | * @return Field|null
85 | *
86 | * @throws \InvalidArgumentException if given $fieldName is unknown
87 | */
88 | public function getField($fieldName);
89 |
90 | /**
91 | * @return array
92 | */
93 | public function getFieldMapping();
94 |
95 | /**
96 | * The document boost value
97 | *
98 | * @return number
99 | */
100 | public function getBoost();
101 |
102 | /**
103 | * @return string
104 | */
105 | public function getSynchronizationCallback();
106 |
107 | /**
108 | * @return boolean
109 | */
110 | public function hasSynchronizationFilter();
111 |
112 | /**
113 | * Returns the configured index argument in FS\SolrBundle\Doctrine\Annotation\Document or the returns value of the index-handler callback
114 | *
115 | * @return string
116 | */
117 | public function getIndex();
118 |
119 | /**
120 | * Returns combination of DOCUMENT_KEY_FIELD_NAME and entity-id
121 | *
122 | * @return string
123 | */
124 | public function getDocumentKey();
125 |
126 | /**
127 | * The property which has the FS\SolrBundle\Doctrine\Annotation\Id annotation
128 | *
129 | * @return string
130 | */
131 | public function getIdentifierFieldName();
132 |
133 | /**
134 | * Returns MetaInformationInterface::DOCTRINE_MAPPER_TYPE_DOCUMENT if target is an doctrine-odm object or
135 | * MetaInformationInterface::DOCTRINE_MAPPER_TYPE_RELATIONAL if it is an doctrine-orm object, otherwise an empty string
136 | *
137 | * @return string
138 | */
139 | public function getDoctrineMapperType();
140 |
141 | /**
142 | * @return bool
143 | */
144 | public function isNested();
145 | }
--------------------------------------------------------------------------------
/Doctrine/ORM/Listener/EntityIndexerSubscriber.php:
--------------------------------------------------------------------------------
1 | getEntity();
44 |
45 | if ($this->isAbleToIndex($entity) === false) {
46 | return;
47 | }
48 |
49 | $doctrineChangeSet = $args->getEntityManager()->getUnitOfWork()->getEntityChangeSet($entity);
50 | try {
51 | if ($this->hasChanged($doctrineChangeSet, $entity) === false) {
52 | return;
53 | }
54 |
55 | $this->solr->updateDocument($entity);
56 | } catch (\Exception $e) {
57 | $this->logger->debug($e->getMessage());
58 | }
59 | }
60 |
61 | /**
62 | * @param LifecycleEventArgs $args
63 | */
64 | public function postPersist(LifecycleEventArgs $args)
65 | {
66 | $entity = $args->getEntity();
67 |
68 | if ($this->isAbleToIndex($entity) === false) {
69 | return;
70 | }
71 |
72 | $this->persistedEntities[] = $entity;
73 | }
74 |
75 | /**
76 | * @param LifecycleEventArgs $args
77 | */
78 | public function preRemove(LifecycleEventArgs $args)
79 | {
80 | $entity = $args->getEntity();
81 |
82 | if ($this->isAbleToIndex($entity) === false) {
83 | return;
84 | }
85 |
86 | if ($this->isNested($entity)) {
87 | $this->deletedNestedEntities[] = $this->emptyCollections($entity);
88 | } else {
89 | $this->deletedRootEntities[] = $this->emptyCollections($entity);
90 | }
91 | }
92 |
93 | /**
94 | * @param object $object
95 | *
96 | * @return object
97 | */
98 | private function emptyCollections($object)
99 | {
100 | $deepcopy = new DeepCopy();
101 | $deepcopy->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
102 |
103 | return $deepcopy->copy($object);
104 | }
105 |
106 | /**
107 | * @param PostFlushEventArgs $eventArgs
108 | */
109 | public function postFlush(PostFlushEventArgs $eventArgs)
110 | {
111 | foreach ($this->persistedEntities as $entity) {
112 | $this->solr->addDocument($entity);
113 | }
114 | $this->persistedEntities = [];
115 |
116 | foreach ($this->deletedRootEntities as $entity) {
117 | $this->solr->removeDocument($entity);
118 | }
119 | $this->deletedRootEntities = [];
120 |
121 | foreach ($this->deletedNestedEntities as $entity) {
122 | $this->solr->removeDocument($entity);
123 | }
124 | $this->deletedNestedEntities = [];
125 | }
126 | }
--------------------------------------------------------------------------------
/Tests/Fixtures/ValidTestEntityAllCores.php:
--------------------------------------------------------------------------------
1 | id;
70 | }
71 |
72 | /**
73 | * @param int $id
74 | */
75 | public function setId($id)
76 | {
77 | $this->id = $id;
78 | }
79 |
80 | /**
81 | * @return string $text
82 | */
83 | public function getText()
84 | {
85 | return $this->text;
86 | }
87 |
88 | /**
89 | * @return string $title
90 | */
91 | public function getTitle()
92 | {
93 | return $this->title;
94 | }
95 |
96 | /**
97 | * @param string $text
98 | */
99 | public function setText($text)
100 | {
101 | $this->text = $text;
102 | }
103 |
104 | /**
105 | * @param string $title
106 | */
107 | public function setTitle($title)
108 | {
109 | $this->title = $title;
110 | }
111 |
112 | /**
113 | * @param string $costomField
114 | */
115 | public function setCostomField($costomField)
116 | {
117 | $this->costomField = $costomField;
118 | }
119 |
120 | /**
121 | * @return string
122 | */
123 | public function getCostomField()
124 | {
125 | return $this->costomField;
126 | }
127 |
128 | /**
129 | * @return \DateTime
130 | */
131 | public function getCreatedAt()
132 | {
133 | return $this->created_at;
134 | }
135 |
136 | /**
137 | * @param \DateTime $created_at
138 | */
139 | public function setCreatedAt($created_at)
140 | {
141 | $this->created_at = $created_at;
142 | }
143 |
144 | /**
145 | * @return ValidTestEntity[]
146 | */
147 | public function getPosts()
148 | {
149 | return $this->posts;
150 | }
151 |
152 | /**
153 | * @param ValidTestEntity[] $posts
154 | */
155 | public function setPosts($posts)
156 | {
157 | $this->posts = $posts;
158 | }
159 |
160 | /**
161 | * @param string $field
162 | */
163 | public function setField($field)
164 | {
165 | $this->privateField = $field;
166 | }
167 |
168 | /**
169 | * @return string
170 | */
171 | public function getField()
172 | {
173 | return $this->privateField;
174 | }
175 |
176 | /**
177 | * @return string
178 | */
179 | public function getPublishDate()
180 | {
181 | return $this->publishDate;
182 | }
183 |
184 | /**
185 | * @param string $publishDate
186 | */
187 | public function setPublishDate($publishDate)
188 | {
189 | $this->publishDate = $publishDate;
190 | }
191 | }
192 |
193 |
--------------------------------------------------------------------------------
/Tests/Fixtures/ValidOdmTestDocument.php:
--------------------------------------------------------------------------------
1 | id;
71 | }
72 |
73 | /**
74 | * @param int $id
75 | */
76 | public function setId($id)
77 | {
78 | $this->id = $id;
79 | }
80 |
81 | /**
82 | * @return string $text
83 | */
84 | public function getText()
85 | {
86 | return $this->text;
87 | }
88 |
89 | /**
90 | * @return string $title
91 | */
92 | public function getTitle()
93 | {
94 | return $this->title;
95 | }
96 |
97 | /**
98 | * @param string $text
99 | */
100 | public function setText($text)
101 | {
102 | $this->text = $text;
103 | }
104 |
105 | /**
106 | * @param string $title
107 | */
108 | public function setTitle($title)
109 | {
110 | $this->title = $title;
111 | }
112 |
113 | /**
114 | * @param string $costomField
115 | */
116 | public function setCostomField($costomField)
117 | {
118 | $this->costomField = $costomField;
119 | }
120 |
121 | /**
122 | * @return string
123 | */
124 | public function getCostomField()
125 | {
126 | return $this->costomField;
127 | }
128 |
129 | /**
130 | * @return \DateTime
131 | */
132 | public function getCreatedAt()
133 | {
134 | return $this->created_at;
135 | }
136 |
137 | /**
138 | * @param \DateTime $created_at
139 | */
140 | public function setCreatedAt($created_at)
141 | {
142 | $this->created_at = $created_at;
143 | }
144 |
145 | /**
146 | * @return ValidTestEntity[]
147 | */
148 | public function getPosts()
149 | {
150 | return $this->posts;
151 | }
152 |
153 | /**
154 | * @param ValidTestEntity[] $posts
155 | */
156 | public function setPosts($posts)
157 | {
158 | $this->posts = $posts;
159 | }
160 |
161 | /**
162 | * @param string $field
163 | */
164 | public function setField($field)
165 | {
166 | $this->privateField = $field;
167 | }
168 |
169 | /**
170 | * @return string
171 | */
172 | public function getField()
173 | {
174 | return $this->privateField;
175 | }
176 |
177 | /**
178 | * @return string
179 | */
180 | public function getPublishDate()
181 | {
182 | return $this->publishDate;
183 | }
184 |
185 | /**
186 | * @param string $publishDate
187 | */
188 | public function setPublishDate($publishDate)
189 | {
190 | $this->publishDate = $publishDate;
191 | }
192 | }
193 |
194 |
--------------------------------------------------------------------------------
/Repository/Repository.php:
--------------------------------------------------------------------------------
1 | solr = $solr;
40 | $this->metaInformation = $metaInformation;
41 |
42 | $this->hydrationMode = HydrationModes::HYDRATE_DOCTRINE;
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | public function find($id)
49 | {
50 | $documentKey = $this->metaInformation->getDocumentName() . '_' . $id;
51 |
52 | $query = new FindByIdentifierQuery();
53 | $query->setIndex($this->metaInformation->getIndex());
54 | $query->setDocumentKey($documentKey);
55 | $query->setEntity($this->metaInformation->getEntity());
56 | $query->setSolr($this->solr);
57 | $query->setHydrationMode($this->hydrationMode);
58 | $found = $this->solr->query($query);
59 |
60 | if (count($found) == 0) {
61 | return null;
62 | }
63 |
64 | return array_pop($found);
65 | }
66 |
67 | /**
68 | * {@inheritdoc}
69 | */
70 | public function findAll()
71 | {
72 | $query = new FindByDocumentNameQuery();
73 | $query->setRows(1000000);
74 | $query->setDocumentName($this->metaInformation->getDocumentName());
75 | $query->setIndex($this->metaInformation->getIndex());
76 | $query->setEntity($this->metaInformation->getEntity());
77 | $query->setSolr($this->solr);
78 | $query->setHydrationMode($this->hydrationMode);
79 |
80 | return $this->solr->query($query);
81 | }
82 |
83 | /**
84 | * {@inheritdoc}
85 | */
86 | public function findBy(array $args)
87 | {
88 | $query = $this->solr->createQuery($this->metaInformation->getEntity());
89 | $query->setHydrationMode($this->hydrationMode);
90 | $query->setRows(100000);
91 | $query->setUseAndOperator(true);
92 | $query->addSearchTerm('id', $this->metaInformation->getDocumentName() . '_*');
93 | $query->setQueryDefaultField('id');
94 |
95 | $helper = $query->getHelper();
96 | foreach ($args as $fieldName => $fieldValue) {
97 | $fieldValue = $helper->escapeTerm($fieldValue);
98 |
99 | $query->addSearchTerm($fieldName, $fieldValue);
100 | }
101 |
102 | return $this->solr->query($query);
103 | }
104 |
105 | /**
106 | * {@inheritdoc}
107 | */
108 | public function findOneBy(array $args)
109 | {
110 | $query = $this->solr->createQuery($this->metaInformation->getEntity());
111 | $query->setHydrationMode($this->hydrationMode);
112 | $query->setRows(1);
113 | $query->setUseAndOperator(true);
114 | $query->addSearchTerm('id', $this->metaInformation->getDocumentName() . '_*');
115 | $query->setQueryDefaultField('id');
116 |
117 | $helper = $query->getHelper();
118 | foreach ($args as $fieldName => $fieldValue) {
119 | $fieldValue = $helper->escapeTerm($fieldValue);
120 |
121 | $query->addSearchTerm($fieldName, $fieldValue);
122 | }
123 |
124 | $found = $this->solr->query($query);
125 |
126 | return array_pop($found);
127 | }
128 |
129 | /**
130 | * @return QueryBuilderInterface
131 | */
132 | public function getQueryBuilder()
133 | {
134 | return $this->solr->getQueryBuilder($this->metaInformation->getEntity());
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/Tests/Client/Solarium/SolariumClientBuilderTest.php:
--------------------------------------------------------------------------------
1 | defaultEndpoints = [
21 | 'unittest' => [
22 | 'schema' => 'http',
23 | 'host' => '127.0.0.1',
24 | 'port' => 8983,
25 | 'path' => '/solr',
26 | 'timeout' => 5,
27 | 'core' => null
28 | ]
29 | ];
30 | }
31 |
32 | public function testCreateClientWithoutDsn()
33 | {
34 | $actual = $this->createClientWithSettings($this->defaultEndpoints);
35 |
36 | $endpoint = $actual->getEndpoint('unittest');
37 | $this->assertEquals('http://127.0.0.1:8983/solr/', $endpoint->getBaseUri());
38 | }
39 |
40 | public function testCreateClientWithoutDsnWithCore()
41 | {
42 | $this->defaultEndpoints['unittest']['core'] = 'core0';
43 |
44 | $actual = $this->createClientWithSettings($this->defaultEndpoints);
45 |
46 | $endpoint = $actual->getEndpoint('unittest');
47 | $this->assertEquals('http://127.0.0.1:8983/solr/core0/', $endpoint->getBaseUri());
48 | }
49 |
50 | /**
51 | * @param string $dsn
52 | * @param string $expectedBaseUri
53 | * @param string $message
54 | *
55 | * @dataProvider dsnProvider
56 | */
57 | public function testCreateClientWithDsn($dsn, $expectedBaseUri, $message)
58 | {
59 | $settings = $this->defaultEndpoints;
60 | $settings['unittest'] = [
61 | 'dsn' => $dsn
62 | ];
63 |
64 | $actual = $this->createClientWithSettings($settings);
65 |
66 | $endpoint = $actual->getEndpoint('unittest');
67 | $this->assertEquals($expectedBaseUri, $endpoint->getBaseUri(), $message);
68 | }
69 |
70 | /**
71 | * @param string $dsn
72 | * @param string $expectedBaseUri
73 | * @param string $message
74 | *
75 | * @dataProvider dsnProvider
76 | */
77 | public function testCreateClientWithDsnAndCore($dsn, $expectedBaseUri, $message)
78 | {
79 | $settings = $this->defaultEndpoints;
80 | $settings['unittest'] = [
81 | 'dsn' => $dsn,
82 | 'core' => 'core0'
83 | ];
84 |
85 | $actual = $this->createClientWithSettings($settings);
86 |
87 | $endpoint = $actual->getEndpoint('unittest');
88 | $this->assertEquals($expectedBaseUri . 'core0/', $endpoint->getBaseUri(), $message . ' with core');
89 | }
90 |
91 | /**
92 | * @return array
93 | */
94 | public function dsnProvider()
95 | {
96 | return [
97 | [
98 | 'http://example.com:1234',
99 | 'http://example.com:1234/',
100 | 'Test DSN without path and any authentication'
101 | ],
102 | [
103 | 'http://example.com:1234/solr',
104 | 'http://example.com:1234/solr/',
105 | 'Test DSN without any authentication'
106 | ],
107 | [
108 | 'http://user@example.com:1234/solr',
109 | 'http://user@example.com:1234/solr/',
110 | 'Test DSN with user-only authentication'
111 | ],
112 | [
113 | 'http://user:secret@example.com:1234/solr',
114 | 'http://user:secret@example.com:1234/solr/',
115 | 'Test DSN with authentication'
116 | ],
117 | [
118 | 'https://example.com:1234/solr',
119 | 'https://example.com:1234/solr/',
120 | 'Test DSN with HTTPS'
121 | ]
122 | ];
123 | }
124 |
125 | /**
126 | * @param array $settings
127 | *
128 | * @return \Solarium\Client
129 | */
130 | private function createClientWithSettings(array $settings)
131 | {
132 | /** @var EventDispatcherInterface $eventDispatcherMock */
133 | $eventDispatcherMock = $this->createMock(EventDispatcherInterface::class);
134 |
135 | return (new SolariumClientBuilder($settings, $eventDispatcherMock))->build();
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/Tests/Fixtures/ValidTestEntity.php:
--------------------------------------------------------------------------------
1 | id;
76 | }
77 |
78 | /**
79 | * @param int $id
80 | */
81 | public function setId($id)
82 | {
83 | $this->id = $id;
84 | }
85 |
86 | /**
87 | * @return string $text
88 | */
89 | public function getText()
90 | {
91 | return $this->text;
92 | }
93 |
94 | /**
95 | * @return string $title
96 | */
97 | public function getTitle()
98 | {
99 | return $this->title;
100 | }
101 |
102 | /**
103 | * @param string $text
104 | */
105 | public function setText($text)
106 | {
107 | $this->text = $text;
108 | }
109 |
110 | /**
111 | * @param string $title
112 | */
113 | public function setTitle($title)
114 | {
115 | $this->title = $title;
116 | }
117 |
118 | /**
119 | * @param string $costomField
120 | */
121 | public function setCostomField($costomField)
122 | {
123 | $this->costomField = $costomField;
124 | }
125 |
126 | /**
127 | * @return string
128 | */
129 | public function getCostomField()
130 | {
131 | return $this->costomField;
132 | }
133 |
134 | /**
135 | * @return \DateTime
136 | */
137 | public function getCreatedAt()
138 | {
139 | return $this->created_at;
140 | }
141 |
142 | /**
143 | * @param \DateTime $created_at
144 | */
145 | public function setCreatedAt($created_at)
146 | {
147 | $this->created_at = $created_at;
148 | }
149 |
150 | /**
151 | * @return ValidTestEntity[]
152 | */
153 | public function getPosts()
154 | {
155 | return $this->posts;
156 | }
157 |
158 | /**
159 | * @param ValidTestEntity[] $posts
160 | */
161 | public function setPosts($posts)
162 | {
163 | $this->posts = $posts;
164 | }
165 |
166 | /**
167 | * @param string $field
168 | */
169 | public function setField($field)
170 | {
171 | $this->privateField = $field;
172 | }
173 |
174 | /**
175 | * @return string
176 | */
177 | public function getField()
178 | {
179 | return $this->privateField;
180 | }
181 |
182 | /**
183 | * @return string
184 | */
185 | public function getPublishDate()
186 | {
187 | return $this->publishDate;
188 | }
189 |
190 | /**
191 | * @param string $publishDate
192 | */
193 | public function setPublishDate($publishDate)
194 | {
195 | $this->publishDate = $publishDate;
196 | }
197 |
198 | /**
199 | * @return array
200 | */
201 | public function getComplexDataType()
202 | {
203 | return $this->complexDataType;
204 | }
205 |
206 | /**
207 | * @param string $complexDataType
208 | */
209 | public function setComplexDataType($complexDataType)
210 | {
211 | $this->complexDataType = $complexDataType;
212 | }
213 |
214 | public function getComplexData()
215 | {
216 | return json_decode($this->complexDataType, true);
217 | }
218 | }
219 |
220 |
--------------------------------------------------------------------------------
/Doctrine/Annotation/Field.php:
--------------------------------------------------------------------------------
1 | '_s',
55 | 'text' => '_t',
56 | 'date' => '_dt',
57 | 'boolean' => '_b',
58 | 'integer' => '_i',
59 | 'long' => '_l',
60 | 'float' => '_f',
61 | 'double' => '_d',
62 | 'datetime' => '_dt',
63 | 'point' => '_p'
64 | );
65 |
66 | /**
67 | * @var array
68 | */
69 | private static $TYP_COMPLEX_MAPPING = array(
70 | 'doubles' => '_ds',
71 | 'floats' => '_fs',
72 | 'longs' => '_ls',
73 | 'integers' => '_is',
74 | 'booleans' => '_bs',
75 | 'dates' => '_dts',
76 | 'texts' => '_txt',
77 | 'strings' => '_ss',
78 | );
79 |
80 | /**
81 | * returns field name with type-suffix:
82 | *
83 | * eg: title_s
84 | *
85 | * @throws \RuntimeException
86 | *
87 | * @return string
88 | */
89 | public function getNameWithAlias()
90 | {
91 | return $this->normalizeName($this->name) . $this->getTypeSuffix($this->type);
92 | }
93 |
94 | /**
95 | * @param string $type
96 | *
97 | * @return string
98 | */
99 | private function getTypeSuffix($type)
100 | {
101 | self::$TYP_MAPPING = array_merge(self::$TYP_COMPLEX_MAPPING, self::$TYP_SIMPLE_MAPPING);
102 |
103 | if ($type == '') {
104 | return '';
105 | }
106 |
107 | if (!isset(self::$TYP_MAPPING[$this->type])) {
108 | return '';
109 | }
110 |
111 | return self::$TYP_MAPPING[$this->type];
112 | }
113 |
114 | /**
115 | * Related object getter name
116 | *
117 | * @return string
118 | */
119 | public function getGetterName()
120 | {
121 | return $this->getter;
122 | }
123 |
124 | /**
125 | * @return string
126 | */
127 | public function getFieldModifier()
128 | {
129 | return $this->fieldModifier;
130 | }
131 |
132 | /**
133 | * @return string
134 | */
135 | public function getValue()
136 | {
137 | return $this->value;
138 | }
139 |
140 | /**
141 | * @return string
142 | */
143 | public function __toString()
144 | {
145 | return $this->name;
146 | }
147 |
148 | /**
149 | * @throws \InvalidArgumentException if boost is not a number
150 | *
151 | * @return number
152 | */
153 | public function getBoost()
154 | {
155 | if (!is_numeric($this->boost)) {
156 | throw new \InvalidArgumentException(sprintf('Invalid boost value %s', $this->boost));
157 | }
158 |
159 | if (($boost = floatval($this->boost)) > 0) {
160 | return $boost;
161 | }
162 |
163 | return null;
164 | }
165 |
166 | /**
167 | * normalize class attributes camelcased names to underscores
168 | * (according to solr specification, document field names should
169 | * contain only lowercase characters and underscores to maintain
170 | * retro compatibility with old components).
171 | *
172 | * @param $name The field name
173 | *
174 | * @return string normalized field name
175 | */
176 | private function normalizeName($name)
177 | {
178 | $words = preg_split('/(?=[A-Z])/', $name);
179 | $words = array_map(
180 | function ($value) {
181 | return strtolower($value);
182 | },
183 | $words
184 | );
185 |
186 | return implode('_', $words);
187 | }
188 |
189 | /**
190 | * @return array
191 | */
192 | public static function getComplexFieldMapping()
193 | {
194 | return self::$TYP_COMPLEX_MAPPING;
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/Tests/DependencyInjection/FSSolrExtensionTest.php:
--------------------------------------------------------------------------------
1 | container = new ContainerBuilder();
25 | }
26 |
27 | private function enableOdmConfig()
28 | {
29 | $this->container->setParameter('doctrine_mongodb.odm.document_managers', array('default' => 'odm.default.mananger'));
30 | }
31 |
32 | private function enableOrmConfig()
33 | {
34 | $this->container->setParameter('doctrine.entity_managers', array('default' => 'orm.default.mananger'));
35 | }
36 |
37 | private function commonConfig()
38 | {
39 | return array(array(
40 |
41 | 'endpoints' => array(
42 | 'default' => array(
43 | 'host' => '192.168.178.24',
44 | 'port' => 8983,
45 | 'path' => '/solr/',
46 | )
47 | )
48 | ));
49 | }
50 |
51 | public function testDoctrineORMSetup()
52 | {
53 | $this->enableOrmConfig();
54 | $config = $this->commonConfig();
55 |
56 | $extension = new FSSolrExtension();
57 | $extension->load($config, $this->container);
58 |
59 | $this->assertTrue($this->container->has('solr.document.orm.subscriber'), 'orm subscriber');
60 |
61 | $this->assertDefinitionHasTag('solr.document.orm.subscriber', 'doctrine.event_subscriber');
62 |
63 | $this->assertClassnameResolverHasOrmDefaultConfiguration();
64 | }
65 |
66 | public function testDoctrineODMSetup()
67 | {
68 | $config = $this->commonConfig();
69 | $this->enableOdmConfig();
70 |
71 | $extension = new FSSolrExtension();
72 | $extension->load($config, $this->container);
73 |
74 | $this->assertTrue($this->container->has('solr.document.odm.subscriber'), 'odm subscriber');
75 |
76 | $this->assertDefinitionHasTag('solr.document.odm.subscriber', 'doctrine_mongodb.odm.event_subscriber');
77 |
78 | $this->assertClassnameResolverHasOdmDefaultConfiguration();
79 | }
80 |
81 | /**
82 | * @test
83 | */
84 | public function solrListensToOdmAndOrmEvents()
85 | {
86 | $config = $this->commonConfig();
87 | $this->enableOdmConfig();
88 | $this->enableOrmConfig();
89 |
90 | $extension = new FSSolrExtension();
91 | $extension->load($config, $this->container);
92 |
93 | $this->assertTrue($this->container->has('solr.document.odm.subscriber'), 'odm subscriber');
94 | $this->assertDefinitionHasTag('solr.document.odm.subscriber', 'doctrine_mongodb.odm.event_subscriber');
95 |
96 | $this->assertTrue($this->container->has('solr.document.orm.subscriber'), 'orm subscriber');
97 | $this->assertDefinitionHasTag('solr.document.orm.subscriber', 'doctrine.event_subscriber');
98 | }
99 |
100 | private function assertClassnameResolverHasOrmDefaultConfiguration()
101 | {
102 | $doctrineConfiguration = $this->getReferenzIdOfCalledMethod();
103 |
104 | $this->assertEquals('doctrine.orm.default_configuration', $doctrineConfiguration);
105 | }
106 |
107 | private function assertClassnameResolverHasOdmDefaultConfiguration()
108 | {
109 | $doctrineConfiguration = $this->getReferenzIdOfCalledMethod();
110 |
111 | $this->assertEquals('doctrine_mongodb.odm.default_configuration', $doctrineConfiguration);
112 | }
113 |
114 | /**
115 | * @return Reference
116 | */
117 | private function getReferenzIdOfCalledMethod()
118 | {
119 | $methodCalls = $this->container->getDefinition('solr.doctrine.classnameresolver.known_entity_namespaces')->getMethodCalls();
120 |
121 | $firstMethodCall = $methodCalls[0];
122 | $references = $firstMethodCall[1];
123 | $reference = $references[0];
124 |
125 | return $reference;
126 | }
127 |
128 | private function assertDefinitionHasTag($definition, $tag)
129 | {
130 | $tags = $this->container->getDefinition($definition)->getTags();
131 |
132 | $this->assertTrue(
133 | $this->container->getDefinition($definition)->hasTag($tag),
134 | sprintf('%s with %s tag, has %s', $definition, $tag, print_r($tags, true))
135 | );
136 | }
137 | }
138 |
139 |
--------------------------------------------------------------------------------
/Query/QueryBuilderInterface.php:
--------------------------------------------------------------------------------
1 | reader = new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader());
29 | }
30 |
31 | /**
32 | * @test
33 | */
34 | public function documentShouldMapToEntity()
35 | {
36 | $obj = new SolrDocumentStub(array(
37 | 'id' => 'document_1',
38 | 'title_t' => 'foo',
39 | 'publish_date_s' => '10.10.2016',
40 | 'field_s' => 'value 1234',
41 | 'unknown_field_s' => 'value'
42 | ));
43 |
44 | $entity = new ValidTestEntity();
45 |
46 | $metainformations = new MetaInformationFactory($this->reader);
47 | $metainformations = $metainformations->loadInformation($entity);
48 |
49 | $hydrator = new ValueHydrator();
50 | $hydratedDocument = $hydrator->hydrate($obj, $metainformations);
51 |
52 | $this->assertTrue($hydratedDocument instanceof $entity);
53 | $this->assertEquals(1, $entity->getId());
54 | $this->assertEquals('foo', $entity->getTitle());
55 | $this->assertEquals('10.10.2016', $entity->getPublishDate());
56 | $this->assertEquals('value 1234', $entity->getField());
57 | }
58 |
59 | /**
60 | * @test
61 | */
62 | public function underscoreFieldBecomeCamelCase()
63 | {
64 | $obj = new SolrDocumentStub(array(
65 | 'id' => 'document_1',
66 | 'created_at_d' => 12345
67 | ));
68 |
69 | $entity = new ValidTestEntity();
70 |
71 | $metainformations = new MetaInformationFactory($this->reader);
72 | $metainformations = $metainformations->loadInformation($entity);
73 |
74 | $hydrator = new ValueHydrator();
75 | $hydratedDocument = $hydrator->hydrate($obj, $metainformations);
76 |
77 | $this->assertTrue($hydratedDocument instanceof $entity);
78 | $this->assertEquals(1, $entity->getId());
79 | $this->assertEquals(12345, $entity->getCreatedAt());
80 | }
81 |
82 | /**
83 | * @test
84 | */
85 | public function doNotOverwriteComplexTypes_Collection()
86 | {
87 | $obj = new SolrDocumentStub(array(
88 | 'id' => 'document_1',
89 | 'title_t' => 'foo',
90 | 'collection_ss' => array('title 1', 'title 2')
91 | ));
92 |
93 | $entity = new ValidTestEntityWithCollection();
94 |
95 | $metainformations = new MetaInformationFactory($this->reader);
96 | $metainformations = $metainformations->loadInformation($entity);
97 |
98 | $hydrator = new ValueHydrator();
99 | $hydratedDocument = $hydrator->hydrate($obj, $metainformations);
100 |
101 | $this->assertTrue($hydratedDocument instanceof $entity);
102 | $this->assertEquals(1, $entity->getId());
103 | $this->assertEquals('foo', $entity->getTitle());
104 | $this->assertEquals(array('title 1', 'title 2'), $entity->getCollection());
105 | }
106 |
107 | /**
108 | * @test
109 | */
110 | public function doNotOverwriteComplexTypes_Relation()
111 | {
112 | $obj = new SolrDocumentStub(array(
113 | 'id' => 'document_1',
114 | 'title_t' => 'foo',
115 | 'posts_ss' => array('title 1', 'title2')
116 | ));
117 |
118 | $entity1 = new ValidTestEntity();
119 | $entity1->setTitle('title 1');
120 |
121 | $entity = new ValidTestEntityWithRelation();
122 | $entity->setRelation($entity1);
123 |
124 | $metainformations = new MetaInformationFactory($this->reader);
125 | $metainformations = $metainformations->loadInformation($entity);
126 |
127 | $hydrator = new ValueHydrator();
128 | $hydratedDocument = $hydrator->hydrate($obj, $metainformations);
129 |
130 | $this->assertTrue($hydratedDocument instanceof $entity);
131 | $this->assertEquals(1, $entity->getId());
132 | $this->assertEquals('foo', $entity->getTitle());
133 |
134 | $this->assertTrue($hydratedDocument->getRelation() === $entity1);
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/Tests/MulticoreSolrTest.php:
--------------------------------------------------------------------------------
1 | createMock(Query::class);
25 | $updateQuery->expects($this->once())
26 | ->method('addDocument');
27 |
28 | $updateQuery->expects($this->once())
29 | ->method('addCommit');
30 |
31 | $this->solrClientFake
32 | ->expects($this->once())
33 | ->method('createUpdate')
34 | ->will($this->returnValue($updateQuery));
35 |
36 | return $updateQuery;
37 | }
38 |
39 | /**
40 | * @test
41 | */
42 | public function addDocumentToAllCores()
43 | {
44 | $updateQuery = $this->assertUpdateQueryExecuted();
45 |
46 | $this->eventDispatcher->expects($this->any())
47 | ->method('dispatch');
48 |
49 | $this->solrClientFake->expects($this->once())
50 | ->method('getEndpoints')
51 | ->will($this->returnValue(array(
52 | 'core0' => array(),
53 | 'core1' => array()
54 | )));
55 |
56 | $this->solrClientFake->expects($this->at(2))
57 | ->method('update')
58 | ->with($updateQuery, 'core0');
59 |
60 | $this->solrClientFake->expects($this->at(3))
61 | ->method('update')
62 | ->with($updateQuery, 'core1');
63 |
64 | $this->mapper->expects($this->once())
65 | ->method('toDocument')
66 | ->will($this->returnValue(new DocumentStub()));
67 |
68 | $solr = new Solr($this->solrClientFake, $this->eventDispatcher, $this->metaFactory, $this->mapper);
69 | $solr->addDocument(new ValidTestEntityAllCores());
70 | }
71 |
72 | /**
73 | * @test
74 | */
75 | public function updateDocumentInAllCores()
76 | {
77 | $updateQuery = $this->assertUpdateQueryExecuted();
78 |
79 | $this->eventDispatcher->expects($this->exactly(2))
80 | ->method('dispatch');
81 |
82 | $this->solrClientFake->expects($this->once())
83 | ->method('getEndpoints')
84 | ->will($this->returnValue(array(
85 | 'core0' => array(),
86 | 'core1' => array()
87 | )));
88 |
89 | $this->solrClientFake->expects($this->at(2))
90 | ->method('update')
91 | ->with($updateQuery, 'core0');
92 |
93 | $this->solrClientFake->expects($this->at(3))
94 | ->method('update')
95 | ->with($updateQuery, 'core1');
96 |
97 | $this->mapper->expects($this->once())
98 | ->method('toDocument')
99 | ->will($this->returnValue(new DocumentStub()));
100 |
101 | $solr = new Solr($this->solrClientFake, $this->eventDispatcher, $this->metaFactory, $this->mapper);
102 | $solr->updateDocument(new ValidTestEntityAllCores());
103 | }
104 |
105 | /**
106 | * @test
107 | */
108 | public function removeDocumentFromAllCores()
109 | {
110 | $this->mapper->expects($this->once())
111 | ->method('toDocument')
112 | ->will($this->returnValue(new DocumentStub()));
113 |
114 | $this->solrClientFake->expects($this->once())
115 | ->method('getEndpoints')
116 | ->will($this->returnValue(array(
117 | 'core0' => array(),
118 | 'core1' => array()
119 | )));
120 |
121 | $deleteQuery = $this->createMock(Query::class);
122 | $deleteQuery->expects($this->once())
123 | ->method('addDeleteQuery')
124 | ->with($this->isType('string'));
125 |
126 | $deleteQuery->expects($this->once())
127 | ->method('addCommit');
128 |
129 | $this->solrClientFake
130 | ->expects($this->once())
131 | ->method('createUpdate')
132 | ->will($this->returnValue($deleteQuery));
133 |
134 | $this->solrClientFake->expects($this->exactly(2))
135 | ->method('update');
136 |
137 | $solr = new Solr($this->solrClientFake, $this->eventDispatcher, $this->metaFactory, $this->mapper);
138 | $solr->removeDocument(new ValidTestEntityAllCores());
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/Tests/AbstractSolrTest.php:
--------------------------------------------------------------------------------
1 | metaFactory = new MetaInformationFactory(new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader()));
36 | $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
37 | $this->mapper = $this->getMockBuilder(EntityMapperInterface::class)
38 | ->disableOriginalConstructor()
39 | ->setMethods(array('setMappingCommand', 'toDocument', 'toEntity', 'setHydrationMode'))
40 | ->getMock();
41 |
42 | $this->solrClientFake = $this->createMock(Client::class);
43 |
44 | $this->solr = new Solr($this->solrClientFake, $this->eventDispatcher, $this->metaFactory, $this->mapper);
45 | }
46 |
47 | protected function assertUpdateQueryExecuted($index = null)
48 | {
49 | $updateQuery = $this->createMock(UpdateQuery::class);
50 | $updateQuery->expects($this->once())
51 | ->method('addDocument');
52 |
53 | $updateQuery->expects($this->once())
54 | ->method('addCommit');
55 |
56 | $this->solrClientFake
57 | ->expects($this->once())
58 | ->method('createUpdate')
59 | ->will($this->returnValue($updateQuery));
60 |
61 | $this->solrClientFake
62 | ->expects($this->once())
63 | ->method('update')
64 | ->with($updateQuery, $index);
65 |
66 | return $updateQuery;
67 | }
68 |
69 | protected function assertUpdateQueryWasNotExecuted()
70 | {
71 | $updateQuery = $this->createMock(UpdateQuery::class);
72 | $updateQuery->expects($this->never())
73 | ->method('addDocument');
74 |
75 | $updateQuery->expects($this->never())
76 | ->method('addCommit');
77 |
78 | $this->solrClientFake
79 | ->expects($this->never())
80 | ->method('createUpdate');
81 | }
82 |
83 | protected function assertDeleteQueryWasExecuted()
84 | {
85 | $deleteQuery = $this->createMock(UpdateQuery::class);
86 | $deleteQuery->expects($this->once())
87 | ->method('addDeleteQuery')
88 | ->with($this->isType('string'));
89 |
90 | $deleteQuery->expects($this->once())
91 | ->method('addCommit');
92 |
93 | $this->solrClientFake
94 | ->expects($this->once())
95 | ->method('createUpdate')
96 | ->will($this->returnValue($deleteQuery));
97 |
98 | $this->solrClientFake
99 | ->expects($this->once())
100 | ->method('update')
101 | ->with($deleteQuery);
102 | }
103 |
104 | protected function setupMetaFactoryLoadOneCompleteInformation($metaInformation = null)
105 | {
106 | if (null === $metaInformation) {
107 | $metaInformation = MetaTestInformationFactory::getMetaInformation();
108 | }
109 |
110 | $this->metaFactory->expects($this->once())
111 | ->method('loadInformation')
112 | ->will($this->returnValue($metaInformation));
113 | }
114 |
115 | protected function assertQueryWasExecuted($data = array(), $index)
116 | {
117 | $selectQuery = $this->createMock(SelectQuery::class);
118 | $selectQuery->expects($this->once())
119 | ->method('setQuery');
120 |
121 | $queryResult = new ResultFake($data);
122 |
123 | $this->solrClientFake
124 | ->expects($this->once())
125 | ->method('createSelect')
126 | ->will($this->returnValue($selectQuery));
127 |
128 | $this->solrClientFake
129 | ->expects($this->once())
130 | ->method('select')
131 | ->will($this->returnValue($queryResult));
132 | }
133 |
134 | protected function mapOneDocument()
135 | {
136 | $this->mapper->expects($this->once())
137 | ->method('toDocument')
138 | ->will($this->returnValue($this->createMock(DocumentInterface::class)));
139 | }
140 | }
--------------------------------------------------------------------------------
/DependencyInjection/FSSolrExtension.php:
--------------------------------------------------------------------------------
1 | load('services.xml');
21 | $loader->load('event_listener.xml');
22 | $loader->load('log_listener.xml');
23 |
24 | $configuration = new Configuration();
25 | $config = $this->processConfiguration($configuration, $configs);
26 |
27 | $this->setupClients($config, $container);
28 |
29 | if (!$container->hasParameter('solr.auto_index')) {
30 | $container->setParameter('solr.auto_index', $config['auto_index']);
31 | }
32 |
33 | $this->setupDoctrineListener($config, $container);
34 | $this->setupDoctrineConfiguration($config, $container);
35 |
36 | }
37 |
38 | /**
39 | * @param array $config
40 | * @param ContainerBuilder $container
41 | */
42 | private function setupClients(array $config, ContainerBuilder $container)
43 | {
44 | $endpoints = $config['endpoints'];
45 |
46 | $builderDefinition = $container->getDefinition('solr.client.adapter.builder');
47 | $builderDefinition->replaceArgument(0, $endpoints);
48 | $builderDefinition->addMethodCall('addPlugin', array('request_debugger', new Reference('solr.debug.client_debugger')));
49 | }
50 |
51 | /**
52 | *
53 | * @param array $config
54 | * @param ContainerBuilder $container
55 | */
56 | private function setupDoctrineConfiguration(array $config, ContainerBuilder $container)
57 | {
58 | if ($this->isOrmConfigured($container)) {
59 | $entityManagers = $container->getParameter('doctrine.entity_managers');
60 |
61 | $entityManagersNames = array_keys($entityManagers);
62 | foreach ($entityManagersNames as $entityManager) {
63 | $container->getDefinition('solr.doctrine.classnameresolver.known_entity_namespaces')->addMethodCall(
64 | 'addEntityNamespaces',
65 | array(new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager)))
66 | );
67 | }
68 | }
69 |
70 | if ($this->isODMConfigured($container)) {
71 | $documentManagers = $container->getParameter('doctrine_mongodb.odm.document_managers');
72 |
73 | $documentManagersNames = array_keys($documentManagers);
74 | foreach ($documentManagersNames as $documentManager) {
75 | $container->getDefinition('solr.doctrine.classnameresolver.known_entity_namespaces')->addMethodCall(
76 | 'addDocumentNamespaces',
77 | array(new Reference(sprintf('doctrine_mongodb.odm.%s_configuration', $documentManager)))
78 | );
79 | }
80 | }
81 |
82 | $container->getDefinition('solr.meta.information.factory')->addMethodCall(
83 | 'setClassnameResolver',
84 | array(new Reference('solr.doctrine.classnameresolver'))
85 | );
86 | }
87 |
88 | /**
89 | * doctrine_orm and doctrine_mongoDB can't be used together. mongo_db wins when it is configured.
90 | *
91 | * listener-methods expecting different types of events
92 | *
93 | * @param array $config
94 | * @param ContainerBuilder $container
95 | */
96 | private function setupDoctrineListener(array $config, ContainerBuilder $container)
97 | {
98 | $autoIndexing = $container->getParameter('solr.auto_index');
99 |
100 | if ($autoIndexing == false) {
101 | return;
102 | }
103 |
104 | if ($this->isODMConfigured($container)) {
105 | $container->getDefinition('solr.document.odm.subscriber')->addTag('doctrine_mongodb.odm.event_subscriber');
106 | }
107 |
108 | if ($this->isOrmConfigured($container)) {
109 | $container->getDefinition('solr.document.orm.subscriber')->addTag('doctrine.event_subscriber');
110 | }
111 | }
112 |
113 | /**
114 | * @param ContainerBuilder $container
115 | *
116 | * @return boolean
117 | */
118 | private function isODMConfigured(ContainerBuilder $container)
119 | {
120 | return $container->hasParameter('doctrine_mongodb.odm.document_managers');
121 | }
122 |
123 | /**
124 | * @param ContainerBuilder $container
125 | *
126 | * @return boolean
127 | */
128 | private function isOrmConfigured(ContainerBuilder $container)
129 | {
130 | return $container->hasParameter('doctrine.entity_managers');
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/Command/ShowSchemaCommand.php:
--------------------------------------------------------------------------------
1 | setName('solr:schema:show')
20 | ->setDescription('Show configured entities and their fields');
21 | }
22 |
23 | /**
24 | * {@inheritdoc}
25 | */
26 | protected function execute(InputInterface $input, OutputInterface $output)
27 | {
28 | $namespaces = $this->getContainer()->get('solr.doctrine.classnameresolver.known_entity_namespaces');
29 | $metaInformationFactory = $this->getContainer()->get('solr.meta.information.factory');
30 |
31 | foreach ($namespaces->getEntityClassnames() as $classname) {
32 | try {
33 | $metaInformation = $metaInformationFactory->loadInformation($classname);
34 |
35 | if ($metaInformation->isNested()) {
36 | continue;
37 | }
38 | } catch (SolrMappingException $e) {
39 | continue;
40 | }
41 |
42 | $nested = '';
43 | if ($metaInformation->isNested()) {
44 | $nested = '(nested)';
45 | }
46 | $output->writeln(sprintf('%s %s', $classname, $nested));
47 | $output->writeln(sprintf('Documentname: %s', $metaInformation->getDocumentName()));
48 | $output->writeln(sprintf('Document Boost: %s', $metaInformation->getBoost()?$metaInformation->getBoost(): '-'));
49 |
50 | $simpleFields = $this->getSimpleFields($metaInformation);
51 |
52 | $rows = [];
53 | foreach ($simpleFields as $documentField => $property) {
54 | if ($field = $metaInformation->getField($documentField)) {
55 | $rows[] = [$property, $documentField, $field->boost];
56 | }
57 | }
58 | $this->renderTable($output, $rows);
59 |
60 | $nestedFields = $this->getNestedFields($metaInformation);
61 | if (count($nestedFields) == 0) {
62 | return;
63 | }
64 |
65 | $output->writeln(sprintf('Fields (%s) with nested documents', count($nestedFields)));
66 |
67 | foreach ($nestedFields as $idField) {
68 | $propertyName = substr($idField, 0, strpos($idField, '.'));
69 |
70 | if ($nestedField = $metaInformation->getField($propertyName)) {
71 | $output->writeln(sprintf('Field %s contains nested class %s', $propertyName, $nestedField->nestedClass));
72 |
73 | $nestedDocument = $metaInformationFactory->loadInformation($nestedField->nestedClass);
74 | $rows = [];
75 | foreach ($nestedDocument->getFieldMapping() as $documentField => $property) {
76 | $field = $nestedDocument->getField($documentField);
77 |
78 | if ($field === null) {
79 | continue;
80 | }
81 |
82 | $rows[] = [$property, $documentField, $field->boost];
83 | }
84 |
85 | $this->renderTable($output, $rows);
86 | }
87 | }
88 |
89 | }
90 |
91 | }
92 |
93 | /**
94 | * @param OutputInterface $output
95 | * @param array $rows
96 | */
97 | private function renderTable(OutputInterface $output, array $rows)
98 | {
99 | $table = new Table($output);
100 | $table->setHeaders(array('Property', 'Document Fieldname', 'Boost'));
101 | $table->setRows($rows);
102 |
103 | $table->render();
104 | }
105 |
106 | /**
107 | * @param MetaInformationInterface $metaInformation
108 | *
109 | * @return array
110 | */
111 | private function getSimpleFields(MetaInformationInterface $metaInformation)
112 | {
113 | $simpleFields = array_filter($metaInformation->getFieldMapping(), function ($field) {
114 | if (strpos($field, '.') === false) {
115 | return true;
116 | }
117 |
118 | return false;
119 | });
120 |
121 | return $simpleFields;
122 | }
123 |
124 | /**
125 | * @param MetaInformationInterface $metaInformation
126 | *
127 | * @return array
128 | */
129 | protected function getNestedFields(MetaInformationInterface $metaInformation)
130 | {
131 | $complexFields = array_filter($metaInformation->getFieldMapping(), function ($field) {
132 | if (strpos($field, '.id') !== false) {
133 | return true;
134 | }
135 |
136 | return false;
137 | });
138 |
139 | return $complexFields;
140 | }
141 | }
--------------------------------------------------------------------------------
/Resources/doc/index_relations.md:
--------------------------------------------------------------------------------
1 | # Index OneToOne/ManyToOne relation
2 |
3 | Given you have the following entity with a ManyToOne relation to `Category`.
4 |
5 | ```php
6 | setTitle('post category #1');
65 |
66 | $post = new Post();
67 | $post->setTitle('a post title');
68 | $post->setCategory($category);
69 |
70 | $em = $this->getDoctrine()->getManager();
71 | $em->persist($post);
72 | $em->flush();
73 | ```
74 |
75 | ### Quering the relation
76 |
77 | ```php
78 | $posts = $this->get('solr.client')->getRepository('AcmeDemoBundle:Post')->findOneBy(array(
79 | 'category' => 'post category #1'
80 | ));
81 | ```
82 |
83 | # Index OneToMany relation
84 |
85 | Given you have the following `Post` entity with a OneToMany relation to `Tag`.
86 |
87 | Again you can index the collection in two ways:
88 |
89 | - flat strings representation
90 | - full objects
91 |
92 | ## flat strings representation
93 |
94 | ```php
95 | setTitle($postTitle);
141 | $post->setText('relation');
142 | $post->setTags(array(
143 | new Tag('tag #1'),
144 | new Tag('tag #2'),
145 | new Tag('tag #3')
146 | ));
147 |
148 | $em = $this->getDoctrine()->getManager();
149 | $em->persist($post);
150 | $em->flush();
151 | ```
152 |
153 | Which will result in a document like this:
154 |
155 | ```json
156 | "docs": [
157 | {
158 | "id": "post_391",
159 | "title_s": "post 25.03.2016",
160 | "text_t": "relation",
161 | "tags_ss": [
162 | "tag #1",
163 | "tag #2",
164 | "tag #3"
165 | ],
166 | "_version_": 1529771282767282200
167 | }
168 | ]
169 | ```
170 |
171 | ### Quering the strings collection
172 |
173 | Now `Post` can be searched like this
174 |
175 | ```php
176 | $posts = $this->get('solr.client')->getRepository('AcmeDemoBundle:Post')->findOneBy(array(
177 | 'tags' => 'tag #1'
178 | ));
179 | ```
180 |
181 | ## Index full objects
182 |
183 | Post entity:
184 |
185 | ```php
186 | /**
187 | * @Solr\Field(type="strings", nestedClass="Acme\DemoBundle\Entity\Tag")
188 | *
189 | * @ORM\OneToMany(targetEntity="Acme\DemoBundle\Entity\Tag", mappedBy="post", cascade={"persist"})
190 | */
191 | private $tags;
192 | ```
193 |
194 | Mark the `Tag` entity as Nested
195 |
196 | ```php
197 | /**
198 | * Tag
199 | *
200 | * @Solr\Nested()
201 | *
202 | * @ORM\Table()
203 | * @ORM\Entity
204 | */
205 | class Tag
206 | {
207 | /**
208 | * @var integer
209 | *
210 | * @Solr\Id
211 | *
212 | * orm stuff
213 | */
214 | private $id;
215 |
216 | /**
217 | * @var string
218 | *
219 | * @Solr\Field(type="string")
220 | *
221 | * @ORM\Column(name="name", type="string", length=255)
222 | */
223 | private $name;
224 |
225 | // getter and setter
226 | }
227 | ```
228 |
229 | ## Querying the collection
230 |
231 | Now `Post` can be searched like this
232 |
233 | ```php
234 | $posts = $this->get('solr.client')->getRepository('AcmeDemoBundle:Post')->findOneBy(array(
235 | 'tags.name' => 'tag #1'
236 | ));
237 | ```
238 |
239 |
--------------------------------------------------------------------------------
/Doctrine/Hydration/ValueHydrator.php:
--------------------------------------------------------------------------------
1 | cache[$metaInformation->getDocumentName()])) {
28 | $this->cache[$metaInformation->getDocumentName()] = array();
29 | }
30 |
31 | $targetEntity = $metaInformation->getEntity();
32 |
33 | $reflectionClass = new \ReflectionClass($targetEntity);
34 | foreach ($document as $property => $value) {
35 | if ($property === MetaInformationInterface::DOCUMENT_KEY_FIELD_NAME) {
36 | $value = $this->removePrefixedKeyValues($value);
37 | }
38 |
39 | // skip field if value is array or "flat" object
40 | // hydrated object should contain a list of real entities / entity
41 | if ($this->mapValue($property, $value, $metaInformation) == false) {
42 | continue;
43 | }
44 |
45 | if (isset($this->cache[$metaInformation->getDocumentName()][$property])) {
46 | $this->cache[$metaInformation->getDocumentName()][$property]->setValue($targetEntity, $value);
47 |
48 | continue;
49 | }
50 |
51 | // find setter method
52 | $camelCasePropertyName = $this->toCamelCase($this->removeFieldSuffix($property));
53 | $setterMethodName = 'set'.ucfirst($camelCasePropertyName);
54 | if (method_exists($targetEntity, $setterMethodName)) {
55 | $accessor = new MethodCallPropertyAccessor($setterMethodName);
56 | $accessor->setValue($targetEntity, $value);
57 |
58 | $this->cache[$metaInformation->getDocumentName()][$property] = $accessor;
59 |
60 | continue;
61 | }
62 |
63 |
64 | if ($reflectionClass->hasProperty($this->removeFieldSuffix($property))) {
65 | $classProperty = $reflectionClass->getProperty($this->removeFieldSuffix($property));
66 | } else {
67 | // could no found document-field in underscore notation, transform them to camel-case notation
68 | $camelCasePropertyName = $this->toCamelCase($this->removeFieldSuffix($property));
69 | if ($reflectionClass->hasProperty($camelCasePropertyName) == false) {
70 | continue;
71 | }
72 |
73 | $classProperty = $reflectionClass->getProperty($camelCasePropertyName);
74 | }
75 |
76 | $accessor = new PrivatePropertyAccessor($classProperty);
77 | $accessor->setValue($targetEntity, $value);
78 |
79 | $this->cache[$metaInformation->getDocumentName()][$property] = $accessor;
80 | }
81 |
82 | return $targetEntity;
83 | }
84 |
85 | /**
86 | * returns the clean fieldname without type-suffix
87 | *
88 | * eg: title_s => title
89 | *
90 | * @param string $property
91 | *
92 | * @return string
93 | */
94 | protected function removeFieldSuffix($property)
95 | {
96 | if (($pos = strrpos($property, '_')) !== false) {
97 | return substr($property, 0, $pos);
98 | }
99 |
100 | return $property;
101 | }
102 |
103 | /**
104 | * keyfield product_1 becomes 1
105 | *
106 | * @param string $value
107 | *
108 | * @return string
109 | */
110 | public function removePrefixedKeyValues($value)
111 | {
112 | if (($pos = strrpos($value, '_')) !== false) {
113 | return substr($value, ($pos + 1));
114 | }
115 |
116 | return $value;
117 | }
118 |
119 | /**
120 | * returns field name camelcased if it has underlines
121 | *
122 | * eg: user_id => userId
123 | *
124 | * @param string $fieldname
125 | *
126 | * @return string
127 | */
128 | private function toCamelCase($fieldname)
129 | {
130 | $words = str_replace('_', ' ', $fieldname);
131 | $words = ucwords($words);
132 | $pascalCased = str_replace(' ', '', $words);
133 |
134 | return lcfirst($pascalCased);
135 | }
136 |
137 | /**
138 | * Check if given field and value can be mapped
139 | *
140 | * @param string $fieldName
141 | * @param string $value
142 | * @param MetaInformationInterface $metaInformation
143 | *
144 | * @return bool
145 | */
146 | public function mapValue($fieldName, $value, MetaInformationInterface $metaInformation)
147 | {
148 | return true;
149 | }
150 | }
--------------------------------------------------------------------------------
/Tests/Doctrine/ORM/Listener/EntityIndexerSubscriberTest.php:
--------------------------------------------------------------------------------
1 | logger = $this->createMock(LoggerInterface::class);
37 | $this->solr = $this->createMock(SolrInterface::class);
38 | $this->metaInformationFactory = new MetaInformationFactory(new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader()));
39 |
40 | $this->subscriber = new EntityIndexerSubscriber($this->solr, $this->metaInformationFactory, $this->logger);
41 | }
42 |
43 | /**
44 | * @test
45 | */
46 | public function separteDeletedRootEntitiesFromNested()
47 | {
48 | $nested = new NestedEntity();
49 | $nested->setId(uniqid());
50 |
51 | $entity = new ValidTestEntityWithCollection();
52 | $entity->setId(uniqid());
53 | $entity->setCollection(new ArrayCollection([$nested]));
54 |
55 | $objectManager = $this->createMock(ObjectManager::class);
56 |
57 | $this->solr->expects($this->at(0))
58 | ->method('removeDocument')
59 | ->with($this->callback(function(ValidTestEntityWithCollection $entity) {
60 | if (count($entity->getCollection())) {
61 | return false;
62 | }
63 |
64 | return true;
65 | }));
66 |
67 | $this->solr->expects($this->at(1))
68 | ->method('removeDocument')
69 | ->with($this->callback(function($entity) {
70 | if (!$entity instanceof NestedEntity) {
71 | return false;
72 | }
73 |
74 | return true;
75 | }));
76 |
77 | $deleteRootEntityEvent = new LifecycleEventArgs($entity, $objectManager);
78 | $this->subscriber->preRemove($deleteRootEntityEvent);
79 |
80 | $deleteNestedEntityEvent = new LifecycleEventArgs($nested, $objectManager);
81 | $this->subscriber->preRemove($deleteNestedEntityEvent);
82 |
83 | $entityManager = $this->createMock(EntityManagerInterface::class);
84 |
85 | $this->subscriber->postFlush(new PostFlushEventArgs($entityManager));
86 | }
87 |
88 | /**
89 | * @test
90 | */
91 | public function indexOnlyModifiedEntites()
92 | {
93 | $changedEntity = new ValidTestEntityWithCollection();
94 | $this->solr->expects($this->once())
95 | ->method('updateDocument')
96 | ->with($changedEntity);
97 |
98 | $unitOfWork = $this->createMock(UnitOfWork::class);
99 | $unitOfWork->expects($this->at(0))
100 | ->method('getEntityChangeSet')
101 | ->willReturn(['title' => 'value']);
102 |
103 | $unitOfWork->expects($this->at(1))
104 | ->method('getEntityChangeSet')
105 | ->willReturn([]);
106 |
107 | $objectManager = $this->createMock(EntityManagerInterface::class);
108 | $objectManager->expects($this->any())
109 | ->method('getUnitOfWork')
110 | ->willReturn($unitOfWork);
111 |
112 | $updateEntityEvent1 = new LifecycleEventArgs($changedEntity, $objectManager);
113 |
114 | $unmodifiedEntity = new ValidTestEntityWithCollection();
115 | $updateEntityEvent2 = new LifecycleEventArgs($unmodifiedEntity, $objectManager);
116 |
117 | $this->subscriber->postUpdate($updateEntityEvent1);
118 | $this->subscriber->postUpdate($updateEntityEvent2);
119 | }
120 |
121 | /**
122 | * @test
123 | */
124 | public function doNotFailHardIfNormalEntityIsPersisted()
125 | {
126 | $this->solr->expects($this->never())
127 | ->method('addDocument');
128 |
129 | $this->solr->expects($this->never())
130 | ->method('removeDocument');
131 |
132 | $entity = new NotIndexedEntity();
133 |
134 | $objectManager = $this->createMock(EntityManagerInterface::class);
135 |
136 | $lifecycleEventArgs = new LifecycleEventArgs($entity, $objectManager);
137 |
138 | $this->subscriber->postPersist($lifecycleEventArgs);
139 | $this->subscriber->preRemove($lifecycleEventArgs);
140 |
141 |
142 |
143 | $this->subscriber->postFlush(new PostFlushEventArgs($objectManager));
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/Resources/doc/indexing.md:
--------------------------------------------------------------------------------
1 | # How to index more than 500k entities
2 |
3 | If you want to index a lot of entities then it is not a good idea to use `solr:index:populate`.
4 | The command works well with 100k-200k entites everything larger than that makes the command incredible slow and needs a lot of memory. This is because doctrine is not designed to handle hundred-thousands of entities.
5 |
6 | In my following example I have a `person` table with 5000000 rows and three columns: `id`, `name` and `email`. The resulting documents are schemaless, so all fields have a suffix e.g. `name_s`.
7 |
8 | Here are some possibilities which works well for me:
9 |
10 | ## CSV export with MySQL Prepared Statement + Solr PostTool
11 |
12 | This solution does not use PHP.
13 |
14 | 1. export your data to person.csv
15 | ```sql
16 | SET @TS = DATE_FORMAT(NOW(),'_%Y_%m_%d_%H_%i_%s');
17 |
18 | SET @FOLDER = '/tmp/'; -- target dir
19 | SET @PREFIX = 'person';
20 | SET @EXT = '.csv';
21 |
22 | -- first select defines the header of the csv-file
23 | SET @CMD = CONCAT("SELECT 'id', 'name_s', 'email_s' UNION ALL SELECT * FROM person INTO OUTFILE '",@FOLDER,@PREFIX,@TS,@EXT,
24 | "' FIELDS ENCLOSED BY '\"' TERMINATED BY ',' ESCAPED BY '\"'",
25 | " LINES TERMINATED BY '\r\n';");
26 |
27 | PREPARE statement FROM @CMD;
28 |
29 | EXECUTE statement;
30 | ```
31 |
32 | Then run this SQL-script:
33 |
34 | ```bash
35 | mysql -udbuser -p123 dbname < dump_person_table.sql
36 | ```
37 |
38 | The resulting file looks like this: `/tmp/person_2017_03_01_11_21_41.csv`
39 |
40 | 2. index the csv with [post-tool](https://lucidworks.com/2015/08/04/solr-5-new-binpost-utility/)
41 |
42 | ```bash
43 | /opt/solr/solr-5.5.2/bin/post -c core0 /tmp/person_2017_03_01_11_21_41.csv
44 | ```
45 |
46 | ## PDO Select + [Solarium BufferedAdd](http://solarium.readthedocs.io/en/stable/plugins/#example-usage)
47 |
48 | The script has two parts:
49 |
50 | 1. select a chunk of rows from the DB
51 | 2. add the rows to the index with Solarium
52 |
53 | ```php
54 | setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
59 |
60 | $statement = $connection->prepare('SELECT COUNT(*) as total_items FROM person');
61 | $statement->execute();
62 | $countResult = $statement->fetch(PDO::FETCH_ASSOC);
63 |
64 | $totalItems = $countResult['total_items'];
65 | $batchSize = 5000;
66 |
67 | $pages = ceil($totalItems / $batchSize);
68 |
69 | $client = new Solarium\Client([
70 | 'endpoint' => [
71 | 'localhost' => [
72 | 'host' => 'localhost',
73 | 'port' => 8983,
74 | 'path' => '/solr/core0',
75 | ]
76 | ]
77 | ]);
78 |
79 | /** @var \Solarium\Plugin\BufferedAdd\BufferedAdd $buffer */
80 | $buffer = $client->getPlugin('bufferedadd');
81 | $buffer->setBufferSize($batchSize);
82 |
83 | for ($i = 0; $i <= $pages; $i++) {
84 | $limitStart = ($i - 1) * $batchSize;
85 | $limitEnd = $batchSize * $i;
86 | if ($i == 0) {
87 | $limitStart = 1;
88 | $limitEnd = $batchSize;
89 | }
90 |
91 | $statement = $connection->prepare(sprintf('SELECT id, name, email FROM person WHERE id >= %s AND id <= %s ', $limitStart, $limitEnd));
92 | $statement->execute();
93 |
94 | foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $item) {
95 | $buffer->createDocument([
96 | 'id' => $item['id'],
97 | 'name_s' => $item['name'],
98 | 'email_s' => $item['email']
99 | ]);
100 | }
101 |
102 | $statement->closeCursor();
103 |
104 | $buffer->commit();
105 |
106 | echo sprintf('Indexing page %s / %s', $i, $pages) . PHP_EOL;
107 | }
108 |
109 | $buffer->flush();
110 | ```
111 |
112 | # PDO Select + CSV Export + Solr Post-Tool
113 |
114 | This solution exports the database to csv by using PDO. The exported files are located under `/tmp/export`.
115 |
116 | ```php
117 | $connection = new PDO('mysql:host=localhost;dbname=dbname;charset=utf8mb4', 'dbuser', '123');
118 | $connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
119 |
120 | $statement = $connection->prepare('SELECT COUNT(*) as total_items FROM person');
121 | $statement->execute();
122 | $countResult = $statement->fetch(PDO::FETCH_ASSOC);
123 | $statement->closeCursor();
124 |
125 | $totalItems = $countResult['total_items'];
126 | $batchSize = 10000;
127 |
128 | $pages = ceil($totalItems / $batchSize);
129 |
130 | @mkdir('/tmp/export');
131 |
132 | for ($i = 0; $i <= $pages; $i++) {
133 | $data = [];
134 | $limitStart = ($i - 1) * $batchSize;
135 | $limitEnd = $batchSize * $i;
136 | if ($i == 0) {
137 | $limitStart = 1;
138 | $limitEnd = $batchSize;
139 | }
140 |
141 | $statement = $connection->prepare(sprintf('SELECT id, name, email FROM person WHERE id >= %s AND id <= %s ', $limitStart, $limitEnd));
142 | $statement->execute();
143 |
144 | $data[] = "id, name_s, email_s\n";
145 |
146 | foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $item) {
147 | $data[] = sprintf("\"%s\", \"%s\", \"%s\"", $item['id'], $item['name'], $item['email']);
148 | }
149 |
150 | $statement->closeCursor();
151 |
152 | file_put_contents(sprintf('/tmp/export/person_%s.csv', $i), join("\n", $data));
153 |
154 | echo sprintf('Indexing page %s / %s', $i, $pages) . PHP_EOL;
155 | }
156 | ```
157 |
158 | To import the data we are using Solr Post-Tool:
159 |
160 | `/opt/solr/solr-5.5.2/bin/post -c core0 /tmp/export`
161 |
--------------------------------------------------------------------------------