diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..a0332143
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Composer template
+composer.phar
+vendor/
+bin/
+
+# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
+# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+# composer.lock
+
+
diff --git a/.travis.yml b/.travis.yml
index c1c50560..f7e6ebd2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,21 +1,21 @@
language: php
-
-services: mongodb
+sudo: false
php:
- - 5.3
- - 5.4
- - 5.5
- - 5.6
+ - 7.0
+ - 7.1
+ - 7.2
+ - 7.3
+
before_script:
- - echo "extension=mongo.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`
- - curl -s http://getcomposer.org/installer | php
- - php composer.phar install --dev
- - curl -sSL https://raw.githubusercontent.com/moliware/travis-solr/master/travis-solr.sh | SOLR_VERSION=4.8.0 bash
+ - 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
+install:
+ - composer self-update
+ - if [[ ${TRAVIS_PHP_VERSION:0:2} == "5." ]]; then yes '' | pecl -q install -f mongo; fi
+ - if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then pecl install -f mongodb; fi
+ - if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then composer config "platform.ext-mongo" "1.6.16" && composer require alcaeus/mongo-php-adapter; fi
+ - composer update
script:
- - phpunit
-notifications:
- email:
- - fsemm.travis-ci@gmx.de
+ - ./bin/phpunit
diff --git a/Client/Builder.php b/Client/Builder.php
index afdd6eba..885546a2 100644
--- a/Client/Builder.php
+++ b/Client/Builder.php
@@ -1,6 +1,6 @@
logger = $logger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function initPluginType()
+ {
+ $dispatcher = $this->client->getEventDispatcher();
+ $dispatcher->addListener(Events::PRE_EXECUTE_REQUEST, [$this, 'preExecuteRequest']);
+ $dispatcher->addListener(Events::POST_EXECUTE_REQUEST, [$this, 'postExecuteRequest']);
+ }
+
+ /**
+ * @param PreExecuteRequest $event
+ */
+ public function preExecuteRequest(PreExecuteRequest $event)
+ {
+ $endpoint = $event->getEndpoint();
+ $request = $event->getRequest();
+ $uri = $request->getUri();
+
+ $path = sprintf('%s://%s:%s%s/%s', $endpoint->getScheme(), $endpoint->getHost(), $endpoint->getPort(), $endpoint->getPath(), urldecode($uri));
+
+ $requestInformation = [
+ 'uri' => $path,
+ 'method' => $request->getMethod(),
+ 'raw_data' => $request->getRawData()
+ ];
+
+ $this->logger->startRequest($requestInformation);
+ }
+
+ /**
+ * Issue stop logger
+ */
+ public function postExecuteRequest()
+ {
+ $this->logger->stopRequest();
+ }
+
+ /**
+ * @return Client
+ */
+ public function getClient()
+ {
+ return $this->client;
+ }
+}
\ No newline at end of file
diff --git a/Client/Solarium/Plugin/RequestDebugger.php b/Client/Solarium/Plugin/RequestDebugger.php
new file mode 100644
index 00000000..57fa14f3
--- /dev/null
+++ b/Client/Solarium/Plugin/RequestDebugger.php
@@ -0,0 +1,39 @@
+logger = $logger;
+
+ parent::__construct();
+ }
+
+ /**
+ * @param PreExecuteRequest $event
+ */
+ public function preExecuteRequest(PreExecuteRequest $event)
+ {
+ $this->logger->info(sprintf('run request: %s', urldecode($event->getRequest()->getUri())));
+ }
+
+}
\ No newline at end of file
diff --git a/Client/Solarium/SolariumClientBuilder.php b/Client/Solarium/SolariumClientBuilder.php
new file mode 100644
index 00000000..8c2d8ee8
--- /dev/null
+++ b/Client/Solarium/SolariumClientBuilder.php
@@ -0,0 +1,92 @@
+settings = $settings;
+ $this->eventDispatcher = $eventDispatcher;
+ }
+
+ /**
+ * @param string $pluginName
+ * @param AbstractPlugin $plugin
+ */
+ public function addPlugin($pluginName, AbstractPlugin $plugin)
+ {
+ $this->plugins[$pluginName] = $plugin;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return Client
+ */
+ public function build()
+ {
+ $settings = [];
+ foreach ($this->settings as $name => $options) {
+ if (isset($options['dsn'])) {
+ unset(
+ $options['scheme'],
+ $options['host'],
+ $options['port'],
+ $options['path']
+ );
+
+ $parsedDsn = parse_url($options['dsn']);
+ unset($options['dsn']);
+ if ($parsedDsn) {
+ $options['scheme'] = isset($parsedDsn['scheme']) ? $parsedDsn['scheme'] : 'http';
+ if (isset($parsedDsn['host'])) {
+ $options['host'] = $parsedDsn['host'];
+ }
+ if (isset($parsedDsn['user'])) {
+ $auth = $parsedDsn['user'] . (isset($parsedDsn['pass']) ? ':' . $parsedDsn['pass'] : '');
+ $options['host'] = $auth . '@' . $options['host'];
+ }
+ $options['port'] = isset($parsedDsn['port']) ? $parsedDsn['port'] : 80;
+ $options['path'] = isset($parsedDsn['path']) ? $parsedDsn['path'] : '';
+ }
+ }
+
+ $settings[$name] = $options;
+ }
+
+ $solariumClient = new Client(array('endpoint' => $settings), $this->eventDispatcher);
+ foreach ($this->plugins as $pluginName => $plugin) {
+ $solariumClient->registerPlugin($pluginName, $plugin);
+ }
+
+ return $solariumClient;
+ }
+}
diff --git a/Client/Client.php b/Client/Solarium/SolariumMulticoreClient.php
similarity index 75%
rename from Client/Client.php
rename to Client/Solarium/SolariumMulticoreClient.php
index 989a3e1c..71460d9d 100644
--- a/Client/Client.php
+++ b/Client/Solarium/SolariumMulticoreClient.php
@@ -1,26 +1,28 @@
solariumClient = $solariumClient;
}
@@ -44,8 +46,12 @@ public function update(DocumentInterface $doc, $index)
*/
public function delete(DocumentInterface $document, $index)
{
- $deleteQuery = new FindByIdentifierQuery();
+ $documentFields = $document->getFields();
+ $documentKey = $documentFields[MetaInformationInterface::DOCUMENT_KEY_FIELD_NAME];
+
+ $deleteQuery = new DeleteDocumentQuery();
$deleteQuery->setDocument($document);
+ $deleteQuery->setDocumentKey($documentKey);
$delete = $this->solariumClient->createUpdate();
$delete->addDeleteQuery($deleteQuery->getQuery());
@@ -54,6 +60,9 @@ public function delete(DocumentInterface $document, $index)
$this->applyQuery($delete, $index);
}
+ /**
+ * Runs a *:* delete query on all cores
+ */
public function clearCores()
{
$delete = $this->solariumClient->createUpdate();
diff --git a/Client/SolrBuilder.php b/Client/SolrBuilder.php
deleted file mode 100644
index 06656e94..00000000
--- a/Client/SolrBuilder.php
+++ /dev/null
@@ -1,32 +0,0 @@
-settings = $settings;
- }
-
- /**
- * @return Client
- */
- public function build()
- {
- return new Client(array('endpoint' => $this->settings));
- }
-}
\ No newline at end of file
diff --git a/Command/ClearIndexCommand.php b/Command/ClearIndexCommand.php
index 3f126f43..61e8114d 100644
--- a/Command/ClearIndexCommand.php
+++ b/Command/ClearIndexCommand.php
@@ -1,15 +1,22 @@
setDescription('Clear the whole index');
}
+ /**
+ * {@inheritdoc}
+ */
protected function execute(InputInterface $input, OutputInterface $output)
{
$solr = $this->getContainer()->get('solr.client');
try {
$solr->clearIndex();
- } catch (\Exception $e) {
- }
-
- $results = $this->getContainer()->get('solr.console.command.results');
- if ($results->hasErrors()) {
- $output->writeln('Clear index finished with errors!');
- } else {
- $output->writeln('Index successful cleared successful');
+ } catch (SolrException $e) {
+ $output->writeln(sprintf('A error occurs: %s', $e->getMessage()));
}
- if ($results->hasErrors()) {
- $output->writeln('');
- $error = array_shift($results->getErrors());
-
- $output->writeln(sprintf('Error: %s', $error->getMessage()));
- }
+ $output->writeln('Index successful cleared.');
}
}
diff --git a/Command/ShowSchemaCommand.php b/Command/ShowSchemaCommand.php
new file mode 100644
index 00000000..2516c28a
--- /dev/null
+++ b/Command/ShowSchemaCommand.php
@@ -0,0 +1,141 @@
+setName('solr:schema:show')
+ ->setDescription('Show configured entities and their fields');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $namespaces = $this->getContainer()->get('solr.doctrine.classnameresolver.known_entity_namespaces');
+ $metaInformationFactory = $this->getContainer()->get('solr.meta.information.factory');
+
+ foreach ($namespaces->getEntityClassnames() as $classname) {
+ try {
+ $metaInformation = $metaInformationFactory->loadInformation($classname);
+
+ if ($metaInformation->isNested()) {
+ continue;
+ }
+ } catch (SolrMappingException $e) {
+ continue;
+ }
+
+ $nested = '';
+ if ($metaInformation->isNested()) {
+ $nested = '(nested)';
+ }
+ $output->writeln(sprintf('%s %s', $classname, $nested));
+ $output->writeln(sprintf('Documentname: %s', $metaInformation->getDocumentName()));
+ $output->writeln(sprintf('Document Boost: %s', $metaInformation->getBoost()?$metaInformation->getBoost(): '-'));
+
+ $simpleFields = $this->getSimpleFields($metaInformation);
+
+ $rows = [];
+ foreach ($simpleFields as $documentField => $property) {
+ if ($field = $metaInformation->getField($documentField)) {
+ $rows[] = [$property, $documentField, $field->boost];
+ }
+ }
+ $this->renderTable($output, $rows);
+
+ $nestedFields = $this->getNestedFields($metaInformation);
+ if (count($nestedFields) == 0) {
+ return;
+ }
+
+ $output->writeln(sprintf('Fields (%s) with nested documents', count($nestedFields)));
+
+ foreach ($nestedFields as $idField) {
+ $propertyName = substr($idField, 0, strpos($idField, '.'));
+
+ if ($nestedField = $metaInformation->getField($propertyName)) {
+ $output->writeln(sprintf('Field %s contains nested class %s', $propertyName, $nestedField->nestedClass));
+
+ $nestedDocument = $metaInformationFactory->loadInformation($nestedField->nestedClass);
+ $rows = [];
+ foreach ($nestedDocument->getFieldMapping() as $documentField => $property) {
+ $field = $nestedDocument->getField($documentField);
+
+ if ($field === null) {
+ continue;
+ }
+
+ $rows[] = [$property, $documentField, $field->boost];
+ }
+
+ $this->renderTable($output, $rows);
+ }
+ }
+
+ }
+
+ }
+
+ /**
+ * @param OutputInterface $output
+ * @param array $rows
+ */
+ private function renderTable(OutputInterface $output, array $rows)
+ {
+ $table = new Table($output);
+ $table->setHeaders(array('Property', 'Document Fieldname', 'Boost'));
+ $table->setRows($rows);
+
+ $table->render();
+ }
+
+ /**
+ * @param MetaInformationInterface $metaInformation
+ *
+ * @return array
+ */
+ private function getSimpleFields(MetaInformationInterface $metaInformation)
+ {
+ $simpleFields = array_filter($metaInformation->getFieldMapping(), function ($field) {
+ if (strpos($field, '.') === false) {
+ return true;
+ }
+
+ return false;
+ });
+
+ return $simpleFields;
+ }
+
+ /**
+ * @param MetaInformationInterface $metaInformation
+ *
+ * @return array
+ */
+ protected function getNestedFields(MetaInformationInterface $metaInformation)
+ {
+ $complexFields = array_filter($metaInformation->getFieldMapping(), function ($field) {
+ if (strpos($field, '.id') !== false) {
+ return true;
+ }
+
+ return false;
+ });
+
+ return $complexFields;
+ }
+}
\ No newline at end of file
diff --git a/Command/SynchronizeIndexCommand.php b/Command/SynchronizeIndexCommand.php
index a41b7374..0dfdbec4 100644
--- a/Command/SynchronizeIndexCommand.php
+++ b/Command/SynchronizeIndexCommand.php
@@ -1,99 +1,206 @@
setName('solr:synchronize')
- ->addArgument('entity', InputArgument::REQUIRED, 'The entity you want to index')
- ->addOption(
- 'source',
- null,
- InputArgument::OPTIONAL,
- 'specify a source from where to load entities [relational, mongodb]',
- 'relational'
- )
+ $this->setName('solr:index:populate')
+ ->addArgument('entity', InputArgument::OPTIONAL, 'The entity you want to index', null)
+ ->addOption('flushsize', null, InputOption::VALUE_OPTIONAL, 'Number of items to handle before flushing data', 500)
+ ->addOption('source', null, InputArgument::OPTIONAL, 'specify a source from where to load entities [relational, mongodb]', null)
+ ->addOption('start-offset', null, InputOption::VALUE_OPTIONAL, 'Start with row', 0)
->setDescription('Index all entities');
}
+ /**
+ * {@inheritdoc}
+ */
protected function execute(InputInterface $input, OutputInterface $output)
{
- $entity = $input->getArgument('entity');
+ $entities = $this->getIndexableEntities($input->getArgument('entity'));
$source = $input->getOption('source');
+ if ($source !== null) {
+ $output->writeln('The source option is deprecated and will be removed in version 2.0');
+ }
- $objectManager = $this->getObjectManager($source);
+ $startOffset = $input->getOption('start-offset');
+ $batchSize = $input->getOption('flushsize');
+ $solr = $this->getContainer()->get('solr.client');
- try {
- $repository = $objectManager->getRepository($entity);
- } catch (\Exception $e) {
- $output->writeln(sprintf('No repository found for "%s", check your input', $entity));
+ if ($startOffset > 0 && count($entities) > 1) {
+ $output->writeln('Wrong usage. Please use start-offset option together with the entity argument.');
return;
}
- $entities = $repository->findAll();
+ foreach ($entities as $entityClassname) {
+ $objectManager = $this->getObjectManager($entityClassname);
- if (count($entities) == 0) {
- $output->writeln('No entities found for indexing');
+ $output->writeln(sprintf('Indexing: %s', $entityClassname));
- return;
+ try {
+ $repository = $objectManager->getRepository($entityClassname);
+ } catch (\Exception $e) {
+ $output->writeln(sprintf('No repository found for "%s", check your input', $entityClassname));
+
+ continue;
+ }
+
+ $totalSize = $this->getTotalNumberOfEntities($entityClassname, $startOffset);
+
+ if ($totalSize >= 500000) {
+ $helper = $this->getHelper('question');
+ $question = new ConfirmationQuestion('Indexing more than 500000 entities does not perform well and can exhaust the whole memory. Execute anyway?', false);
+
+ if (!$helper->ask($input, $output, $question)) {
+ $output->writeln('');
+
+ continue;
+ }
+ }
+
+ if ($totalSize === 0) {
+ $output->writeln('No entities found for indexing');
+
+ continue;
+ }
+
+ $output->writeln(sprintf('Synchronize %s entities', $totalSize));
+
+ $batchLoops = ceil($totalSize / $batchSize);
+
+ for ($i = 0; $i <= $batchLoops; $i++) {
+ $offset = $i * $batchSize;
+ if ($startOffset && $i == 0) {
+ $offset = $startOffset;
+ $i++;
+ }
+
+ $entities = $repository->findBy([], null, $batchSize, $offset);
+
+ try {
+ $solr->synchronizeIndex($entities);
+ } catch (\Exception $e) {
+ $output->writeln(sprintf('A error occurs: %s', $e->getMessage()));
+ }
+ }
+
+ $output->writeln('Synchronization finished');
+ $output->writeln('');
}
+ }
- $solr = $this->getContainer()->get('solr.client');
+ /**
+ * @param string $entityClassname
+ *
+ * @throws \RuntimeException if no doctrine instance is configured
+ *
+ * @return ObjectManager
+ */
+ private function getObjectManager($entityClassname)
+ {
+ $objectManager = $this->getContainer()->get('doctrine')->getManagerForClass($entityClassname);
+ if ($objectManager) {
+ return $objectManager;
+ }
- foreach ($entities as $entity) {
- try {
- $solr->synchronizeIndex($entity);
- } catch (\Exception $e) {}
+ $objectManager = $this->getContainer()->get('doctrine_mongodb')->getManagerForClass($entityClassname);
+ if ($objectManager) {
+ return $objectManager;
}
- $results = $this->getContainer()->get('solr.console.command.results');
- if ($results->hasErrors()) {
- $output->writeln('Synchronization finished with errors!');
- } else {
- $output->writeln('Synchronization successful');
+ throw new \RuntimeException(sprintf('Class "%s" is not a managed entity', $entityClassname));
+ }
+
+ /**
+ * Get a list of entities which are indexable by Solr
+ *
+ * @param null|string $entity
+ *
+ * @return array
+ */
+ private function getIndexableEntities($entity = null)
+ {
+ if ($entity) {
+ return [$entity];
}
- $output->writeln('');
- $output->writeln(sprintf('Synchronized Documents: %s', $results->getSucceed()));
- $output->writeln(sprintf('Not Synchronized Documents: %s', $results->getErrored()));
- $output->writeln('');
+ $entities = [];
+ $namespaces = $this->getContainer()->get('solr.doctrine.classnameresolver.known_entity_namespaces');
+ $metaInformationFactory = $this->getContainer()->get('solr.meta.information.factory');
- $output->writeln(sprintf('Overall: %s', $results->getOverall()));
- if ($results->hasErrors()) {
- $errorList = new ConsoleErrorListOutput($output, $this->getHelper('table'), $results->getErrors());
- $errorList->render();
+ foreach ($namespaces->getEntityClassnames() as $classname) {
+ try {
+ $metaInformation = $metaInformationFactory->loadInformation($classname);
+ if ($metaInformation->isNested()) {
+ continue;
+ }
+
+ array_push($entities, $metaInformation->getClassName());
+ } catch (SolrMappingException $e) {
+ continue;
+ }
}
+
+ return $entities;
}
/**
- * @param string $source
- * @throws \InvalidArgumentException if $source is unknown
- * @throws \RuntimeException if no doctrine instance is configured
- * @return AbstractManagerRegistry
+ * Get the total number of entities in a repository
+ *
+ * @param string $entity
+ * @param int $startOffset
+ *
+ * @return int
+ *
+ * @throws \Exception if no primary key was found for the given entity
*/
- private function getObjectManager($source)
+ private function getTotalNumberOfEntities($entity, $startOffset)
{
- $objectManager = null;
+ $objectManager = $this->getObjectManager($entity);
+ $repository = $objectManager->getRepository($entity);
- if ($source == 'relational') {
- $objectManager = $this->getContainer()->get('doctrine');
+ if ($repository instanceof DocumentRepository) {
+ $totalSize = $repository->createQueryBuilder()
+ ->getQuery()
+ ->count();
} else {
- if ($source == 'mongodb') {
- $objectManager = $this->getContainer()->get('doctrine_mongodb');
- } else {
- throw new \InvalidArgumentException(sprintf('Unknown source %s', $source));
+ $dataStoreMetadata = $objectManager->getClassMetadata($entity);
+
+ $identifierFieldNames = $dataStoreMetadata->getIdentifierFieldNames();
+
+ if (!count($identifierFieldNames)) {
+ throw new \Exception(sprintf('No primary key found for entity %s', $entity));
}
+
+ $countableColumn = reset($identifierFieldNames);
+
+ /** @var EntityRepository $repository */
+ $totalSize = $repository->createQueryBuilder('size')
+ ->select(sprintf('count(size.%s)', $countableColumn))
+ ->getQuery()
+ ->getSingleScalarResult();
}
- return $objectManager;
+ return $totalSize - $startOffset;
}
}
diff --git a/Console/CommandResult.php b/Console/CommandResult.php
deleted file mode 100644
index 0520ade9..00000000
--- a/Console/CommandResult.php
+++ /dev/null
@@ -1,58 +0,0 @@
-resultId = $resultId;
- $this->entity = $entity;
- $this->message = $message;
- }
-
- /**
- * @return int
- */
- public function getResultId()
- {
- return $this->resultId;
- }
-
- /**
- * @return string
- */
- public function getEntity()
- {
- return $this->entity;
- }
-
- /**
- * @return string
- */
- public function getMessage()
- {
- return $this->message;
- }
-}
\ No newline at end of file
diff --git a/Console/ConsoleCommandResults.php b/Console/ConsoleCommandResults.php
deleted file mode 100644
index 9d75f3a0..00000000
--- a/Console/ConsoleCommandResults.php
+++ /dev/null
@@ -1,92 +0,0 @@
-success[$result->getResultId()] = $result;
- }
-
- /**
- * @param CommandResult $result
- */
- public function error(CommandResult $result)
- {
- $this->errors[$result->getResultId()] = $result;
- }
-
- /**
- * @return CommandResult[]
- */
- public function getErrors()
- {
- return $this->errors;
- }
-
- /**
- * @return CommandResult[]
- */
- public function getSuccess()
- {
- return $this->success;
- }
-
- /**
- * @return bool
- */
- public function hasErrors()
- {
- return count($this->errors) > 0;
- }
-
- /**
- * @return int
- */
- public function getOverall()
- {
- return $this->getErrored() + $this->getSucceed();
- }
-
- /**
- * filtering of succeed result required:
- *
- * error-event will trigger after exception. the normal program-flow continues WITH post_update/insert events
- *
- * @return int
- */
- public function getSucceed()
- {
- foreach ($this->success as $resultId => $result) {
- if (isset($this->errors[$resultId])) {
- unset($this->success[$resultId]);
- }
- }
-
- return count($this->success);
- }
-
- /**
- * @return int
- */
- public function getErrored()
- {
- return count($this->errors);
- }
-}
\ No newline at end of file
diff --git a/Console/ConsoleErrorListOutput.php b/Console/ConsoleErrorListOutput.php
deleted file mode 100644
index 79d9c621..00000000
--- a/Console/ConsoleErrorListOutput.php
+++ /dev/null
@@ -1,52 +0,0 @@
-output = $output;
- $this->errors = $errors;
- $this->tableHelperSet = $tableHelperSet;
- }
-
- public function render()
- {
- $this->output->writeln('');
- $this->output->writeln('Errors:');
- $rows = array();
- foreach ($this->errors as $error) {
- $rows[] = array($error->getEntity(), $error->getResultId(), $error->getMessage());
- }
-
- $this->tableHelperSet->setHeaders(array('Entity', 'ID', 'Error'))
- ->setRows($rows);
-
- $this->tableHelperSet->render($this->output);
- }
-}
\ No newline at end of file
diff --git a/Console/ConsoleResultFactory.php b/Console/ConsoleResultFactory.php
deleted file mode 100644
index ec16955f..00000000
--- a/Console/ConsoleResultFactory.php
+++ /dev/null
@@ -1,64 +0,0 @@
-getResultId($event),
- $this->getClassname($event),
- $this->getMessage($event)
- );
-
- }
-
- /**
- * @param Event $event
- * @return null|number
- */
- private function getResultId(Event $event)
- {
- if ($event->getMetaInformation() == null) {
- return null;
- }
-
- return $event->getMetaInformation()->getEntityId();
- }
-
- /**
- * @param Event $event
- * @return string
- */
- private function getClassname(Event $event)
- {
- if ($event->getMetaInformation() == null) {
- return '';
- }
-
- return $event->getMetaInformation()->getClassName();
- }
-
- /**
- * @param Event $event
- * @return string
- */
- private function getMessage(Event $event)
- {
- if (!$event instanceof ErrorEvent) {
- return '';
- }
-
- return $event->getExceptionMessage();
- }
-}
\ No newline at end of file
diff --git a/DataCollector/RequestCollector.php b/DataCollector/RequestCollector.php
new file mode 100644
index 00000000..b5649ae9
--- /dev/null
+++ b/DataCollector/RequestCollector.php
@@ -0,0 +1,117 @@
+logger = $logger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function collect(Request $request, Response $response, \Exception $exception = null)
+ {
+ $this->data = [
+ 'queries' => array_map(function ($query) {
+ return $this->parseQuery($query);
+ }, $this->logger->getQueries())
+ ];
+ }
+
+ /**
+ * @return int
+ */
+ public function getQueryCount()
+ {
+ return count($this->data['queries']);
+ }
+
+ /**
+ * @return array
+ */
+ public function getQueries()
+ {
+ return $this->data['queries'];
+ }
+
+ /**
+ * @return int
+ */
+ public function getTime()
+ {
+ $time = 0;
+ foreach ($this->data['queries'] as $query) {
+ $time += $query['executionMS'];
+ }
+
+ return $time;
+ }
+
+ /**
+ * @param array $request
+ *
+ * @return array
+ */
+ public function parseQuery($request)
+ {
+ list($endpoint, $params) = explode('?', $request['request']['uri']);
+
+ $request['endpoint'] = $endpoint;
+ $request['params'] = $params;
+ $request['method'] = $request['request']['method'];
+ $request['raw_data'] = $request['request']['raw_data'];
+
+ if (class_exists(VarCloner::class)) {
+ $varCloner = new VarCloner();
+
+ parse_str($params, $stub);
+ $request['stub'] = Kernel::VERSION_ID >= 30200 ? $varCloner->cloneVar($stub) : $stub;
+ }
+
+ return $request;
+ }
+
+ /**
+ * @param DebugLogger $logger
+ */
+ public function setLogger(DebugLogger $logger)
+ {
+ $this->logger = $logger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return 'solr';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function reset()
+ {
+ $this->data = [
+ 'queries' => [],
+ ];
+ }
+}
diff --git a/DependencyInjection/Compiler/AddCreateDocumentCommandPass.php b/DependencyInjection/Compiler/AddCreateDocumentCommandPass.php
deleted file mode 100644
index 89ce7e94..00000000
--- a/DependencyInjection/Compiler/AddCreateDocumentCommandPass.php
+++ /dev/null
@@ -1,29 +0,0 @@
-findTaggedServiceIds('solr.document.command');
-
- $factory = $container->getDefinition('solr.mapping.factory');
-
- foreach ($definitions as $service => $definition) {
- $factory->addMethodCall(
- 'add',
- array(
- new Reference($service),
- $definition[0]['command']
- )
- );
- }
- }
-}
diff --git a/DependencyInjection/Compiler/AddSolariumPluginsPass.php b/DependencyInjection/Compiler/AddSolariumPluginsPass.php
new file mode 100644
index 00000000..c605c830
--- /dev/null
+++ b/DependencyInjection/Compiler/AddSolariumPluginsPass.php
@@ -0,0 +1,32 @@
+findTaggedServiceIds('solarium.client.plugin');
+
+ $clientBuilder = $container->getDefinition('solr.client.adapter.builder');
+ foreach ($plugins as $service => $definition) {
+ $clientBuilder->addMethodCall(
+ 'addPlugin',
+ array(
+ $definition[0]['plugin-name'],
+ new Reference($service)
+ )
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index dbf8954e..955b051a 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -9,7 +9,7 @@ class Configuration implements ConfigurationInterface
{
/**
- * @return TreeBuilder
+ * {@inheritdoc}
*/
public function getConfigTreeBuilder()
{
@@ -20,6 +20,8 @@ public function getConfigTreeBuilder()
->useAttributeAsKey('name')
->prototype('array')
->children()
+ ->scalarNode('dsn')->end()
+ ->scalarNode('scheme')->end()
->scalarNode('host')->end()
->scalarNode('port')->end()
->scalarNode('path')->end()
@@ -29,16 +31,6 @@ public function getConfigTreeBuilder()
->end()
->end()
->end()
-// ->arrayNode('clients')
-// ->useAttributeAsKey('name')
-// ->prototype('array')
-// ->children()
-// ->arrayNode('endpoints')
-// ->prototype('scalar')->end()
-// ->end()
-// ->end()
-// ->end()
-// ->end()
->booleanNode('auto_index')->defaultValue(true)->end()
->end();
diff --git a/DependencyInjection/FSSolrExtension.php b/DependencyInjection/FSSolrExtension.php
index aa2e86cb..1a3f2745 100644
--- a/DependencyInjection/FSSolrExtension.php
+++ b/DependencyInjection/FSSolrExtension.php
@@ -2,8 +2,6 @@
namespace FS\SolrBundle\DependencyInjection;
-use Symfony\Component\DependencyInjection\DefinitionDecorator;
-use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
@@ -14,8 +12,7 @@ class FSSolrExtension extends Extension
{
/**
- * @param array $configs
- * @param ContainerBuilder $container
+ * {@inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
@@ -29,7 +26,9 @@ public function load(array $configs, ContainerBuilder $container)
$this->setupClients($config, $container);
- $container->setParameter('solr.auto_index', $config['auto_index']);
+ if (!$container->hasParameter('solr.auto_index')) {
+ $container->setParameter('solr.auto_index', $config['auto_index']);
+ }
$this->setupDoctrineListener($config, $container);
$this->setupDoctrineConfiguration($config, $container);
@@ -37,7 +36,7 @@ public function load(array $configs, ContainerBuilder $container)
}
/**
- * @param array $config
+ * @param array $config
* @param ContainerBuilder $container
*/
private function setupClients(array $config, ContainerBuilder $container)
@@ -46,11 +45,12 @@ private function setupClients(array $config, ContainerBuilder $container)
$builderDefinition = $container->getDefinition('solr.client.adapter.builder');
$builderDefinition->replaceArgument(0, $endpoints);
+ $builderDefinition->addMethodCall('addPlugin', array('request_debugger', new Reference('solr.debug.client_debugger')));
}
/**
*
- * @param array $config
+ * @param array $config
* @param ContainerBuilder $container
*/
private function setupDoctrineConfiguration(array $config, ContainerBuilder $container)
@@ -59,7 +59,7 @@ private function setupDoctrineConfiguration(array $config, ContainerBuilder $con
$entityManagers = $container->getParameter('doctrine.entity_managers');
$entityManagersNames = array_keys($entityManagers);
- foreach($entityManagersNames as $entityManager) {
+ foreach ($entityManagersNames as $entityManager) {
$container->getDefinition('solr.doctrine.classnameresolver.known_entity_namespaces')->addMethodCall(
'addEntityNamespaces',
array(new Reference(sprintf('doctrine.orm.%s_configuration', $entityManager)))
@@ -71,7 +71,7 @@ private function setupDoctrineConfiguration(array $config, ContainerBuilder $con
$documentManagers = $container->getParameter('doctrine_mongodb.odm.document_managers');
$documentManagersNames = array_keys($documentManagers);
- foreach($documentManagersNames as $documentManager) {
+ foreach ($documentManagersNames as $documentManager) {
$container->getDefinition('solr.doctrine.classnameresolver.known_entity_namespaces')->addMethodCall(
'addDocumentNamespaces',
array(new Reference(sprintf('doctrine_mongodb.odm.%s_configuration', $documentManager)))
@@ -90,7 +90,7 @@ private function setupDoctrineConfiguration(array $config, ContainerBuilder $con
*
* listener-methods expecting different types of events
*
- * @param array $config
+ * @param array $config
* @param ContainerBuilder $container
*/
private function setupDoctrineListener(array $config, ContainerBuilder $container)
@@ -102,39 +102,17 @@ private function setupDoctrineListener(array $config, ContainerBuilder $containe
}
if ($this->isODMConfigured($container)) {
- $container->getDefinition('solr.delete.document.odm.listener')->addTag(
- 'doctrine_mongodb.odm.event_listener',
- array('event' => 'preRemove')
- );
- $container->getDefinition('solr.update.document.odm.listener')->addTag(
- 'doctrine_mongodb.odm.event_listener',
- array('event' => 'postUpdate')
- );
- $container->getDefinition('solr.add.document.odm.listener')->addTag(
- 'doctrine_mongodb.odm.event_listener',
- array('event' => 'postPersist')
- );
-
+ $container->getDefinition('solr.document.odm.subscriber')->addTag('doctrine_mongodb.odm.event_subscriber');
}
if ($this->isOrmConfigured($container)) {
- $container->getDefinition('solr.add.document.orm.listener')->addTag(
- 'doctrine.event_listener',
- array('event' => 'postPersist')
- );
- $container->getDefinition('solr.delete.document.orm.listener')->addTag(
- 'doctrine.event_listener',
- array('event' => 'preRemove')
- );
- $container->getDefinition('solr.update.document.orm.listener')->addTag(
- 'doctrine.event_listener',
- array('event' => 'postUpdate')
- );
+ $container->getDefinition('solr.document.orm.subscriber')->addTag('doctrine.event_subscriber');
}
}
/**
* @param ContainerBuilder $container
+ *
* @return boolean
*/
private function isODMConfigured(ContainerBuilder $container)
@@ -144,6 +122,7 @@ private function isODMConfigured(ContainerBuilder $container)
/**
* @param ContainerBuilder $container
+ *
* @return boolean
*/
private function isOrmConfigured(ContainerBuilder $container)
diff --git a/Doctrine/AbstractIndexingListener.php b/Doctrine/AbstractIndexingListener.php
new file mode 100644
index 00000000..1db9e405
--- /dev/null
+++ b/Doctrine/AbstractIndexingListener.php
@@ -0,0 +1,93 @@
+solr = $solr;
+ $this->metaInformationFactory = $metaInformationFactory;
+ $this->logger = $logger;
+ }
+
+ /**
+ * @param array $doctrineChangeSet
+ * @param object $entity
+ *
+ * @return bool
+ */
+ protected function hasChanged($doctrineChangeSet, $entity)
+ {
+ if (empty($doctrineChangeSet)) {
+ return false;
+ }
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $documentChangeSet = array();
+
+ /* Check all Solr fields on this entity and check if this field is in the change set */
+ foreach ($metaInformation->getFields() as $field) {
+ if (array_key_exists($field->name, $doctrineChangeSet)) {
+ $documentChangeSet[] = $field->name;
+ }
+ }
+
+ return count($documentChangeSet) > 0;
+ }
+
+ /**
+ * @param object $entity
+ *
+ * @return bool
+ */
+ protected function isNested($entity)
+ {
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ return $metaInformation->isNested();
+ }
+
+ /**
+ * @param object $entity
+ *
+ * @return bool
+ */
+ protected function isAbleToIndex($entity)
+ {
+ try {
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+ } catch (SolrMappingException $e) {
+ return false;
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/Doctrine/Annotation/AnnotationReader.php b/Doctrine/Annotation/AnnotationReader.php
index e56d90ac..d8c8ed64 100644
--- a/Doctrine/Annotation/AnnotationReader.php
+++ b/Doctrine/Annotation/AnnotationReader.php
@@ -1,41 +1,50 @@
reader = new Reader();
+ $this->reader = $reader;
}
/**
* reads the entity and returns a set of annotations
*
- * @param string $entity
+ * @param object $entity
* @param string $type
*
- * @return array
+ * @return Annotation[]
*/
private function getPropertiesByType($entity, $type)
{
- $reflectionClass = new \ReflectionClass($entity);
- $properties = array_merge($reflectionClass->getProperties(), $this->getParentProperties($reflectionClass));
+ $properties = $this->readClassProperties($entity);
- $fields = array();
+ $fields = [];
foreach ($properties as $property) {
$annotation = $this->reader->getPropertyAnnotation($property, $type);
@@ -61,11 +70,11 @@ private function getPropertiesByType($entity, $type)
private function getParentProperties(\ReflectionClass $reflectionClass)
{
$parent = $reflectionClass->getParentClass();
- if ($parent == null) {
- return array();
+ if ($parent != null) {
+ return array_merge($reflectionClass->getProperties(), $this->getParentProperties($parent));
}
- return $parent->getProperties();
+ return $reflectionClass->getProperties();
}
/**
@@ -78,40 +87,75 @@ public function getFields($entity)
return $this->getPropertiesByType($entity, self::FIELD_CLASS);
}
+ /**
+ * @param object $entity
+ *
+ * @return array
+ *
+ * @throws \ReflectionException
+ */
+ public function getMethods($entity)
+ {
+ $reflectionClass = new \ReflectionClass($entity);
+
+ $methods = [];
+ foreach ($reflectionClass->getMethods() as $method) {
+ /** @var Field $annotation */
+ $annotation = $this->reader->getMethodAnnotation($method, self::FIELD_CLASS);
+
+ if ($annotation === null) {
+ continue;
+ }
+
+ $annotation->value = $method->invoke($entity);
+
+ if ($annotation->name == '') {
+ throw new SolrMappingException(sprintf('Please configure a field-name for method "%s" with field-annotation in class "%s"', $method->getName(), get_class($entity)));
+ }
+
+ $methods[] = $annotation;
+ }
+
+ return $methods;
+ }
+
/**
* @param object $entity
*
* @return number
*
- * @throws \InvalidArgumentException if the boost value is not numeric
+ * @throws AnnotationReaderException if the boost value is not numeric
*/
public function getEntityBoost($entity)
{
- $annotation = $this->getClassAnnotation($entity, self::DOCUMENT_INDEX_CLASS);
+ $annotation = $this->getClassAnnotation($entity, self::DOCUMENT_CLASS);
if (!$annotation instanceof Document) {
return 0;
}
- try {
- $boostValue = $annotation->getBoost();
- } catch (\InvalidArgumentException $e) {
- throw new \InvalidArgumentException(sprintf($e->getMessage() . ' for entity %s', get_class($entity)));
+ $boostValue = $annotation->getBoost();
+ if (!is_numeric($boostValue)) {
+ throw new AnnotationReaderException(sprintf('Invalid boost value "%s" in class "%s" configured', $boostValue, get_class($entity)));
+ }
+
+ if ($boostValue === 0) {
+ return null;
}
return $boostValue;
}
/**
- * @param $entity
+ * @param object $entity
*
* @return string
*/
public function getDocumentIndex($entity)
{
- $annotation = $this->getClassAnnotation($entity, self::DOCUMENT_INDEX_CLASS);
+ $annotation = $this->getClassAnnotation($entity, self::DOCUMENT_CLASS);
if (!$annotation instanceof Document) {
- return '';
+ return null;
}
$indexHandler = $annotation->indexHandler;
@@ -125,16 +169,16 @@ public function getDocumentIndex($entity)
/**
* @param object $entity
*
- * @return Type
+ * @return Id
*
- * @throws \RuntimeException
+ * @throws AnnotationReaderException if given $entity has no identifier
*/
public function getIdentifier($entity)
{
$id = $this->getPropertiesByType($entity, self::FIELD_IDENTIFIER_CLASS);
if (count($id) == 0) {
- throw new \RuntimeException('no identifer declared in entity ' . get_class($entity));
+ throw new AnnotationReaderException('no identifer declared in entity ' . get_class($entity));
}
return reset($id);
@@ -167,11 +211,9 @@ public function getFieldMapping($entity)
{
$fields = $this->getPropertiesByType($entity, self::FIELD_CLASS);
- $mapping = array();
+ $mapping = [];
foreach ($fields as $field) {
- if ($field instanceof Field) {
- $mapping[$field->getNameWithAlias()] = $field->name;
- }
+ $mapping[$field->getNameWithAlias()] = $field->name;
}
$id = $this->getIdentifier($entity);
@@ -187,9 +229,15 @@ public function getFieldMapping($entity)
*/
public function hasDocumentDeclaration($entity)
{
- $annotation = $this->getClassAnnotation($entity, self::DOCUMENT_INDEX_CLASS);
+ if ($rootDocument = $this->getClassAnnotation($entity, self::DOCUMENT_CLASS)) {
+ return true;
+ }
- return $annotation !== null;
+ if ($this->isNested($entity)) {
+ return true;
+ }
+
+ return false;
}
/**
@@ -208,11 +256,57 @@ public function getSynchronizationCallback($entity)
return $annotation->callback;
}
+ /**
+ * @param object $entity
+ *
+ * @return bool
+ */
+ public function isOrm($entity)
+ {
+ $annotation = $this->getClassAnnotation($entity, 'Doctrine\ORM\Mapping\Entity');
+
+ if ($annotation === null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param object $entity
+ *
+ * @return bool
+ */
+ public function isOdm($entity)
+ {
+ $annotation = $this->getClassAnnotation($entity, 'Doctrine\ODM\MongoDB\Mapping\Annotations\Document');
+
+ if ($annotation === null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * @param object $entity
+ *
+ * @return bool
+ */
+ public function isNested($entity)
+ {
+ if ($nestedDocument = $this->getClassAnnotation($entity, self::DOCUMENT_NESTED_CLASS)) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
* @param string $entity
* @param string $annotationName
*
- * @return string
+ * @return Annotation|null
*/
private function getClassAnnotation($entity, $annotationName)
{
@@ -226,4 +320,29 @@ private function getClassAnnotation($entity, $annotationName)
return $annotation;
}
+
+ /**
+ * @param object $entity
+ *
+ * @return \ReflectionProperty[]
+ */
+ private function readClassProperties($entity)
+ {
+ $className = get_class($entity);
+ if (isset($this->entityProperties[$className])) {
+ return $this->entityProperties[$className];
+ }
+
+ $reflectionClass = new \ReflectionClass($entity);
+ $inheritedProperties = array_merge($this->getParentProperties($reflectionClass), $reflectionClass->getProperties());
+
+ $properties = [];
+ foreach ($inheritedProperties as $property) {
+ $properties[$property->getName()] = $property;
+ }
+
+ $this->entityProperties[$className] = $properties;
+
+ return $properties;
+ }
}
diff --git a/Doctrine/Annotation/AnnotationReaderException.php b/Doctrine/Annotation/AnnotationReaderException.php
new file mode 100644
index 00000000..d48a4f2f
--- /dev/null
+++ b/Doctrine/Annotation/AnnotationReaderException.php
@@ -0,0 +1,8 @@
+boost)) {
- throw new \InvalidArgumentException(sprintf('Invalid boost value %s', $this->boost));
- }
-
- $float = floatval($this->boost);
- return $float ?: null;
+ return $this->boost;
}
/**
diff --git a/Doctrine/Annotation/Field.php b/Doctrine/Annotation/Field.php
index 04baf7af..3f87f08b 100644
--- a/Doctrine/Annotation/Field.php
+++ b/Doctrine/Annotation/Field.php
@@ -2,8 +2,11 @@
namespace FS\SolrBundle\Doctrine\Annotation;
use Doctrine\Common\Annotations\Annotation;
+use phpDocumentor\Reflection\DocBlock\Type\Collection;
/**
+ * Defines a field of a solr-document
+ *
* @Annotation
*/
class Field extends Annotation
@@ -20,14 +23,34 @@ class Field extends Annotation
public $name;
/**
- * @var numeric
+ * @var float
*/
public $boost = 0;
+ /**
+ * @var string
+ */
+ public $getter;
+
+ /**
+ * @var string
+ */
+ public $fieldModifier;
+
+ /**
+ * @var string
+ */
+ public $nestedClass;
+
+ /**
+ * @var array
+ */
+ private static $TYP_MAPPING = array();
+
/**
* @var array
*/
- private static $TYP_MAPPING = array(
+ private static $TYP_SIMPLE_MAPPING = array(
'string' => '_s',
'text' => '_t',
'date' => '_dt',
@@ -36,6 +59,22 @@ class Field extends Annotation
'long' => '_l',
'float' => '_f',
'double' => '_d',
+ 'datetime' => '_dt',
+ 'point' => '_p'
+ );
+
+ /**
+ * @var array
+ */
+ private static $TYP_COMPLEX_MAPPING = array(
+ 'doubles' => '_ds',
+ 'floats' => '_fs',
+ 'longs' => '_ls',
+ 'integers' => '_is',
+ 'booleans' => '_bs',
+ 'dates' => '_dts',
+ 'texts' => '_txt',
+ 'strings' => '_ss',
);
/**
@@ -44,6 +83,7 @@ class Field extends Annotation
* eg: title_s
*
* @throws \RuntimeException
+ *
* @return string
*/
public function getNameWithAlias()
@@ -53,10 +93,13 @@ public function getNameWithAlias()
/**
* @param string $type
+ *
* @return string
*/
private function getTypeSuffix($type)
{
+ self::$TYP_MAPPING = array_merge(self::$TYP_COMPLEX_MAPPING, self::$TYP_SIMPLE_MAPPING);
+
if ($type == '') {
return '';
}
@@ -68,6 +111,24 @@ private function getTypeSuffix($type)
return self::$TYP_MAPPING[$this->type];
}
+ /**
+ * Related object getter name
+ *
+ * @return string
+ */
+ public function getGetterName()
+ {
+ return $this->getter;
+ }
+
+ /**
+ * @return string
+ */
+ public function getFieldModifier()
+ {
+ return $this->fieldModifier;
+ }
+
/**
* @return string
*/
@@ -86,6 +147,7 @@ public function __toString()
/**
* @throws \InvalidArgumentException if boost is not a number
+ *
* @return number
*/
public function getBoost()
@@ -123,4 +185,12 @@ function ($value) {
return implode('_', $words);
}
+
+ /**
+ * @return array
+ */
+ public static function getComplexFieldMapping()
+ {
+ return self::$TYP_COMPLEX_MAPPING;
+ }
}
diff --git a/Doctrine/Annotation/Id.php b/Doctrine/Annotation/Id.php
index bb4dafcd..1d8b207f 100644
--- a/Doctrine/Annotation/Id.php
+++ b/Doctrine/Annotation/Id.php
@@ -1,4 +1,5 @@
knownNamespaceAlias = array_merge($this->knownNamespaceAlias, $configuration->getDocumentNamespaces());
+
+ if ($configuration->getMetadataDriverImpl()) {
+ $this->entityClassnames = array_merge($this->entityClassnames, $configuration->getMetadataDriverImpl()->getAllClassNames());
+ }
}
/**
@@ -26,6 +38,10 @@ public function addDocumentNamespaces(OdmConfiguration $configuration)
public function addEntityNamespaces(OrmConfiguration $configuration)
{
$this->knownNamespaceAlias = array_merge($this->knownNamespaceAlias, $configuration->getEntityNamespaces());
+
+ if ($configuration->getMetadataDriverImpl()) {
+ $this->entityClassnames = array_merge($this->entityClassnames, $configuration->getMetadataDriverImpl()->getAllClassNames());
+ }
}
/**
@@ -59,4 +75,12 @@ public function getAllNamespaceAliases()
{
return array_keys($this->knownNamespaceAlias);
}
-}
\ No newline at end of file
+
+ /**
+ * @return array
+ */
+ public function getEntityClassnames()
+ {
+ return $this->entityClassnames;
+ }
+}
\ No newline at end of file
diff --git a/Doctrine/Hydration/DoctrineHydrator.php b/Doctrine/Hydration/DoctrineHydrator.php
index aa195723..88c0f4c3 100644
--- a/Doctrine/Hydration/DoctrineHydrator.php
+++ b/Doctrine/Hydration/DoctrineHydrator.php
@@ -2,45 +2,76 @@
namespace FS\SolrBundle\Doctrine\Hydration;
-use FS\SolrBundle\Doctrine\Mapper\MetaInformation;
+use Doctrine\Common\Persistence\ManagerRegistry;
+use FS\SolrBundle\Doctrine\Mapper\MetaInformationInterface;
use Symfony\Bridge\Doctrine\RegistryInterface;
-class DoctrineHydrator implements Hydrator
+/**
+ * A doctrine-hydrator finds the entity for a given solr-document. This entity is updated with the solr-document values.
+ *
+ * The hydration is necessary because fields, which are not declared as solr-field, will not populate in the result.
+ */
+class DoctrineHydrator implements HydratorInterface
{
/**
- * @var RegistryInterface
+ * @var ManagerRegistry
*/
- private $doctrine;
+ private $ormManager;
/**
- * @var Hydrator
+ * @var ManagerRegistry
+ */
+ private $odmManager;
+
+ /**
+ * @var HydratorInterface
*/
private $valueHydrator;
/**
- * @param RegistryInterface $doctrine
- * @param Hydrator $valueHydrator
+ * @param HydratorInterface $valueHydrator
*/
- public function __construct(RegistryInterface $doctrine, Hydrator $valueHydrator)
+ public function __construct(HydratorInterface $valueHydrator)
{
- $this->doctrine = $doctrine;
$this->valueHydrator = $valueHydrator;
}
/**
- * @param $document
- * @param MetaInformation $metaInformation
- *
- * @return object
+ * @param ManagerRegistry $ormManager
+ */
+ public function setOrmManager($ormManager)
+ {
+ $this->ormManager = $ormManager;
+ }
+
+ /**
+ * @param ManagerRegistry $odmManager
*/
- public function hydrate($document, MetaInformation $metaInformation)
+ public function setOdmManager($odmManager)
{
- $entityId = $document->id;
- $doctrineEntity = $this->doctrine
- ->getManager()
- ->getRepository($metaInformation->getClassName())
- ->find($entityId);
+ $this->odmManager = $odmManager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hydrate($document, MetaInformationInterface $metaInformation)
+ {
+ $entityId = $this->valueHydrator->removePrefixedKeyValues($document['id']);
+
+ $doctrineEntity = null;
+ if ($metaInformation->getDoctrineMapperType() == MetaInformationInterface::DOCTRINE_MAPPER_TYPE_RELATIONAL) {
+ $doctrineEntity = $this->ormManager
+ ->getManager()
+ ->getRepository($metaInformation->getClassName())
+ ->find($entityId);
+ } elseif ($metaInformation->getDoctrineMapperType() == MetaInformationInterface::DOCTRINE_MAPPER_TYPE_DOCUMENT) {
+ $doctrineEntity = $this->odmManager
+ ->getManager()
+ ->getRepository($metaInformation->getClassName())
+ ->find($entityId);
+ }
if ($doctrineEntity !== null) {
$metaInformation->setEntity($doctrineEntity);
diff --git a/Doctrine/Hydration/DoctrineHydratorFactory.php b/Doctrine/Hydration/DoctrineHydratorFactory.php
new file mode 100644
index 00000000..900e791b
--- /dev/null
+++ b/Doctrine/Hydration/DoctrineHydratorFactory.php
@@ -0,0 +1,40 @@
+container = $container;
+ }
+
+ /**
+ * @return DoctrineHydrator
+ */
+ public function factory()
+ {
+ $valueHydrator = $this->container->get('solr.doctrine.hydration.doctrine_value_hydrator');
+
+ $hydrator = new DoctrineHydrator($valueHydrator);
+ if ($this->container->has('doctrine')) {
+ $hydrator->setOrmManager($this->container->get('doctrine'));
+ }
+
+ if ($this->container->has('doctrine_mongodb')) {
+ $hydrator->setOdmManager($this->container->get('doctrine_mongodb'));
+ }
+
+ return $hydrator;
+ }
+}
\ No newline at end of file
diff --git a/Doctrine/Hydration/DoctrineValueHydrator.php b/Doctrine/Hydration/DoctrineValueHydrator.php
new file mode 100644
index 00000000..d39efda9
--- /dev/null
+++ b/Doctrine/Hydration/DoctrineValueHydrator.php
@@ -0,0 +1,27 @@
+getField($fieldName) && $metaInformation->getField($fieldName)->getter) {
+ return false;
+ }
+
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/Doctrine/Hydration/HydrationModes.php b/Doctrine/Hydration/HydrationModes.php
index 553bbc43..7ff89307 100644
--- a/Doctrine/Hydration/HydrationModes.php
+++ b/Doctrine/Hydration/HydrationModes.php
@@ -2,9 +2,15 @@
namespace FS\SolrBundle\Doctrine\Hydration;
-
class HydrationModes
{
+ /**
+ * use only the values from the index. Ignore not indexed db values.
+ */
const HYDRATE_INDEX = 'index';
+
+ /**
+ * use values from the index and db. Resulting entity holds also not indexed values.
+ */
const HYDRATE_DOCTRINE = 'doctrine';
}
\ No newline at end of file
diff --git a/Doctrine/Hydration/Hydrator.php b/Doctrine/Hydration/Hydrator.php
deleted file mode 100644
index 6a559914..00000000
--- a/Doctrine/Hydration/Hydrator.php
+++ /dev/null
@@ -1,16 +0,0 @@
-valueHydrator = $valueHydrator;
}
/**
- * @param $document
- * @param MetaInformation $metaInformation
- *
- * @return object
+ * {@inheritdoc}
*/
- public function hydrate($document, MetaInformation $metaInformation)
+ public function hydrate($document, MetaInformationInterface $metaInformation)
{
$sourceTargetEntity = $metaInformation->getEntity();
$targetEntity = clone $sourceTargetEntity;
diff --git a/Doctrine/Hydration/NoDatabaseValueHydrator.php b/Doctrine/Hydration/NoDatabaseValueHydrator.php
new file mode 100644
index 00000000..ba8cf42a
--- /dev/null
+++ b/Doctrine/Hydration/NoDatabaseValueHydrator.php
@@ -0,0 +1,20 @@
+setterName = $setterName;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setValue($targetObject, $value)
+ {
+ $targetObject->{$this->setterName}($value);
+ }
+}
\ No newline at end of file
diff --git a/Doctrine/Hydration/PropertyAccessor/PrivatePropertyAccessor.php b/Doctrine/Hydration/PropertyAccessor/PrivatePropertyAccessor.php
new file mode 100644
index 00000000..07019f88
--- /dev/null
+++ b/Doctrine/Hydration/PropertyAccessor/PrivatePropertyAccessor.php
@@ -0,0 +1,29 @@
+classProperty = $classProperty;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setValue($targetObject, $value)
+ {
+ $this->classProperty->setAccessible(true);
+ $this->classProperty->setValue($targetObject, $value);
+ }
+}
\ No newline at end of file
diff --git a/Doctrine/Hydration/PropertyAccessor/PropertyAccessorInterface.php b/Doctrine/Hydration/PropertyAccessor/PropertyAccessorInterface.php
new file mode 100644
index 00000000..e4a5e001
--- /dev/null
+++ b/Doctrine/Hydration/PropertyAccessor/PropertyAccessorInterface.php
@@ -0,0 +1,15 @@
+cache[$metaInformation->getDocumentName()])) {
+ $this->cache[$metaInformation->getDocumentName()] = array();
+ }
+
$targetEntity = $metaInformation->getEntity();
$reflectionClass = new \ReflectionClass($targetEntity);
foreach ($document as $property => $value) {
- try {
+ if ($property === MetaInformationInterface::DOCUMENT_KEY_FIELD_NAME) {
+ $value = $this->removePrefixedKeyValues($value);
+ }
+
+ // skip field if value is array or "flat" object
+ // hydrated object should contain a list of real entities / entity
+ if ($this->mapValue($property, $value, $metaInformation) == false) {
+ continue;
+ }
+
+ if (isset($this->cache[$metaInformation->getDocumentName()][$property])) {
+ $this->cache[$metaInformation->getDocumentName()][$property]->setValue($targetEntity, $value);
+
+ continue;
+ }
+
+ // find setter method
+ $camelCasePropertyName = $this->toCamelCase($this->removeFieldSuffix($property));
+ $setterMethodName = 'set'.ucfirst($camelCasePropertyName);
+ if (method_exists($targetEntity, $setterMethodName)) {
+ $accessor = new MethodCallPropertyAccessor($setterMethodName);
+ $accessor->setValue($targetEntity, $value);
+
+ $this->cache[$metaInformation->getDocumentName()][$property] = $accessor;
+
+ continue;
+ }
+
+
+ if ($reflectionClass->hasProperty($this->removeFieldSuffix($property))) {
$classProperty = $reflectionClass->getProperty($this->removeFieldSuffix($property));
- } catch (\ReflectionException $e) {
- try {
- $classProperty = $reflectionClass->getProperty(
- $this->toCamelCase($this->removeFieldSuffix($property))
- );
- } catch (\ReflectionException $e) {
+ } else {
+ // could no found document-field in underscore notation, transform them to camel-case notation
+ $camelCasePropertyName = $this->toCamelCase($this->removeFieldSuffix($property));
+ if ($reflectionClass->hasProperty($camelCasePropertyName) == false) {
continue;
}
+
+ $classProperty = $reflectionClass->getProperty($camelCasePropertyName);
}
- $classProperty->setAccessible(true);
- $classProperty->setValue($targetEntity, $value);
+ $accessor = new PrivatePropertyAccessor($classProperty);
+ $accessor->setValue($targetEntity, $value);
+
+ $this->cache[$metaInformation->getDocumentName()][$property] = $accessor;
}
return $targetEntity;
@@ -46,7 +91,7 @@ public function hydrate($document, MetaInformation $metaInformation)
*
* @return string
*/
- private function removeFieldSuffix($property)
+ protected function removeFieldSuffix($property)
{
if (($pos = strrpos($property, '_')) !== false) {
return substr($property, 0, $pos);
@@ -55,6 +100,22 @@ private function removeFieldSuffix($property)
return $property;
}
+ /**
+ * keyfield product_1 becomes 1
+ *
+ * @param string $value
+ *
+ * @return string
+ */
+ public function removePrefixedKeyValues($value)
+ {
+ if (($pos = strrpos($value, '_')) !== false) {
+ return substr($value, ($pos + 1));
+ }
+
+ return $value;
+ }
+
/**
* returns field name camelcased if it has underlines
*
@@ -72,4 +133,18 @@ private function toCamelCase($fieldname)
return lcfirst($pascalCased);
}
+
+ /**
+ * Check if given field and value can be mapped
+ *
+ * @param string $fieldName
+ * @param string $value
+ * @param MetaInformationInterface $metaInformation
+ *
+ * @return bool
+ */
+ public function mapValue($fieldName, $value, MetaInformationInterface $metaInformation)
+ {
+ return true;
+ }
}
\ No newline at end of file
diff --git a/Doctrine/Mapper/EntityMapper.php b/Doctrine/Mapper/EntityMapper.php
index 683c8d75..abe92a35 100644
--- a/Doctrine/Mapper/EntityMapper.php
+++ b/Doctrine/Mapper/EntityMapper.php
@@ -2,25 +2,21 @@
namespace FS\SolrBundle\Doctrine\Mapper;
use FS\SolrBundle\Doctrine\Hydration\HydrationModes;
-use FS\SolrBundle\Doctrine\Hydration\Hydrator;
+use FS\SolrBundle\Doctrine\Hydration\HydratorInterface;
+use FS\SolrBundle\Doctrine\Mapper\Factory\DocumentFactory;
use FS\SolrBundle\Doctrine\Mapper\Mapping\AbstractDocumentCommand;
use FS\SolrBundle\Doctrine\Annotation\Index as Solr;
use Solarium\QueryType\Update\Query\Document\Document;
-class EntityMapper
+class EntityMapper implements EntityMapperInterface
{
/**
- * @var CreateDocumentCommandInterface
- */
- private $mappingCommand = null;
-
- /**
- * @var Hydrator
+ * @var HydratorInterface
*/
private $doctrineHydrator;
/**
- * @var Hydrator
+ * @var HydratorInterface
*/
private $indexHydrator;
@@ -30,62 +26,56 @@ class EntityMapper
private $hydrationMode = '';
/**
- * @param Hydrator $doctrineHydrator
- * @param Hydrator $indexHydrator
+ * @var MetaInformationFactory
*/
- public function __construct(Hydrator $doctrineHydrator, Hydrator $indexHydrator)
- {
- $this->doctrineHydrator = $doctrineHydrator;
- $this->indexHydrator = $indexHydrator;
+ private $metaInformationFactory;
- $this->hydrationMode = HydrationModes::HYDRATE_DOCTRINE;
- }
+ /**
+ * @var DocumentFactory
+ */
+ private $documentFactory;
/**
- * @param AbstractDocumentCommand $command
+ * @param HydratorInterface $doctrineHydrator
+ * @param HydratorInterface $indexHydrator
+ * @param MetaInformationFactory $metaInformationFactory
*/
- public function setMappingCommand(AbstractDocumentCommand $command)
+ public function __construct(HydratorInterface $doctrineHydrator, HydratorInterface $indexHydrator, MetaInformationFactory $metaInformationFactory)
{
- $this->mappingCommand = $command;
+ $this->doctrineHydrator = $doctrineHydrator;
+ $this->indexHydrator = $indexHydrator;
+ $this->metaInformationFactory = $metaInformationFactory;
+ $this->documentFactory = new DocumentFactory($metaInformationFactory);
+
+ $this->hydrationMode = HydrationModes::HYDRATE_DOCTRINE;
}
/**
- * @param MetaInformation $meta
- *
- * @return Document
+ * {@inheritdoc}
*/
- public function toDocument(MetaInformation $meta)
+ public function toDocument(MetaInformationInterface $metaInformation)
{
- if ($this->mappingCommand instanceof AbstractDocumentCommand) {
- return $this->mappingCommand->createDocument($meta);
- }
-
- return null;
+ return $this->documentFactory->createDocument($metaInformation);
}
/**
- * @param \ArrayAccess $document
- * @param object $sourceTargetEntity
- *
- * @return object
- *
- * @throws \InvalidArgumentException if $sourceTargetEntity is null
+ * {@inheritdoc}
*/
public function toEntity(\ArrayAccess $document, $sourceTargetEntity)
{
if (null === $sourceTargetEntity) {
- throw new \InvalidArgumentException('$sourceTargetEntity should not be null');
+ throw new SolrMappingException('$sourceTargetEntity should not be null');
}
- $metaInformationFactory = new MetaInformationFactory();
- $metaInformation = $metaInformationFactory->loadInformation($sourceTargetEntity);
+ $metaInformation = $this->metaInformationFactory->loadInformation($sourceTargetEntity);
- $hydratedDocument = $this->indexHydrator->hydrate($document, $metaInformation);
- if ($this->hydrationMode == HydrationModes::HYDRATE_INDEX) {
- return $hydratedDocument;
+ if ($metaInformation->isDoctrineEntity() === false && $this->hydrationMode == HydrationModes::HYDRATE_DOCTRINE) {
+ 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.'));
}
- $metaInformation->setEntity($hydratedDocument);
+ if ($this->hydrationMode == HydrationModes::HYDRATE_INDEX) {
+ return $this->indexHydrator->hydrate($document, $metaInformation);
+ }
if ($this->hydrationMode == HydrationModes::HYDRATE_DOCTRINE) {
return $this->doctrineHydrator->hydrate($document, $metaInformation);
diff --git a/Doctrine/Mapper/EntityMapperInterface.php b/Doctrine/Mapper/EntityMapperInterface.php
new file mode 100644
index 00000000..054035cf
--- /dev/null
+++ b/Doctrine/Mapper/EntityMapperInterface.php
@@ -0,0 +1,33 @@
+metaInformationFactory = $metaInformationFactory;
+ }
+
+ /**
+ * @param MetaInformationInterface $metaInformation
+ *
+ * @return null|Document
+ *
+ * @throws SolrMappingException if no id is set
+ */
+ public function createDocument(MetaInformationInterface $metaInformation)
+ {
+ $fields = $metaInformation->getFields();
+ if (count($fields) == 0) {
+ return null;
+ }
+
+ if (!$metaInformation->getEntityId() && !$metaInformation->generateDocumentId()) {
+ throw new SolrMappingException(sprintf('No entity id set for "%s"', $metaInformation->getClassName()));
+ }
+
+ $documentId = $metaInformation->getDocumentKey();
+ if ($metaInformation->generateDocumentId()) {
+ $documentId = $metaInformation->getDocumentName() . '_' . Uuid::uuid1()->toString();
+ }
+
+ $document = new Document();
+ $document->setKey(MetaInformationInterface::DOCUMENT_KEY_FIELD_NAME, $documentId);
+
+ $document->setBoost($metaInformation->getBoost());
+
+ foreach ($fields as $field) {
+ if (!$field instanceof Field) {
+ continue;
+ }
+
+ $fieldValue = $field->getValue();
+ if (($fieldValue instanceof Collection || is_array($fieldValue)) && $field->nestedClass) {
+ $this->mapCollectionField($document, $field, $metaInformation->getEntity());
+ } else if (is_object($fieldValue) && $field->nestedClass) { // index sinsgle object as nested child-document
+ $document->addField('_childDocuments_', [$this->objectToDocument($fieldValue)], $field->getBoost());
+ } else if (is_object($fieldValue) && !$field->nestedClass) { // index object as "flat" string, call getter
+ $document->addField($field->getNameWithAlias(), $this->mapObjectField($field), $field->getBoost());
+ } else if ($field->getter && $fieldValue) { // call getter to transform data (json to array, etc.)
+ $getterValue = $this->callGetterMethod($metaInformation->getEntity(), $field->getGetterName());
+ $document->addField($field->getNameWithAlias(), $getterValue, $field->getBoost());
+ } else { // field contains simple data-type
+ $document->addField($field->getNameWithAlias(), $fieldValue, $field->getBoost());
+ }
+
+ if ($field->getFieldModifier()) {
+ $document->setFieldModifier($field->getNameWithAlias(), $field->getFieldModifier());
+ }
+ }
+
+ return $document;
+ }
+
+ /**
+ * @param Field $field
+ *
+ * @return array|string
+ *
+ * @throws SolrMappingException if getter return value is object
+ */
+ private function mapObjectField(Field $field)
+ {
+ $value = $field->getValue();
+ $getter = $field->getGetterName();
+ if (empty($getter)) {
+ throw new SolrMappingException(sprintf('Please configure a getter for property "%s" in class "%s"', $field->name, get_class($value)));
+ }
+
+ $getterReturnValue = $this->callGetterMethod($value, $getter);
+
+ if (is_object($getterReturnValue)) {
+ throw new SolrMappingException(sprintf('The configured getter "%s" in "%s" must return a string or array, got object', $getter, get_class($value)));
+ }
+
+ return $getterReturnValue;
+ }
+
+ /**
+ * @param object $object
+ * @param string $getter
+ *
+ * @return mixed
+ *
+ * @throws SolrMappingException if given getter does not exists
+ */
+ private function callGetterMethod($object, $getter)
+ {
+ $methodName = $getter;
+ if (strpos($getter, '(') !== false) {
+ $methodName = substr($getter, 0, strpos($getter, '('));
+ }
+
+ if (!method_exists($object, $methodName)) {
+ throw new SolrMappingException(sprintf('No method "%s()" found in class "%s"', $methodName, get_class($object)));
+ }
+
+ $method = new \ReflectionMethod($object, $methodName);
+ // getter with arguments
+ if (strpos($getter, ')') !== false) {
+ $getterArguments = explode(',', substr($getter, strpos($getter, '(') + 1, -1));
+ $getterArguments = array_map(function ($parameter) {
+ return trim(preg_replace('#[\'"]#', '', $parameter));
+ }, $getterArguments);
+
+ return $method->invokeArgs($object, $getterArguments);
+ }
+
+ return $method->invoke($object);
+ }
+
+ /**
+ * @param Field $field
+ * @param string $sourceTargetClass
+ *
+ * @return array
+ *
+ * @throws SolrMappingException if no getter method was found
+ */
+ private function mapCollectionField($document, Field $field, $sourceTargetObject)
+ {
+ /** @var Collection $collection */
+ $collection = $field->getValue();
+ $getter = $field->getGetterName();
+
+ if ($getter != '') {
+ $collection = $this->callGetterMethod($sourceTargetObject, $getter);
+
+ $collection = array_filter($collection, function ($value) {
+ return $value !== null;
+ });
+ }
+
+ $values = [];
+ if (count($collection)) {
+ foreach ($collection as $relatedObj) {
+ if (is_object($relatedObj)) {
+ $values[] = $this->objectToDocument($relatedObj);
+ } else {
+ $values[] = $relatedObj;
+ }
+ }
+
+ $document->addField('_childDocuments_', $values, $field->getBoost());
+ }
+
+ return $values;
+ }
+
+ /**
+ * @param mixed $value
+ *
+ * @return array
+ *
+ * @throws SolrMappingException
+ */
+ private function objectToDocument($value)
+ {
+ $metaInformation = $this->metaInformationFactory->loadInformation($value);
+
+ $field = [];
+ $document = $this->createDocument($metaInformation);
+ foreach ($document as $fieldName => $value) {
+ $field[$fieldName] = $value;
+ }
+
+ return $field;
+ }
+}
\ No newline at end of file
diff --git a/Doctrine/Mapper/Mapping/AbstractDocumentCommand.php b/Doctrine/Mapper/Mapping/AbstractDocumentCommand.php
deleted file mode 100644
index 25364683..00000000
--- a/Doctrine/Mapper/Mapping/AbstractDocumentCommand.php
+++ /dev/null
@@ -1,28 +0,0 @@
-addField('id', $meta->getEntityId());
- $document->addField('document_name_s', $meta->getDocumentName());
- $document->setBoost($meta->getBoost());
-
- return $document;
- }
-}
diff --git a/Doctrine/Mapper/Mapping/CommandFactory.php b/Doctrine/Mapper/Mapping/CommandFactory.php
deleted file mode 100644
index b0f0b632..00000000
--- a/Doctrine/Mapper/Mapping/CommandFactory.php
+++ /dev/null
@@ -1,36 +0,0 @@
-commands)) {
- throw new \RuntimeException(sprintf('%s is an unknown command', $command));
- }
-
- return $this->commands[$command];
- }
-
- /**
- * @param AbstractDocumentCommand $command
- * @param string $commandName
- */
- public function add(AbstractDocumentCommand $command, $commandName)
- {
- $this->commands[$commandName] = $command;
- }
-}
diff --git a/Doctrine/Mapper/Mapping/MapAllFieldsCommand.php b/Doctrine/Mapper/Mapping/MapAllFieldsCommand.php
deleted file mode 100644
index 11bf81b6..00000000
--- a/Doctrine/Mapper/Mapping/MapAllFieldsCommand.php
+++ /dev/null
@@ -1,39 +0,0 @@
-getFields();
- if (count($fields) == 0) {
- return null;
- }
-
- $document = parent::createDocument($meta);
-
- foreach ($fields as $field) {
- if (!$field instanceof Field) {
- continue;
- }
-
- $document->addField($field->getNameWithAlias(), $field->getValue(), $field->getBoost());
- }
-
- return $document;
- }
-}
diff --git a/Doctrine/Mapper/Mapping/MapIdentifierCommand.php b/Doctrine/Mapper/Mapping/MapIdentifierCommand.php
deleted file mode 100644
index d12966b9..00000000
--- a/Doctrine/Mapper/Mapping/MapIdentifierCommand.php
+++ /dev/null
@@ -1,9 +0,0 @@
-entity !== null) {
+ if ($this->entity !== null && $this->entity->getId()) {
return $this->entity->getId();
}
- return 0;
+ return $this->entityId;
}
/**
- * @return string
+ * @param int $entityId
+ */
+ public function setEntityId($entityId)
+ {
+ $this->entityId = $entityId;
+ }
+
+ /**
+ * {@inheritdoc}
*/
public function getIdentifier()
{
@@ -78,7 +109,7 @@ public function getIdentifier()
}
/**
- * @return string
+ * {@inheritdoc}
*/
public function getClassName()
{
@@ -86,7 +117,7 @@ public function getClassName()
}
/**
- * @return string
+ * {@inheritdoc}
*/
public function getDocumentName()
{
@@ -94,7 +125,7 @@ public function getDocumentName()
}
/**
- * @return array With instances of FS\SolrBundle\Doctrine\Annotation\Field
+ * {@inheritdoc}
*/
public function getFields()
{
@@ -102,7 +133,7 @@ public function getFields()
}
/**
- * @return string
+ * {@inheritdoc}
*/
public function getRepository()
{
@@ -110,7 +141,7 @@ public function getRepository()
}
/**
- * @return object
+ * {@inheritdoc}
*/
public function getEntity()
{
@@ -118,7 +149,7 @@ public function getEntity()
}
/**
- * @param string $identifiert
+ * @param Id $identifier
*/
public function setIdentifier($identifier)
{
@@ -142,7 +173,7 @@ public function setDocumentName($documentName)
}
/**
- * @param multitype: $fields
+ * @param Field[] $fields
*/
public function setFields($fields)
{
@@ -150,38 +181,57 @@ public function setFields($fields)
}
/**
- * @param string $field
+ * @param string $fieldName
+ *
* @return boolean
*/
- public function hasField($field)
+ public function hasField($fieldName)
{
- if (count($this->fields) == 0) {
+ $fields = array_filter($this->fields, function(Field $field) use ($fieldName) {
+ return $field->name == $fieldName || $field->getNameWithAlias() == $fieldName;
+ });
+
+ if (count($fields) == 0) {
return false;
}
- return isset($this->fields[$field]);
+ return true;
}
/**
- * @param string $field
+ * @param string $fieldName
* @param string $value
+ *
+ * @throws SolrMappingException if $fieldName does not exist
*/
- public function setFieldValue($field, $value)
+ public function setFieldValue($fieldName, $value)
{
- $this->fields[$field]->value = $value;
+ if ($this->hasField($fieldName) == false) {
+ throw new SolrMappingException(sprintf('Field %s does not exist', $fieldName));
+ }
+
+ $field = $this->getField($fieldName);
+ $field->value = $value;
}
/**
- * @param unknown_type $field
- * @return Field|null
+ * {@inheritdoc}
*/
- public function getField($field)
+ public function getField($fieldName)
{
- if (!$this->hasField($field)) {
+ if ($fieldName == '') {
+ throw new SolrMappingException('$fieldName must not be empty');
+ }
+
+ if (!$this->hasField($fieldName)) {
return null;
}
- return $this->fields[$field];
+ $fields = array_filter($this->fields, function(Field $field) use ($fieldName) {
+ return $field->name == $fieldName || $field->getNameWithAlias() == $fieldName;
+ });
+
+ return array_pop($fields);
}
/**
@@ -201,7 +251,7 @@ public function setEntity($entity)
}
/**
- * @return array
+ * {@inheritdoc}
*/
public function getFieldMapping()
{
@@ -217,7 +267,7 @@ public function setFieldMapping($fieldMapping)
}
/**
- * @return number
+ * {@inheritdoc}
*/
public function getBoost()
{
@@ -245,7 +295,7 @@ public function hasSynchronizationFilter()
}
/**
- * @return string
+ * {@inheritdoc}
*/
public function getSynchronizationCallback()
{
@@ -269,10 +319,86 @@ public function setIndex($index)
}
/**
- * @return string
+ * {@inheritdoc}
*/
public function getIndex()
{
return $this->index;
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDocumentKey()
+ {
+ return $this->documentName . '_' . $this->getEntityId();
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isDoctrineEntity()
+ {
+ return $this->isDoctrineEntity;
+ }
+
+ /**
+ * @param boolean $isDoctrineEntity
+ */
+ public function setIsDoctrineEntity($isDoctrineEntity)
+ {
+ $this->isDoctrineEntity = $isDoctrineEntity;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getIdentifierFieldName()
+ {
+ return $this->identifier->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDoctrineMapperType()
+ {
+ return $this->doctrineMapperType;
+ }
+
+ /**
+ * @param string $doctrineMapperType
+ */
+ public function setDoctrineMapperType($doctrineMapperType)
+ {
+ $this->doctrineMapperType = $doctrineMapperType;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function generateDocumentId()
+ {
+ if ($this->identifier == null) {
+ throw new SolrMappingException('No identifier is set');
+ }
+
+ return $this->identifier->generateId;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isNested()
+ {
+ return $this->nested;
+ }
+
+ /**
+ * @param bool $nested
+ */
+ public function setNested(bool $nested)
+ {
+ $this->nested = $nested;
+ }
}
diff --git a/Doctrine/Mapper/MetaInformationFactory.php b/Doctrine/Mapper/MetaInformationFactory.php
index f91aa3cb..071b219f 100644
--- a/Doctrine/Mapper/MetaInformationFactory.php
+++ b/Doctrine/Mapper/MetaInformationFactory.php
@@ -3,21 +3,12 @@
use FS\SolrBundle\Doctrine\Annotation\AnnotationReader;
use FS\SolrBundle\Doctrine\ClassnameResolver\ClassnameResolver;
-use FS\SolrBundle\Doctrine\Configuration;
/**
- *
- * @author fs
- *
+ * instantiates a new MetaInformation object by a given entity
*/
class MetaInformationFactory
{
-
- /**
- * @var MetaInformation
- */
- private $metaInformations = null;
-
/**
* @var AnnotationReader
*/
@@ -28,9 +19,12 @@ class MetaInformationFactory
*/
private $classnameResolver = null;
- public function __construct()
+ /**
+ * @param AnnotationReader $reader
+ */
+ public function __construct(AnnotationReader $reader)
{
- $this->annotationReader = new AnnotationReader();
+ $this->annotationReader = $reader;
}
/**
@@ -42,40 +36,102 @@ public function setClassnameResolver(ClassnameResolver $classnameResolver)
}
/**
- * @param $entity
+ * @param object|string $entity entity, entity-alias or classname
*
* @return MetaInformation
*
- * @throws \RuntimeException if no declaration for document found in $entity
+ * @throws SolrMappingException if no declaration for document found in $entity
*/
public function loadInformation($entity)
{
-
$className = $this->getClass($entity);
if (!is_object($entity)) {
- $entity = new $className;
+ $reflectionClass = new \ReflectionClass($className);
+ if (!$reflectionClass->isInstantiable()) {
+ throw new SolrMappingException(sprintf('Cannot instantiate entity %s', $className));
+ }
+ $entity = $reflectionClass->newInstanceWithoutConstructor();
}
if (!$this->annotationReader->hasDocumentDeclaration($entity)) {
- throw new \RuntimeException(sprintf('no declaration for document found in entity %s', $className));
+ throw new SolrMappingException(sprintf('no declaration for document found in entity %s', $className));
}
+ $fields = array_merge($this->annotationReader->getFields($entity), $this->annotationReader->getMethods($entity));
+
$metaInformation = new MetaInformation();
$metaInformation->setEntity($entity);
$metaInformation->setClassName($className);
$metaInformation->setDocumentName($this->getDocumentName($className));
$metaInformation->setFieldMapping($this->annotationReader->getFieldMapping($entity));
- $metaInformation->setFields($this->annotationReader->getFields($entity));
+ $metaInformation->setFields($fields);
$metaInformation->setRepository($this->annotationReader->getRepository($entity));
$metaInformation->setIdentifier($this->annotationReader->getIdentifier($entity));
$metaInformation->setBoost($this->annotationReader->getEntityBoost($entity));
$metaInformation->setSynchronizationCallback($this->annotationReader->getSynchronizationCallback($entity));
$metaInformation->setIndex($this->annotationReader->getDocumentIndex($entity));
+ $metaInformation->setIsDoctrineEntity($this->isDoctrineEntity($entity));
+ $metaInformation->setDoctrineMapperType($this->getDoctrineMapperType($entity));
+ $metaInformation->setNested($this->annotationReader->isNested($entity));
+
+ $fields = $this->annotationReader->getFields($entity);
+ foreach ($fields as $field) {
+ if (!$field->nestedClass) {
+ continue;
+ }
+
+ $nestedObjectMetainformation = $this->loadInformation($field->nestedClass);
+
+ $subentityMapping = [];
+ $nestedFieldName = $field->name;
+ foreach ($nestedObjectMetainformation->getFieldMapping() as $documentName => $fieldName) {
+ $subentityMapping[$nestedFieldName . '.' . $documentName] = $nestedFieldName . '.' . $fieldName;
+ }
+
+ $rootEntityMapping = $metaInformation->getFieldMapping();
+ $subentityMapping = array_merge($subentityMapping, $rootEntityMapping);
+ unset($subentityMapping[$field->name]);
+ $metaInformation->setFieldMapping($subentityMapping);
+ }
return $metaInformation;
}
+ /**
+ * @param object $entity
+ *
+ * @return bool
+ */
+ private function isDoctrineEntity($entity)
+ {
+ if ($this->annotationReader->isOrm($entity) || $this->annotationReader->isOdm($entity)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @param object $entity
+ *
+ * @return string
+ */
+ private function getDoctrineMapperType($entity)
+ {
+ if ($this->isDoctrineEntity($entity) == false) {
+ return '';
+ }
+
+ if ($this->annotationReader->isOdm($entity)) {
+ return MetaInformationInterface::DOCTRINE_MAPPER_TYPE_DOCUMENT;
+ }
+
+ if ($this->annotationReader->isOrm($entity)) {
+ return MetaInformationInterface::DOCTRINE_MAPPER_TYPE_RELATIONAL;
+ }
+ }
+
/**
* @param object $entity
*
diff --git a/Doctrine/Mapper/MetaInformationInterface.php b/Doctrine/Mapper/MetaInformationInterface.php
new file mode 100644
index 00000000..01f425d7
--- /dev/null
+++ b/Doctrine/Mapper/MetaInformationInterface.php
@@ -0,0 +1,145 @@
+ exampleentity
+ *
+ * @return string
+ */
+ public function getDocumentName();
+
+ /**
+ * @return Field[]
+ */
+ public function getFields();
+
+ /**
+ * Returns full qualified classname of repository-class
+ *
+ * @return string
+ */
+ public function getRepository();
+
+ /**
+ * Source/target entity instance
+ *
+ * @return object
+ */
+ public function getEntity();
+
+ /**
+ * @param string $fieldName
+ *
+ * @return Field|null
+ *
+ * @throws \InvalidArgumentException if given $fieldName is unknown
+ */
+ public function getField($fieldName);
+
+ /**
+ * @return array
+ */
+ public function getFieldMapping();
+
+ /**
+ * The document boost value
+ *
+ * @return number
+ */
+ public function getBoost();
+
+ /**
+ * @return string
+ */
+ public function getSynchronizationCallback();
+
+ /**
+ * @return boolean
+ */
+ public function hasSynchronizationFilter();
+
+ /**
+ * Returns the configured index argument in FS\SolrBundle\Doctrine\Annotation\Document or the returns value of the index-handler callback
+ *
+ * @return string
+ */
+ public function getIndex();
+
+ /**
+ * Returns combination of DOCUMENT_KEY_FIELD_NAME and entity-id
+ *
+ * @return string
+ */
+ public function getDocumentKey();
+
+ /**
+ * The property which has the FS\SolrBundle\Doctrine\Annotation\Id annotation
+ *
+ * @return string
+ */
+ public function getIdentifierFieldName();
+
+ /**
+ * Returns MetaInformationInterface::DOCTRINE_MAPPER_TYPE_DOCUMENT if target is an doctrine-odm object or
+ * MetaInformationInterface::DOCTRINE_MAPPER_TYPE_RELATIONAL if it is an doctrine-orm object, otherwise an empty string
+ *
+ * @return string
+ */
+ public function getDoctrineMapperType();
+
+ /**
+ * @return bool
+ */
+ public function isNested();
+}
\ No newline at end of file
diff --git a/Doctrine/Mapper/SolrMappingException.php b/Doctrine/Mapper/SolrMappingException.php
new file mode 100644
index 00000000..254c16eb
--- /dev/null
+++ b/Doctrine/Mapper/SolrMappingException.php
@@ -0,0 +1,8 @@
+solr = $solr;
- }
-
- /**
- * @param LifecycleEventArgs $args
- */
- public function postPersist(LifecycleEventArgs $args)
- {
- $entity = $args->getDocument();
-
- try {
- $this->solr->addDocument($entity);
- } catch (\RuntimeException $e) {
- }
- }
-}
diff --git a/Doctrine/ODM/Listener/DeleteDocumentListener.php b/Doctrine/ODM/Listener/DeleteDocumentListener.php
deleted file mode 100644
index 96b19ba4..00000000
--- a/Doctrine/ODM/Listener/DeleteDocumentListener.php
+++ /dev/null
@@ -1,38 +0,0 @@
-solr = $solr;
- }
-
- /**
- * @param LifecycleEventArgs $args
- */
- public function preRemove(LifecycleEventArgs $args)
- {
- $entity = $args->getDocument();
-
- try {
- $this->solr->removeDocument($entity);
- } catch (\RuntimeException $e) {
- }
- }
-}
-
-?>
\ No newline at end of file
diff --git a/Doctrine/ODM/Listener/DocumentIndexerSubscriber.php b/Doctrine/ODM/Listener/DocumentIndexerSubscriber.php
new file mode 100644
index 00000000..a071603b
--- /dev/null
+++ b/Doctrine/ODM/Listener/DocumentIndexerSubscriber.php
@@ -0,0 +1,68 @@
+getDocument();
+
+ try {
+ $doctrineChangeSet = $args->getDocumentManager()->getUnitOfWork()->getDocumentChangeSet($document);
+
+ if ($this->hasChanged($doctrineChangeSet, $document) == false) {
+ return;
+ }
+
+ $this->solr->updateDocument($document);
+ } catch (\RuntimeException $e) {
+ $this->logger->debug($e->getMessage());
+ }
+ }
+
+ /**
+ * @param LifecycleEventArgs $args
+ */
+ public function preRemove(LifecycleEventArgs $args)
+ {
+ $entity = $args->getDocument();
+
+ try {
+ $this->solr->removeDocument($entity);
+ } catch (\RuntimeException $e) {
+ $this->logger->debug($e->getMessage());
+ }
+ }
+
+ /**
+ * @param LifecycleEventArgs $args
+ */
+ public function postPersist(LifecycleEventArgs $args)
+ {
+ $entity = $args->getDocument();
+
+ try {
+ $this->solr->addDocument($entity);
+ } catch (\RuntimeException $e) {
+ $this->logger->debug($e->getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/Doctrine/ODM/Listener/UpdateDocumentListener.php b/Doctrine/ODM/Listener/UpdateDocumentListener.php
deleted file mode 100644
index 551c0400..00000000
--- a/Doctrine/ODM/Listener/UpdateDocumentListener.php
+++ /dev/null
@@ -1,36 +0,0 @@
-solr = $solr;
- }
-
- /**
- * @param LifecycleEventArgs $args
- */
- public function postUpdate(LifecycleEventArgs $args)
- {
- $entity = $args->getDocument();
-
- try {
- $this->solr->updateDocument($entity);
- } catch (\RuntimeException $e) {
- }
- }
-}
diff --git a/Doctrine/ORM/Listener/AddDocumentListener.php b/Doctrine/ORM/Listener/AddDocumentListener.php
deleted file mode 100644
index 8704ea96..00000000
--- a/Doctrine/ORM/Listener/AddDocumentListener.php
+++ /dev/null
@@ -1,35 +0,0 @@
-solr = $solr;
- }
-
- /**
- * @param LifecycleEventArgs $args
- */
- public function postPersist(LifecycleEventArgs $args)
- {
- $entity = $args->getEntity();
-
- try {
- $this->solr->addDocument($entity);
- } catch (\RuntimeException $e) {
- }
- }
-}
diff --git a/Doctrine/ORM/Listener/DeleteDocumentListener.php b/Doctrine/ORM/Listener/DeleteDocumentListener.php
deleted file mode 100644
index ff97de24..00000000
--- a/Doctrine/ORM/Listener/DeleteDocumentListener.php
+++ /dev/null
@@ -1,35 +0,0 @@
-solr = $solr;
- }
-
- /**
- * @param LifecycleEventArgs $args
- */
- public function preRemove(LifecycleEventArgs $args)
- {
- $entity = $args->getEntity();
-
- try {
- $this->solr->removeDocument($entity);
- } catch (\RuntimeException $e) {
- }
- }
-}
diff --git a/Doctrine/ORM/Listener/EntityIndexerSubscriber.php b/Doctrine/ORM/Listener/EntityIndexerSubscriber.php
new file mode 100644
index 00000000..80f72c3e
--- /dev/null
+++ b/Doctrine/ORM/Listener/EntityIndexerSubscriber.php
@@ -0,0 +1,126 @@
+getEntity();
+
+ if ($this->isAbleToIndex($entity) === false) {
+ return;
+ }
+
+ $doctrineChangeSet = $args->getEntityManager()->getUnitOfWork()->getEntityChangeSet($entity);
+ try {
+ if ($this->hasChanged($doctrineChangeSet, $entity) === false) {
+ return;
+ }
+
+ $this->solr->updateDocument($entity);
+ } catch (\Exception $e) {
+ $this->logger->debug($e->getMessage());
+ }
+ }
+
+ /**
+ * @param LifecycleEventArgs $args
+ */
+ public function postPersist(LifecycleEventArgs $args)
+ {
+ $entity = $args->getEntity();
+
+ if ($this->isAbleToIndex($entity) === false) {
+ return;
+ }
+
+ $this->persistedEntities[] = $entity;
+ }
+
+ /**
+ * @param LifecycleEventArgs $args
+ */
+ public function preRemove(LifecycleEventArgs $args)
+ {
+ $entity = $args->getEntity();
+
+ if ($this->isAbleToIndex($entity) === false) {
+ return;
+ }
+
+ if ($this->isNested($entity)) {
+ $this->deletedNestedEntities[] = $this->emptyCollections($entity);
+ } else {
+ $this->deletedRootEntities[] = $this->emptyCollections($entity);
+ }
+ }
+
+ /**
+ * @param object $object
+ *
+ * @return object
+ */
+ private function emptyCollections($object)
+ {
+ $deepcopy = new DeepCopy();
+ $deepcopy->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
+
+ return $deepcopy->copy($object);
+ }
+
+ /**
+ * @param PostFlushEventArgs $eventArgs
+ */
+ public function postFlush(PostFlushEventArgs $eventArgs)
+ {
+ foreach ($this->persistedEntities as $entity) {
+ $this->solr->addDocument($entity);
+ }
+ $this->persistedEntities = [];
+
+ foreach ($this->deletedRootEntities as $entity) {
+ $this->solr->removeDocument($entity);
+ }
+ $this->deletedRootEntities = [];
+
+ foreach ($this->deletedNestedEntities as $entity) {
+ $this->solr->removeDocument($entity);
+ }
+ $this->deletedNestedEntities = [];
+ }
+}
\ No newline at end of file
diff --git a/Doctrine/ORM/Listener/UpdateDocumentListener.php b/Doctrine/ORM/Listener/UpdateDocumentListener.php
deleted file mode 100644
index 0d8fe0d6..00000000
--- a/Doctrine/ORM/Listener/UpdateDocumentListener.php
+++ /dev/null
@@ -1,35 +0,0 @@
-solr = $solr;
- }
-
- /**
- * @param LifecycleEventArgs $args
- */
- public function postUpdate(LifecycleEventArgs $args)
- {
- $entity = $args->getEntity();
-
- try {
- $this->solr->updateDocument($entity);
- } catch (\RuntimeException $e) {
- }
- }
-}
diff --git a/Event/ErrorEvent.php b/Event/ErrorEvent.php
index c690f0fd..1eb09e2b 100644
--- a/Event/ErrorEvent.php
+++ b/Event/ErrorEvent.php
@@ -1,6 +1,10 @@
sourceEvent;
}
+ /**
+ * @return bool
+ */
public function hasSourceEvent()
{
return $this->sourceEvent !== null;
diff --git a/Event/Events.php b/Event/Events.php
index 360c0923..9020547e 100644
--- a/Event/Events.php
+++ b/Event/Events.php
@@ -2,6 +2,9 @@
namespace FS\SolrBundle\Event;
+/**
+ * List of event which can be fired
+ */
final class Events
{
const PRE_INSERT = 'solr.pre_insert';
diff --git a/Event/Listener/AbstractLogListener.php b/Event/Listener/AbstractLogListener.php
index 8d9cb6ea..1bcf5b81 100644
--- a/Event/Listener/AbstractLogListener.php
+++ b/Event/Listener/AbstractLogListener.php
@@ -1,8 +1,7 @@
getDocumentName() . ':' . $metaInformation->getEntityId();
}
/**
- * @param MetaInformation $metaInformation
+ * @param MetaInformationInterface $metaInformation
+ *
* @return string
*/
- protected function createFieldList(MetaInformation $metaInformation)
+ protected function createFieldList(MetaInformationInterface $metaInformation)
{
return implode(', ', $metaInformation->getFields());
}
diff --git a/Event/Listener/ClearIndexLogListener.php b/Event/Listener/ClearIndexLogListener.php
index 5a8d844a..b7508bb2 100644
--- a/Event/Listener/ClearIndexLogListener.php
+++ b/Event/Listener/ClearIndexLogListener.php
@@ -2,11 +2,16 @@
namespace FS\SolrBundle\Event\Listener;
-
use FS\SolrBundle\Event\Event;
+/**
+ * Create a log-entry if the index was cleared
+ */
class ClearIndexLogListener extends AbstractLogListener
{
+ /**
+ * @param Event $event
+ */
public function onClearIndex(Event $event)
{
$this->logger->debug(sprintf('clear index'));
diff --git a/Event/Listener/DeleteLogListener.php b/Event/Listener/DeleteLogListener.php
index a6f9e90c..30d69cd7 100644
--- a/Event/Listener/DeleteLogListener.php
+++ b/Event/Listener/DeleteLogListener.php
@@ -1,8 +1,12 @@
getExceptionMessage();
diff --git a/Event/Listener/InsertLogListener.php b/Event/Listener/InsertLogListener.php
index 6d2e6e76..162f908d 100644
--- a/Event/Listener/InsertLogListener.php
+++ b/Event/Listener/InsertLogListener.php
@@ -3,6 +3,9 @@
use FS\SolrBundle\Event\Event;
+/**
+ * Create a log-entry if a document was insert
+ */
class InsertLogListener extends AbstractLogListener
{
diff --git a/Event/Listener/SynchronizationSummaryListener.php b/Event/Listener/SynchronizationSummaryListener.php
deleted file mode 100644
index 4d965362..00000000
--- a/Event/Listener/SynchronizationSummaryListener.php
+++ /dev/null
@@ -1,37 +0,0 @@
-commandResult = $commandResult;
- $this->resultFactory = $resultFactory;
- }
-
- public function onSolrError(Event $event)
- {
- if ($event instanceof ErrorEvent) {
- $this->commandResult->error(
- $this->resultFactory->fromEvent($event)
- );
- }
- }
-
- public function onSolrSuccess(Event $event)
- {
- $this->commandResult->success(
- $this->resultFactory->fromEvent($event)
- );
- }
-}
\ No newline at end of file
diff --git a/Event/Listener/UpdateLogListener.php b/Event/Listener/UpdateLogListener.php
index d9f4960e..cf052755 100644
--- a/Event/Listener/UpdateLogListener.php
+++ b/Event/Listener/UpdateLogListener.php
@@ -1,8 +1,12 @@
addCompilerPass(new AddCreateDocumentCommandPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION);
- }
+ $container->addCompilerPass(new AddSolariumPluginsPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION);
+ }
}
diff --git a/Helper/DocumentHelper.php b/Helper/DocumentHelper.php
new file mode 100644
index 00000000..4d776686
--- /dev/null
+++ b/Helper/DocumentHelper.php
@@ -0,0 +1,73 @@
+solariumClient = $solr->getClient();
+ $this->metaInformationFactory = $solr->getMetaFactory();
+ }
+
+ /**
+ * @param mixed $entity
+ *
+ * @return int
+ */
+ public function getLastInsertDocumentId($entity)
+ {
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ /** @var Query $select */
+ $select = $this->solariumClient->createQuery(SolariumClient::QUERY_SELECT);
+ $select->setQuery(sprintf('id:%s*', $metaInformation->getDocumentKey()));
+ $select->setRows($this->getNumberOfDocuments($metaInformation->getDocumentName()));
+ $select->addFields(array('id'));
+
+ $result = $this->solariumClient->select($select);
+
+ if ($result->count() == 0) {
+ return 0;
+ }
+
+ $ids = array_map(function ($document) {
+ return substr($document->id, stripos($document->id, '_') + 1);
+ }, $result->getIterator()->getArrayCopy());
+
+ return intval(max($ids));
+ }
+
+ /**
+ * @param string $documentKey
+ *
+ * @return int
+ */
+ private function getNumberOfDocuments($documentKey)
+ {
+ $select = $this->solariumClient->createQuery(SolariumClient::QUERY_SELECT);
+ $select->setQuery(sprintf('id:%s_*', $documentKey));
+
+ $result = $this->solariumClient->select($select);
+
+ return $result->getNumFound();
+ }
+}
\ No newline at end of file
diff --git a/Logging/DebugLogger.php b/Logging/DebugLogger.php
new file mode 100644
index 00000000..e58c19f0
--- /dev/null
+++ b/Logging/DebugLogger.php
@@ -0,0 +1,52 @@
+queries;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function startRequest(array $request)
+ {
+ $this->start = microtime(true);
+ $this->queries[++$this->currentQuery] = [
+ 'request' => $request,
+ 'executionMS' => 0
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function stopRequest()
+ {
+ $this->queries[$this->currentQuery]['executionMS'] = microtime(true) - $this->start;
+ }
+}
\ No newline at end of file
diff --git a/Logging/SolrLoggerInterface.php b/Logging/SolrLoggerInterface.php
new file mode 100644
index 00000000..324d1347
--- /dev/null
+++ b/Logging/SolrLoggerInterface.php
@@ -0,0 +1,18 @@
+metaInformation;
+ }
+
+ /**
+ * @param MetaInformationInterface $metaInformation
+ */
+ public function setMetaInformation($metaInformation)
+ {
+ $this->metaInformation = $metaInformation;
+
+ $this->entity = $metaInformation->getClassName();
+ $this->index = $metaInformation->getIndex();
+ }
+
+ /**
+ * @return string
*/
public function getEntity()
{
@@ -32,7 +65,7 @@ public function getEntity()
}
/**
- * @param object $entity
+ * @param string $entity
*/
public function setEntity($entity)
{
@@ -40,7 +73,7 @@ public function setEntity($entity)
}
/**
- * @param \Solarium\QueryType\Update\Query\Document\Document $document
+ * @param Document $document
*/
public function setDocument($document)
{
@@ -48,7 +81,7 @@ public function setDocument($document)
}
/**
- * @return \Solarium\QueryType\Update\Query\Document\Document
+ * @return Document
*/
public function getDocument()
{
@@ -56,15 +89,15 @@ public function getDocument()
}
/**
- * @param \FS\SolrBundle\Solr $solr
+ * @param SolrInterface $solr
*/
- public function setSolr($solr)
+ public function setSolr(SolrInterface $solr)
{
$this->solr = $solr;
}
/**
- * @return \FS\SolrBundle\Solr
+ * @return SolrInterface
*/
public function getSolr()
{
@@ -80,4 +113,20 @@ public function setHydrationMode($mode)
{
$this->getSolr()->getMapper()->setHydrationMode($mode);
}
+
+ /**
+ * @return string
+ */
+ public function getIndex()
+ {
+ return $this->index;
+ }
+
+ /**
+ * @param string $index
+ */
+ public function setIndex($index)
+ {
+ $this->index = $index;
+ }
}
diff --git a/Query/DeleteDocumentQuery.php b/Query/DeleteDocumentQuery.php
new file mode 100644
index 00000000..ab4582ff
--- /dev/null
+++ b/Query/DeleteDocumentQuery.php
@@ -0,0 +1,39 @@
+documentKey = $documentKey;
+ }
+
+ /**
+ * @return string
+ *
+ * @throws QueryException when id or document_name is null
+ */
+ public function getQuery()
+ {
+ $idField = $this->documentKey;
+
+ if ($idField == null) {
+ throw new QueryException('id should not be null');
+ }
+
+ $this->setQuery(sprintf('id:%s', $idField));
+
+ return parent::getQuery();
+ }
+}
\ No newline at end of file
diff --git a/Query/Exception/QueryException.php b/Query/Exception/QueryException.php
new file mode 100644
index 00000000..6c1806fd
--- /dev/null
+++ b/Query/Exception/QueryException.php
@@ -0,0 +1,8 @@
+documentName = $documentName;
+ }
+
/**
* @return string
*
- * @throws \RuntimeException if documentName is null
+ * @throws QueryException if documentName is null
*/
public function getQuery()
{
- $documentNameField = $this->document->document_name_s;
+ $documentName = $this->documentName;
- if ($documentNameField == null) {
- throw new \RuntimeException('documentName should not be null');
+ if ($documentName == null) {
+ throw new QueryException('documentName should not be null');
}
- $query = sprintf('document_name_s:%s', $documentNameField);
+ $documentLimitation = $this->createFilterQuery('id')->setQuery(sprintf('id:%s_*', $documentName));
+ $this->addFilterQuery($documentLimitation);
- $this->setQuery($query);
+ $this->setQuery('*:*');
return parent::getQuery();
}
diff --git a/Query/FindByIdentifierQuery.php b/Query/FindByIdentifierQuery.php
index 32676517..82af8da9 100644
--- a/Query/FindByIdentifierQuery.php
+++ b/Query/FindByIdentifierQuery.php
@@ -1,28 +1,41 @@
documentKey = $documentKey;
+ }
/**
* @return string
- * @throws \RuntimeException when id or document_name is null
+ *
+ * @throws QueryException when id or document_name is null
*/
public function getQuery()
{
- $idField = $this->document->id;
- $documentNameField = $this->document->document_name_s;
+ $idField = $this->documentKey;
if ($idField == null) {
- throw new \RuntimeException('id should not be null');
+ throw new QueryException('id should not be null');
}
- if ($documentNameField == null) {
- throw new \RuntimeException('documentName should not be null');
- }
+ $documentLimitation = $this->createFilterQuery('id')->setQuery(sprintf('id:%s', $idField));
+ $this->addFilterQuery($documentLimitation);
- $query = sprintf('id:%s AND document_name_s:%s', $idField, $documentNameField);
- $this->setQuery($query);
+ $this->setQuery('*:*');
return parent::getQuery();
}
diff --git a/Query/QueryBuilder.php b/Query/QueryBuilder.php
new file mode 100644
index 00000000..841d5a7e
--- /dev/null
+++ b/Query/QueryBuilder.php
@@ -0,0 +1,332 @@
+solr = $solr;
+ $this->metaInformation = $metaInformation;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function where($field)
+ {
+ $solrField = $this->metaInformation->getField($field);
+ if ($solrField === null) {
+ throw new UnknownFieldException(sprintf('Field %s does not exists', $field));
+ }
+
+ $fieldName = $solrField->getNameWithAlias();
+
+ $this->criteria = Criteria::where($fieldName);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function andWhere($field)
+ {
+ if ($field instanceof QueryBuilder) {
+ $this->criteria = $this->criteria->andWhere($field->getCriteria());
+
+ return $this;
+ }
+
+ $solrField = $this->metaInformation->getField($field);
+ if ($solrField === null) {
+ throw new UnknownFieldException(sprintf('Field %s does not exists', $field));
+ }
+
+ $fieldName = $solrField->getNameWithAlias();
+
+ $this->criteria = $this->criteria->andWhere($fieldName);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function orWhere($field)
+ {
+ if ($field instanceof QueryBuilder) {
+ $this->criteria = $this->criteria->orWhere($field->getCriteria());
+
+ return $this;
+ }
+
+ $solrField = $this->metaInformation->getField($field);
+ if ($solrField === null) {
+ throw new UnknownFieldException(sprintf('Field %s does not exists', $field));
+ }
+
+ $fieldName = $solrField->getNameWithAlias();
+
+ $this->criteria = $this->criteria->orWhere($fieldName);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function is($value)
+ {
+ $this->criteria = $this->criteria->is($value);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function between($lowerBound, $upperBound, $includeLowerBound = true, $includeUpperBound = true)
+ {
+ $this->criteria = $this->criteria->between($lowerBound, $upperBound, $includeLowerBound, $includeUpperBound);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function in(array $values)
+ {
+ $this->criteria = $this->criteria->in($values);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withinCircle($latitude, $longitude, $distance)
+ {
+ $this->criteria = $this->criteria->withinCircle($latitude, $longitude, $distance);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withinBox($startLatitude, $startLongitude, $endLatitude, $endLongitude)
+ {
+ $this->criteria = $this->criteria->withinBox($startLatitude, $startLongitude, $endLatitude, $endLongitude);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function nearCircle($latitude, $longitude, $distance)
+ {
+ $this->criteria = $this->criteria->nearCircle($latitude, $longitude, $distance);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isNull()
+ {
+ $this->criteria = $this->criteria->isNull();
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isNotNull()
+ {
+ $this->criteria = $this->criteria->isNotNull();
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function contains($value)
+ {
+ $this->criteria = $this->criteria->contains($value);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function startsWith($prefix)
+ {
+ $this->criteria = $this->criteria->startsWith($prefix);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function endsWith($postfix)
+ {
+ $this->criteria = $this->criteria->endsWith($postfix);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function not()
+ {
+ $this->criteria = $this->criteria->not();
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function notOperator()
+ {
+ $this->criteria = $this->criteria->notOperator();
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fuzzy($value, $levenshteinDistance = null)
+ {
+ $this->criteria = $this->criteria->fuzzy($value, $levenshteinDistance);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sloppy($phrase, $distance)
+ {
+ $this->criteria = $this->criteria->sloppy($phrase, $distance);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function expression($value)
+ {
+ $this->criteria = $this->criteria->expression($value);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function greaterThanEqual($value)
+ {
+ $this->criteria = $this->criteria->greaterThanEqual($value);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function greaterThan($value)
+ {
+ $this->criteria = $this->criteria->greaterThan($value);
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function lessThanEqual($value)
+ {
+ $this->criteria = $this->criteria->lessThanEqual($value);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function lessThan($value)
+ {
+ $this->criteria = $this->criteria->lessThan($value);
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function boost($value)
+ {
+ $this->criteria = $this->criteria->boost($value);
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getQuery()
+ {
+ $query = new SolrQuery();
+ $query->setSolr($this->solr);
+ $query->setRows(1000000);
+ $query->setCustomQuery($this->criteria->getQuery());
+ $query->setIndex($this->metaInformation->getIndex());
+ $query->setEntity($this->metaInformation->getEntity());
+ $query->setMetaInformation($this->metaInformation);
+
+ return $query;
+ }
+
+ /**
+ * @return Criteria
+ */
+ public function getCriteria()
+ {
+ return $this->criteria;
+ }
+}
diff --git a/Query/QueryBuilderInterface.php b/Query/QueryBuilderInterface.php
new file mode 100644
index 00000000..54d3dc32
--- /dev/null
+++ b/Query/QueryBuilderInterface.php
@@ -0,0 +1,191 @@
+setUseAndOperator(false);
foreach ($this->mappedFields as $documentField => $entityField) {
+ if ($documentField == $this->getMetaInformation()->getIdentifierFieldName()) {
+ continue;
+ }
+
$this->searchTerms[$documentField] = $value;
}
}
@@ -109,15 +121,34 @@ public function queryAllFields($value)
*
* @param string $field
* @param string $value
+ *
* @return SolrQuery
+ *
+ * @throws UnknownFieldException if $field has not mapping / is unknown
*/
public function addSearchTerm($field, $value)
{
$documentFieldsAsValues = array_flip($this->mappedFields);
- if (array_key_exists($field, $documentFieldsAsValues)) {
- $documentFieldName = $documentFieldsAsValues[$field];
+ $classname = $this->getMetaInformation()->getClassName();
+
+ if (!array_key_exists($field, $documentFieldsAsValues)) {
+ throw new UnknownFieldException(sprintf('Entity %s has no mapping for field %s', $classname, $field));
+ }
+
+ $documentFieldName = $documentFieldsAsValues[$field];
+ if ($position = strpos($field, '.')) {
+ $nestedFieldMapping = $documentFieldsAsValues[$field];
+
+ $nestedField = substr($nestedFieldMapping, $position + 1);
+ $documentName = $this->getMetaInformation()->getDocumentName();
+ $documentFieldName = sprintf('{!parent which="id:%s_*"}%s', $documentName, $nestedField);
+ $childFilterPhrase = str_replace('"', '*', $value);
+ $childFilterPhrase = str_replace(' ', '*', $value);
+ $childFilterPhrase = str_replace('\*', '*', $value);
+ $this->childQueries[$documentFieldName] = $childFilterPhrase;
+ } else {
$this->searchTerms[$documentFieldName] = $value;
}
@@ -126,6 +157,7 @@ public function addSearchTerm($field, $value)
/**
* @param string $field
+ *
* @return SolrQuery
*/
public function addField($field)
@@ -138,19 +170,43 @@ public function addField($field)
return $this;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function addFilterQuery($filterQuery)
+ {
+ if ($this->getFilterQuery('id')) {
+ return $this;
+ }
+
+ return parent::addFilterQuery($filterQuery);
+ }
+
/**
* @return string
*/
public function getQuery()
{
+ $searchTerms = array_merge($this->searchTerms, $this->childQueries);
+
+ $keyField = $this->getMetaInformation()->getDocumentKey();
+
+ $documentLimitation = $this->createFilterQuery('id')->setQuery('id:'.$keyField.'*');
+
+ $this->addFilterQuery($documentLimitation);
if ($this->customQuery) {
- $this->setQuery($this->customQuery);
+ parent::setQuery($this->customQuery);
+
return $this->customQuery;
}
$term = '';
- if (count($this->searchTerms) == 0) {
- return $term;
+ // query all documents if no terms exists
+ if (count($searchTerms) == 0) {
+ $query = '*:*';
+ parent::setQuery($query);
+
+ return $query;
}
$logicOperator = 'AND';
@@ -159,24 +215,71 @@ public function getQuery()
}
$termCount = 1;
- foreach ($this->searchTerms as $fieldName => $fieldValue) {
+ foreach ($searchTerms as $fieldName => $fieldValue) {
+
+ if ($fieldName == 'id') {
+ $this->getFilterQuery('id')->setQuery('id:' . $fieldValue);
+
+ $termCount++;
- if ($this->useWildcards) {
- $term .= $fieldName . ':*' . $fieldValue . '*';
- } else {
- $term .= $fieldName . ':' . $fieldValue;
+ continue;
}
- if ($termCount < count($this->searchTerms)) {
+ $fieldValue = $this->querifyFieldValue($fieldValue);
+
+ $term .= $fieldName . ':' . $fieldValue;
+
+ if ($termCount < count($searchTerms)) {
$term .= ' ' . $logicOperator . ' ';
}
$termCount++;
}
+ if (strlen($term) == 0) {
+ $term = '*:*';
+ }
+
$this->setQuery($term);
return $term;
}
+ /**
+ * Transforms array to string representation and adds quotes
+ *
+ * @param string $fieldValue
+ *
+ * @return string
+ */
+ private function querifyFieldValue($fieldValue)
+ {
+ if (is_array($fieldValue) && count($fieldValue) > 1) {
+ sort($fieldValue);
+
+ $quoted = array_map(function($value) {
+ return '"'. $value .'"';
+ }, $fieldValue);
+
+ $fieldValue = implode(' TO ', $quoted);
+ $fieldValue = '['. $fieldValue . ']';
+
+ return $fieldValue;
+ }
+
+ if (is_array($fieldValue) && count($fieldValue) === 1) {
+ $fieldValue = array_pop($fieldValue);
+ }
+
+ if ($this->useWildcards) {
+ $fieldValue = '*' . $fieldValue . '*';
+ }
+
+ $termParts = explode(' ', $fieldValue);
+ if (count($termParts) > 1) {
+ $fieldValue = '"'.$fieldValue.'"';
+ }
+
+ return $fieldValue;
+ }
}
diff --git a/README.md b/README.md
index f9045b78..7abfcc60 100644
--- a/README.md
+++ b/README.md
@@ -1,297 +1,441 @@
-
+SolrBundle
+==========
[](http://travis-ci.org/floriansemm/SolrBundle)
[](https://packagist.org/packages/floriansemm/solr-bundle)
[](https://packagist.org/packages/floriansemm/solr-bundle)
+Introduction
+------------
+
This Bundle provides a simple API to index and query a Solr Index.
-# Configuration
+## Installation
+
+Installation is a quick (I promise!) 3 step process:
+
+1. Download SolrBundle
+2. Enable the Bundle
+3. Configure the SolrBundle
+4. configure your entity
+
+### Step 1: Download SolrBundle
+
+This bundle is available on Packagist. You can install it using Composer:
+
+```bash
+$ composer require floriansemm/solr-bundle
+```
+
+### Step 2: Enable the bundle
+
+Next, enable the bundle in the kernel:
+
+``` php
+language == 'en') {
+ return 'core0';
}
-
- B. or manually, in app/autoload.php
-
- i. In symfony 2.1.4 (supposing you clone the bundle in vendor/floriansemm/solr-bundle/FS/, making available vendor/floriansemm/solr-bundle/FS/SolrBundle/FSSolrBundle.php)
-
- $loader->add('FS\\SolrBundle', array(__DIR__.'/../vendor/floriansemm/solr-bundle'));
-
- ii. in older version it could be
-
- $loader->registerNamespaces(array(
- // ...
- 'FS' => __DIR__.'/../vendor/bundles',
- // ...
- ));
-
-## Multiple Indexes
-
-You have to setup the connection options
-
- # app/config/config.yml
- fs_solr:
- endpoints:
- core1:
- host: host
- port: 8983
- path: /solr/core1
- core: corename
- timeout: 5
- core1:
- host: host
- port: 8983
- path: /solr/core1
- core: corename
- timeout: 5
-
-With this config you can setup two cores: `core1` and `core2`. See section `Specify cores` for more information.
-
-# Usage #
-
-## Annotations
-
-To put an entity to the index, you must add some annotations to your entity:
-
- // your Entity
-
- // ....
- use FS\SolrBundle\Doctrine\Annotation as Solr;
-
- /**
- * @Solr\Document(repository="Full\Qualified\Class\Name")
- * @ORM\Table()
- */
- class Post
- {
- /**
- * @Solr\Id
- *
- * @ORM\Column(name="id", type="integer")
- * @ORM\Id
- * @ORM\GeneratedValue(strategy="AUTO")
- */
-
- private $id;
- /**
- *
- * @Solr\Field(type="string")
- *
- * @ORM\Column(name="title", type="string", length=255)
- */
- private $title = '';
-
- /**
- *
- * @Solr\Field(type="string")
- *
- * @ORM\Column(name="text", type="text")
- */
- private $text = '';
-
- /**
- * @Solr\Field(type="date")
- *
- * @ORM\Column(name="created_at", type="datetime")
- */
- private $created_at = null;
- }
-
-### Supported field types
-
-Currently is a basic set of types implemented.
-
-- string
-- text
-- date
-- integer
-- float
-- double
-- long
-- boolean
-
-It is possible to use custum field types (schema.xml).
-
-### Filter annotation
-
-In some cases a entity should not be index. For this you have the `SynchronizationFilter` Annotation.
-
-
- /**
- * @Solr\Document
- * @Solr\SynchronizationFilter(callback="shouldBeIndex")
- */
- class SomeEntity
- {
- /**
- * @return boolean
- */
- public function shouldBeIndex()
- {
- // put your logic here
- }
- }
-
-The callback property specifies an callable function, which decides whether the should index or not.
-
-### Specify cores
-
-It is possible to specify a core dedicated to a document
-
- /**
- * @Solr\Document(index="core0")
- */
- class SomeEntity
- {
- // ...
- }
-
-All documents will be indexed in the core `core0`. If your entities/document have different languages then you can setup
-a callback method, which returns the preferred core for the entity.
-
- /**
- * @Solr\Document(indexHandler="indexHandler")
- */
- class SomeEntity
- {
- public function indexHandler()
- {
- if ($this->language == 'en') {
- return 'core0';
- }
- }
- }
-
-Each core must setup up in the config.yml under `endpoints`. If you leave the `index` or `indexHandler` property empty,
-then a default core will be used (first in the `endpoints` list). To index a document in all cores use `*` as index value:
-
- @Solr\Document(index="*")
-
-## Solr field configuration
-
-Solr comes with a set of predefined field-name/field-types mapping:
-
-- title (solr-type: general_text)
-- text (solr-type: general_text)
-- category (solr-type: general_text)
-- content_type (solr-type: string)
-
-For details have a look into your schema.xml.
-
-So if you have an entity with a property "category", then you don't need a type-declaration in the annotation:
+ }
+}
+```
+
+Each core must be set up in `config.yml` under `endpoints`. If you leave the `index` or `indexHandler` property empty,
+then the default core will be used (first one in the `endpoints` list). To index a document in all cores, use `*` as index value.
+
+## `@Solr\Id` annotation
+
+This annotation is required to index an entity. The annotation has no properties. You should add this annotation to the field that will be
+used as the primary identifier for the entity/document.
+```php
+class Post
+{
/**
- * @Solr\Field
- * @ORM\Column(name="category", type="text")
+ * @Solr\Id
+ *
+ * @ORM\Column(name="id", type="integer")
+ * @ORM\Id
+ * @ORM\GeneratedValue(strategy="AUTO")
+ */
+
+ private $id;
+}
+```
+
+### `generateId` option
+
+Set this option to true and a the bundle will generate a Id for you. Use this option if you have no underlying DB which
+ generates incremental Ids for you.
+
+## `@Solr\Field` annotation
+
+This annotation should be added to properties that should be indexed. You should specify the `type` option for the annotation.
+
+### `type` property
+
+Currently, a basic set of types is implemented:
+
+- string(s)
+- text(s)
+- date(s)
+- integer(s)
+- float(s)
+- double(s)
+- long(s)
+- boolean(s)
+
+If you have a customized `schema.xml` than you don't need to setup a field-type.
+
+### `fieldModifier` property
+
+Solr supports partial updates of fields in an existing document. Supported values are:
+
+- set
+- add (multivalue field only, adds a value(s) to a existing list)
+- remove (multivalue field only, removes a value(s) from existing list)
+- inc (integer field only)
+
+### `nestedClass` property
+
+Set this property if you want to index collections with nested Objects.
+
+
+
+### Object relations
+
+[For more information read the more detailed "How to index relation" guide](Resources/doc/index_relations.md)
+
+### `@Solr\SynchronizationFilter(callback="shouldBeIndexed")` annotation
+
+In some cases, an entity should not be indexed. For this, you have the `SynchronizationFilter` annotation to run a filter-callback.
+
+```php
+/**
+ * // ....
+ * @Solr\SynchronizationFilter(callback="shouldBeIndexed")
+ */
+class SomeEntity
+{
+ /**
+ * @return boolean
*/
- private $category = '';
+ public function shouldBeIndexed()
+ {
+ // put your logic here
+ }
+}
+```
+
+The callback property specifies an callable function, which should return a boolean value, specifying whether a concrete
+entity should be indexed.
+
+## Queries
+
+### Query a field of a document
-The field has in this case automaticaly the type "general_text".
+Querying the index is done via the `solr.client` service:
-If you persist this entity, it will put automaticlly to the index. Update and delete happens automatically too.
+```php
+$query = $this->get('solr.client')->createQuery('AcmeDemoBundle:Post');
+$query->addSearchTerm('title', 'my title');
+$query->addSearchTerm('collection_field', array('value1', 'value2'));
-## Query a field of a document
+$result = $query->getResult();
+```
-To query the index you have to call some services.
+or
- $query = $this->get('solr')->createQuery('AcmeDemoBundle:Post');
- $query->addSearchTerm('title', 'my title');
+```php
+$posts = $this->get('solr.client')->getRepository('AcmeDemoBundle:Post')->findOneBy(array(
+ 'title' => 'my title',
+ 'collection_field' => array('value1', 'value2')
+));
+```
- $result = $result = $query->getResult();
-
-The $result array contains all found entities. The solr-service does all mappings from SolrDocument
-to your entity for you.
+### Query all fields of a document
-## Query all fields of a document
+The previous examples were only querying the `title` field. You can also query all fields with a string.
-The pervious examples have queried only the field 'title'. You can also query all fields with a string.
+```php
+$query = $this->get('solr.client')->createQuery('AcmeDemoBundle:Post');
+$query->queryAllFields('my title');
- $query = $this->get('solr.client')->createQuery('AcmeDemoBundle:Post');
- $query->queryAllFields('my title');
+$result = $query->getResult();
+```
- $result = $query->getResult();
+### Define a custom query string
+If you need more flexiblity in your queries you can define your own query strings:
-## Define Result-Mapping
+```php
+$query = $this->get('solr.client')->createQuery('AcmeDemoBundle:Post');
+$query->setCustomQuery('id:post_* AND (author_s:Name1 OR author_s:Name2)');
-To narrow the mapping, you can use the `addField()` method.
+$result = $query->getResult();
+```
- $query = $this->get('solr.client')->createQuery('AcmeDemoBundle:Post');
- $query->addSearchTerm('title', 'my title');
- $query->addField('id');
- $query->addField('text');
+### The QueryBuilder
- $result = $query->getResult();
+The query-builder based on [https://github.com/minimalcode-org/search](/minimalcode-org/search) Criteria API.
-In this case only the fields id and text will be mapped (addField()), so title and created_at will be
-empty. If nothing was found $result is empty.
+```php
+$queryBuilder = $this->get('solr.client')->getQueryBuilder('AcmeDemoBundle:Post');
+$result = $queryBuilder
+ ->where('author')
+ ->is('Name1')
+ ->orWhere('author')
+ ->is('Name2')
+ ->getQuery()
+ ->getResult();
-## Configure HydrationModes
+```
-HydrationMode tells the Bundle how to create an entity from a document.
+To keep your code clean you should move the select-criteria in a repository-class:
+
+```php
+
+class YourRepository extends Repository
+{
+ public function findAuthor($name1, $name2)
+ {
+ return $this->getQueryBuilder()
+ ->where('author')
+ ->is($name1)
+ ->orWhere('author')
+ ->is($name2)
+ ->getQuery()
+ ->getResult();
+ }
+}
+
+```
+
+### Configure HydrationModes
+
+HydrationMode tells the bundle how to create an entity from a document.
1. `FS\SolrBundle\Doctrine\Hydration\HydrationModes::HYDRATE_INDEX` - use only the data from solr
2. `FS\SolrBundle\Doctrine\Hydration\HydrationModes::HYDRATE_DOCTRINE` - merge the data from solr with the entire doctrine-entity
With a custom query:
- $query = $this->get('solr')->createQuery('AcmeDemoBundle:Post');
- $query->setHydrationMode($mode)
+```php
+$query = $this->get('solr.client')->createQuery('AcmeDemoBundle:Post');
+$query->setHydrationMode($mode)
+```
With a custom document-repository you have to set the property `$hydrationMode` itself:
- public function find($id)
+```php
+public function find($id)
+{
+ $this->hydrationMode = HydrationModes::HYDRATE_INDEX;
+
+ return parent::find($id);
+}
+```
+
+## Repositories
+
+Your should define your own repository-class to make your custom queries reuseable. How to configure a repository for a document have a look at [the annotation section](https://github.com/floriansemm/SolrBundle#setting-custom-repository-class-with-repository-option)
+
+```php
+namespace AppBundle\Search;
+
+use FS\SolrBundle\Repository\Repository;
+
+class ProviderRepository extends Repository
+{
+ public function findPost($what)
{
- $this->hydrationMode = HydrationModes::HYDRATE_INDEX;
+ $query = $this->solr->createQuery('AcmeDemoBundle:Post');
+ // some query-magic here
- return parent::find($id);
+ return $query->getResult();
}
+}
+```
-## Index manually an entity
+In your repository you have full access to the querybuilder.
-To index your entities manually, you can do it the following way:
+## Commands
- $this->get('solr')->addDocument($entity);
- $this->get('solr')->updateDocument($entity);
- $this->get('solr')->deleteDocument($entity);
+Here's all the commands provided by this bundle:
-`deleteDocument()` requires that the entity-id is set.
+* `solr:index:clear` - delete all documents in the index
+* `solr:index:populate` - synchronize the db with the index
+* `solr:schema:show` - shows your configured documents
-## Use document repositories
+### Indexing huge sets of entities
-If you specify your own repository you must extend the `FS\SolrBundle\Repository\Repository` class. The useage is the same
-like Doctrine-Repositories:
+The `solr:index:populate` command works well for sets up to 300k entities, everthing large makes the command very slow. You can find [here](Resources/doc/indexing.md) some solution how to sync your DB with Solr.
- $myRepository = $this->get('solr')->getRepository('AcmeDemoBundle:Post');
- $result = $myRepository->mySpecialFindMethod();
-
-If you haven't declared a concrete repository in your entity and you calling `$this->get('solr')->getRepository('AcmeDemoBundle:Post')`, you will
-get an instance of `FS\SolrBundle\Repository\Repository`.
+## Extend Solarium
-## Commands
+To extend Solarium with your own plugins, create a tagged service:
-There are comming two commands with this bundle:
+```xml
+
+```
-* `solr:index:clear` - delete all documents in the index
-* `solr:synchronize` - synchronize the db with the index
+To hook into the [Solarium events](http://solarium.readthedocs.io/en/stable/customizing-solarium/#plugin-system) create a common Symfony event-listener:
+
+```xml
+
+```
+
+## Document helper
+
+### Retrieve the last insert entity-id
+
+```php
+$helper = $this->get('solr.client')->getDocumentHelper();
+$id = $helper->getLastInsertDocumentId();
+```
\ No newline at end of file
diff --git a/Repository/Repository.php b/Repository/Repository.php
index 68b1d1e6..35d810ce 100644
--- a/Repository/Repository.php
+++ b/Repository/Repository.php
@@ -2,10 +2,16 @@
namespace FS\SolrBundle\Repository;
use FS\SolrBundle\Doctrine\Hydration\HydrationModes;
+use FS\SolrBundle\Doctrine\Mapper\MetaInformationInterface;
use FS\SolrBundle\Query\FindByDocumentNameQuery;
use FS\SolrBundle\Query\FindByIdentifierQuery;
+use FS\SolrBundle\Query\QueryBuilderInterface;
use FS\SolrBundle\Solr;
+use FS\SolrBundle\SolrInterface;
+/**
+ * Common repository class to find documents in the index
+ */
class Repository implements RepositoryInterface
{
@@ -15,9 +21,9 @@ class Repository implements RepositoryInterface
protected $solr = null;
/**
- * @var object
+ * @var MetaInformationInterface
*/
- protected $entity = null;
+ protected $metaInformation = null;
/**
* @var string
@@ -25,32 +31,28 @@ class Repository implements RepositoryInterface
protected $hydrationMode = '';
/**
- * @param Solr $solr
- * @param object $entity
+ * @param SolrInterface $solr
+ * @param MetaInformationInterface $metaInformation
*/
- public function __construct(Solr $solr, $entity)
+ public function __construct(SolrInterface $solr, MetaInformationInterface $metaInformation)
{
$this->solr = $solr;
- $this->entity = $entity;
+ $this->metaInformation = $metaInformation;
$this->hydrationMode = HydrationModes::HYDRATE_DOCTRINE;
}
/**
- * @param int $id
- * @return object|null
+ * {@inheritdoc}
*/
public function find($id)
{
- $mapper = $this->solr->getMapper();
- $mapper->setMappingCommand($this->solr->getCommandFactory()->get('all'));
- $metaInformation = $this->solr->getMetaFactory()->loadInformation($this->entity);
-
- $document = $mapper->toDocument($metaInformation);
+ $documentKey = $this->metaInformation->getDocumentName() . '_' . $id;
$query = new FindByIdentifierQuery();
- $query->setDocument($document);
- $query->setEntity($this->entity);
+ $query->setIndex($this->metaInformation->getIndex());
+ $query->setDocumentKey($documentKey);
+ $query->setEntity($this->metaInformation->getEntity());
$query->setSolr($this->solr);
$query->setHydrationMode($this->hydrationMode);
$found = $this->solr->query($query);
@@ -63,25 +65,15 @@ public function find($id)
}
/**
- * @return array of found documents
+ * {@inheritdoc}
*/
public function findAll()
{
- $mapper = $this->solr->getMapper();
- $mapper->setMappingCommand($this->solr->getCommandFactory()->get('all'));
- $metaInformation = $this->solr->getMetaFactory()->loadInformation($this->entity);
-
- $document = $mapper->toDocument($metaInformation);
-
- if (null === $document) {
- return null;
- }
-
- $document->removeField('id');
-
$query = new FindByDocumentNameQuery();
- $query->setDocument($document);
- $query->setEntity($this->entity);
+ $query->setRows(1000000);
+ $query->setDocumentName($this->metaInformation->getDocumentName());
+ $query->setIndex($this->metaInformation->getIndex());
+ $query->setEntity($this->metaInformation->getEntity());
$query->setSolr($this->solr);
$query->setHydrationMode($this->hydrationMode);
@@ -89,14 +81,21 @@ public function findAll()
}
/**
- * @param array $args
- * @return array of found documents
+ * {@inheritdoc}
*/
public function findBy(array $args)
{
- $query = $this->solr->createQuery($this->entity);
+ $query = $this->solr->createQuery($this->metaInformation->getEntity());
+ $query->setHydrationMode($this->hydrationMode);
+ $query->setRows(100000);
+ $query->setUseAndOperator(true);
+ $query->addSearchTerm('id', $this->metaInformation->getDocumentName() . '_*');
+ $query->setQueryDefaultField('id');
+ $helper = $query->getHelper();
foreach ($args as $fieldName => $fieldValue) {
+ $fieldValue = $helper->escapeTerm($fieldValue);
+
$query->addSearchTerm($fieldName, $fieldValue);
}
@@ -104,13 +103,34 @@ public function findBy(array $args)
}
/**
- * @param array $args
- * @return array
+ * {@inheritdoc}
*/
public function findOneBy(array $args)
{
- $found = $this->findBy($args);
+ $query = $this->solr->createQuery($this->metaInformation->getEntity());
+ $query->setHydrationMode($this->hydrationMode);
+ $query->setRows(1);
+ $query->setUseAndOperator(true);
+ $query->addSearchTerm('id', $this->metaInformation->getDocumentName() . '_*');
+ $query->setQueryDefaultField('id');
+
+ $helper = $query->getHelper();
+ foreach ($args as $fieldName => $fieldValue) {
+ $fieldValue = $helper->escapeTerm($fieldValue);
+
+ $query->addSearchTerm($fieldName, $fieldValue);
+ }
+
+ $found = $this->solr->query($query);
return array_pop($found);
}
+
+ /**
+ * @return QueryBuilderInterface
+ */
+ public function getQueryBuilder()
+ {
+ return $this->solr->getQueryBuilder($this->metaInformation->getEntity());
+ }
}
diff --git a/Repository/RepositoryInterface.php b/Repository/RepositoryInterface.php
index fa1d135a..a53011f3 100644
--- a/Repository/RepositoryInterface.php
+++ b/Repository/RepositoryInterface.php
@@ -1,23 +1,30 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
-
- FS\SolrBundle\Doctrine\ORM\Listener\AddDocumentListener
- FS\SolrBundle\Doctrine\ORM\Listener\DeleteDocumentListener
- FS\SolrBundle\Doctrine\ORM\Listener\UpdateDocumentListener
-
- FS\SolrBundle\Doctrine\ODM\Listener\AddDocumentListener
- FS\SolrBundle\Doctrine\ODM\Listener\DeleteDocumentListener
- FS\SolrBundle\Doctrine\ODM\Listener\UpdateDocumentListener
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
+
+
-
-
-
+
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/Resources/config/log_listener.xml b/Resources/config/log_listener.xml
index 82c16513..0e57fc87 100644
--- a/Resources/config/log_listener.xml
+++ b/Resources/config/log_listener.xml
@@ -1,46 +1,51 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
-
+
FS\SolrBundle\Event\Listener\InsertLogListener
FS\SolrBundle\Event\Listener\UpdateLogListener
FS\SolrBundle\Event\Listener\DeleteLogListener
- FS\SolrBundle\Event\Listener\ErrorLogListener
+ FS\SolrBundle\Event\Listener\ErrorLogListener
FS\SolrBundle\Event\Listener\ClearIndexLogListener
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index f21815e7..8904cf2e 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -1,94 +1,104 @@
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
-
FS\SolrBundle\Solr
- FS\SolrBundle\Client\SolrBuilder
- Solarium\Client
-
- FS\SolrBundle\Doctrine\Mapper\Mapping\CommandFactory
- FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory
-
- FS\SolrBundle\Doctrine\ClassnameResolver\ClassnameResolver
- FS\SolrBundle\Doctrine\ClassnameResolver\KnownNamespaceAliases
-
- FS\SolrBundle\Doctrine\Mapper\Mapping\MapAllFieldsCommand
- FS\SolrBundle\Doctrine\Mapper\Mapping\MapIdentifierCommand
-
- FS\SolrBundle\Console\ConsoleCommandResults
- FS\SolrBundle\Event\Listener\SynchronizationSummaryListener
- FS\SolrBundle\Console\ConsoleResultFactory
-
-
-
-
-
-
-
+
+
+
+
+
+
-
+
+
+
-
-
+
+
+
-
+
+
-
+
+
-
+
-
+
+
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
+
+
-
-
-
+
-
-
-
+
+
-
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Resources/doc/index_relations.md b/Resources/doc/index_relations.md
new file mode 100644
index 00000000..8c432088
--- /dev/null
+++ b/Resources/doc/index_relations.md
@@ -0,0 +1,238 @@
+# Index OneToOne/ManyToOne relation
+
+Given you have the following entity with a ManyToOne relation to `Category`.
+
+```php
+setTitle('post category #1');
+
+$post = new Post();
+$post->setTitle('a post title');
+$post->setCategory($category);
+
+$em = $this->getDoctrine()->getManager();
+$em->persist($post);
+$em->flush();
+```
+
+### Quering the relation
+
+```php
+$posts = $this->get('solr.client')->getRepository('AcmeDemoBundle:Post')->findOneBy(array(
+ 'category' => 'post category #1'
+));
+```
+
+# Index OneToMany relation
+
+Given you have the following `Post` entity with a OneToMany relation to `Tag`.
+
+Again you can index the collection in two ways:
+
+- flat strings representation
+- full objects
+
+## flat strings representation
+
+```php
+setTitle($postTitle);
+$post->setText('relation');
+$post->setTags(array(
+ new Tag('tag #1'),
+ new Tag('tag #2'),
+ new Tag('tag #3')
+));
+
+$em = $this->getDoctrine()->getManager();
+$em->persist($post);
+$em->flush();
+```
+
+Which will result in a document like this:
+
+```json
+"docs": [
+ {
+ "id": "post_391",
+ "title_s": "post 25.03.2016",
+ "text_t": "relation",
+ "tags_ss": [
+ "tag #1",
+ "tag #2",
+ "tag #3"
+ ],
+ "_version_": 1529771282767282200
+ }
+]
+```
+
+### Quering the strings collection
+
+Now `Post` can be searched like this
+
+```php
+$posts = $this->get('solr.client')->getRepository('AcmeDemoBundle:Post')->findOneBy(array(
+ 'tags' => 'tag #1'
+));
+```
+
+## Index full objects
+
+Post entity:
+
+```php
+ /**
+ * @Solr\Field(type="strings", nestedClass="Acme\DemoBundle\Entity\Tag")
+ *
+ * @ORM\OneToMany(targetEntity="Acme\DemoBundle\Entity\Tag", mappedBy="post", cascade={"persist"})
+ */
+ private $tags;
+```
+
+Mark the `Tag` entity as Nested
+
+```php
+/**
+ * Tag
+ *
+ * @Solr\Nested()
+ *
+ * @ORM\Table()
+ * @ORM\Entity
+ */
+class Tag
+{
+ /**
+ * @var integer
+ *
+ * @Solr\Id
+ *
+ * orm stuff
+ */
+ private $id;
+
+ /**
+ * @var string
+ *
+ * @Solr\Field(type="string")
+ *
+ * @ORM\Column(name="name", type="string", length=255)
+ */
+ private $name;
+
+ // getter and setter
+}
+```
+
+## Querying the collection
+
+Now `Post` can be searched like this
+
+```php
+$posts = $this->get('solr.client')->getRepository('AcmeDemoBundle:Post')->findOneBy(array(
+ 'tags.name' => 'tag #1'
+));
+```
+
diff --git a/Resources/doc/indexing.md b/Resources/doc/indexing.md
new file mode 100644
index 00000000..2f5d0e9a
--- /dev/null
+++ b/Resources/doc/indexing.md
@@ -0,0 +1,160 @@
+# How to index more than 500k entities
+
+If you want to index a lot of entities then it is not a good idea to use `solr:index:populate`.
+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.
+
+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`.
+
+Here are some possibilities which works well for me:
+
+## CSV export with MySQL Prepared Statement + Solr PostTool
+
+This solution does not use PHP.
+
+1. export your data to person.csv
+```sql
+SET @TS = DATE_FORMAT(NOW(),'_%Y_%m_%d_%H_%i_%s');
+
+SET @FOLDER = '/tmp/'; -- target dir
+SET @PREFIX = 'person';
+SET @EXT = '.csv';
+
+-- first select defines the header of the csv-file
+SET @CMD = CONCAT("SELECT 'id', 'name_s', 'email_s' UNION ALL SELECT * FROM person INTO OUTFILE '",@FOLDER,@PREFIX,@TS,@EXT,
+ "' FIELDS ENCLOSED BY '\"' TERMINATED BY ',' ESCAPED BY '\"'",
+ " LINES TERMINATED BY '\r\n';");
+
+PREPARE statement FROM @CMD;
+
+EXECUTE statement;
+```
+
+Then run this SQL-script:
+
+```bash
+mysql -udbuser -p123 dbname < dump_person_table.sql
+```
+
+The resulting file looks like this: `/tmp/person_2017_03_01_11_21_41.csv`
+
+2. index the csv with [post-tool](https://lucidworks.com/2015/08/04/solr-5-new-binpost-utility/)
+
+```bash
+/opt/solr/solr-5.5.2/bin/post -c core0 /tmp/person_2017_03_01_11_21_41.csv
+```
+
+## PDO Select + [Solarium BufferedAdd](http://solarium.readthedocs.io/en/stable/plugins/#example-usage)
+
+The script has two parts:
+
+1. select a chunk of rows from the DB
+2. add the rows to the index with Solarium
+
+```php
+setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+$statement = $connection->prepare('SELECT COUNT(*) as total_items FROM person');
+$statement->execute();
+$countResult = $statement->fetch(PDO::FETCH_ASSOC);
+
+$totalItems = $countResult['total_items'];
+$batchSize = 5000;
+
+$pages = ceil($totalItems / $batchSize);
+
+$client = new Solarium\Client([
+ 'endpoint' => [
+ 'localhost' => [
+ 'host' => 'localhost',
+ 'port' => 8983,
+ 'path' => '/solr/core0',
+ ]
+ ]
+]);
+
+/** @var \Solarium\Plugin\BufferedAdd\BufferedAdd $buffer */
+$buffer = $client->getPlugin('bufferedadd');
+$buffer->setBufferSize($batchSize);
+
+for ($i = 0; $i <= $pages; $i++) {
+ $limitStart = ($i - 1) * $batchSize;
+ $limitEnd = $batchSize * $i;
+ if ($i == 0) {
+ $limitStart = 1;
+ $limitEnd = $batchSize;
+ }
+
+ $statement = $connection->prepare(sprintf('SELECT id, name, email FROM person WHERE id >= %s AND id <= %s ', $limitStart, $limitEnd));
+ $statement->execute();
+
+ foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $item) {
+ $buffer->createDocument([
+ 'id' => $item['id'],
+ 'name_s' => $item['name'],
+ 'email_s' => $item['email']
+ ]);
+ }
+
+ $statement->closeCursor();
+
+ $buffer->commit();
+
+ echo sprintf('Indexing page %s / %s', $i, $pages) . PHP_EOL;
+}
+
+$buffer->flush();
+```
+
+# PDO Select + CSV Export + Solr Post-Tool
+
+This solution exports the database to csv by using PDO. The exported files are located under `/tmp/export`.
+
+```php
+$connection = new PDO('mysql:host=localhost;dbname=dbname;charset=utf8mb4', 'dbuser', '123');
+$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+$statement = $connection->prepare('SELECT COUNT(*) as total_items FROM person');
+$statement->execute();
+$countResult = $statement->fetch(PDO::FETCH_ASSOC);
+$statement->closeCursor();
+
+$totalItems = $countResult['total_items'];
+$batchSize = 10000;
+
+$pages = ceil($totalItems / $batchSize);
+
+@mkdir('/tmp/export');
+
+for ($i = 0; $i <= $pages; $i++) {
+ $data = [];
+ $limitStart = ($i - 1) * $batchSize;
+ $limitEnd = $batchSize * $i;
+ if ($i == 0) {
+ $limitStart = 1;
+ $limitEnd = $batchSize;
+ }
+
+ $statement = $connection->prepare(sprintf('SELECT id, name, email FROM person WHERE id >= %s AND id <= %s ', $limitStart, $limitEnd));
+ $statement->execute();
+
+ $data[] = "id, name_s, email_s\n";
+
+ foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $item) {
+ $data[] = sprintf("\"%s\", \"%s\", \"%s\"", $item['id'], $item['name'], $item['email']);
+ }
+
+ $statement->closeCursor();
+
+ file_put_contents(sprintf('/tmp/export/person_%s.csv', $i), join("\n", $data));
+
+ echo sprintf('Indexing page %s / %s', $i, $pages) . PHP_EOL;
+}
+```
+
+To import the data we are using Solr Post-Tool:
+
+`/opt/solr/solr-5.5.2/bin/post -c core0 /tmp/export`
diff --git a/Resources/views/Profiler/icon.svg b/Resources/views/Profiler/icon.svg
new file mode 100644
index 00000000..8b9214fa
--- /dev/null
+++ b/Resources/views/Profiler/icon.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/Resources/views/Profiler/solr.html.twig b/Resources/views/Profiler/solr.html.twig
new file mode 100644
index 00000000..7c2036b8
--- /dev/null
+++ b/Resources/views/Profiler/solr.html.twig
@@ -0,0 +1,222 @@
+{% extends app.request.isXmlHttpRequest ? '@WebProfiler/Profiler/ajax_layout.html.twig' : '@WebProfiler/Profiler/layout.html.twig' %}
+
+{% block toolbar %}
+ {% set profiler_markup_version = profiler_markup_version|default(1) %}
+
+ {% if collector.querycount > 0 %}
+ {% if profiler_markup_version == 1 %}
+ {% set icon %}
+
+ {{ collector.querycount }}
+ {% if collector.querycount > 0 %}
+ in {{ '%0.2f'|format(collector.time * 1000) }} ms
+ {% endif %}
+ {% endset %}
+
+ {% set text %}
+
+ Solr queries
+ {{ collector.querycount }}
+
+
+ Query time
+ {{ '%0.2f'|format(collector.time * 1000) }} ms
+
+ {% endset %}
+ {% else %}
+ {% set status = collector.querycount > 50 ? 'yellow' %}
+
+ {% set icon %}
+ {{ include('@FSSolr/Profiler/icon.svg') }}
+ {{ collector.querycount }}
+
+ in
+ {{ '%0.2f'|format(collector.time * 1000) }}
+ ms
+
+ {% endset %}
+
+ {% set text %}
+
+ Solr queries
+ {{ collector.querycount }}
+
+
+ Query time
+ {{ '%0.2f'|format(collector.time * 1000) }} ms
+
+ {% endset %}
+ {% endif %}
+
+ {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status|default('') }) }}
+ {% endif %}
+{% endblock %}
+
+{% block menu %}
+ {% set profiler_markup_version = profiler_markup_version|default(1) %}
+
+ {% if profiler_markup_version == 1 %}
+
+
+
+ Solr
+
+ {{ collector.querycount }}
+ {{ '%0.0f'|format(collector.time * 1000) }} ms
+
+
+
+ {% else %}
+
+
+ {{ include('@FSSolr/Profiler/icon.svg') }}
+ Solr
+
+
+ {% endif %}
+{% endblock %}
+
+{% block panel %}
+ {% set profiler_markup_version = profiler_markup_version|default(1) %}
+
+ {{ block('queries') }}
+{% endblock %}
+
+{% block queries %}
+ {% if profiler_markup_version == 1 %}
+
+
+
+ Solr Queries
+
+ {% else %}
+
+ Query Metrics
+
+
+ {{ collector.querycount }}
+ Solr queries
+
+
+
+ {{ '%0.2f'|format(collector.time * 1000) }} ms
+ Query time
+
+
+
+ Queries
+
+ {% endif %}
+
+ {% if collector.queries is empty %}
+
+
No solr queries were performed.
+
+ {% else %}
+
+
+
+ | #▲ |
+ Time |
+ End Point |
+ Method |
+ Parameters |
+ Raw Data |
+
+
+
+ {% for i, query in collector.queries %}
+
+ | {{ loop.index }} |
+ {{ '%0.2f'|format(query.executionMS * 1000) }} ms |
+
+ {{ query.endpoint }}
+ |
+
+ {{ query.method }}
+ |
+
+ {% if query.stub is not defined %}
+ {{ query.params | replace({'&': " "}) | raw }}
+ {% else %}
+ {{ profiler_dump(query.stub, 1) }}
+ {% endif %}
+ |
+
+ {{ query.raw_data }}
+ |
+
+ {% endfor %}
+
+
+ {% endif %}
+
+
+{% endblock %}
diff --git a/Solr.php b/Solr.php
index 72acc983..9c303a93 100644
--- a/Solr.php
+++ b/Solr.php
@@ -1,27 +1,36 @@
solrClientCore = $client;
- $this->commandFactory = $commandFactory;
$this->eventManager = $manager;
$this->metaInformationFactory = $metaInformationFactory;
$this->entityMapper = $entityMapper;
}
-
+
/**
* @return Client
*/
- public function getClient()
+ public function getClient(): Client
{
return $this->solrClientCore;
}
@@ -85,104 +87,114 @@ public function getClient()
/**
* @return EntityMapper
*/
- public function getMapper()
+ public function getMapper(): EntityMapper
{
return $this->entityMapper;
}
/**
- * @return CommandFactory
+ * @return MetaInformationFactory
*/
- public function getCommandFactory()
+ public function getMetaFactory(): MetaInformationFactory
{
- return $this->commandFactory;
+ return $this->metaInformationFactory;
}
/**
- * @return MetaInformationFactory
+ * @return DocumentHelper
*/
- public function getMetaFactory()
+ public function getDocumentHelper()
{
- return $this->metaInformationFactory;
+ return new DocumentHelper($this);
}
/**
- * @param object $entity
+ * @param object|string $entity entity, entity-alias or classname
*
* @return SolrQuery
*/
- public function createQuery($entity)
+ public function createQuery($entity): SolrQuery
{
$metaInformation = $this->metaInformationFactory->loadInformation($entity);
- $class = $metaInformation->getClassName();
- $entity = new $class;
$query = new SolrQuery();
$query->setSolr($this);
- $query->setEntity($entity);
-
+ $query->setEntity($metaInformation->getClassName());
+ $query->setIndex($metaInformation->getIndex());
+ $query->setMetaInformation($metaInformation);
$query->setMappedFields($metaInformation->getFieldMapping());
return $query;
}
/**
- * @param string $entityAlias
- *
- * @return Repository
+ * @param string|object $entity
*
- * @throws \RuntimeException if repository does not extend FS\SolrBundle\Repository\Repository
+ * @return QueryBuilderInterface
*/
- public function getRepository($entityAlias)
+ public function getQueryBuilder($entity): QueryBuilderInterface
{
- $metaInformation = $this->metaInformationFactory->loadInformation($entityAlias);
- $class = $metaInformation->getClassName();
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ return new QueryBuilder($this, $metaInformation);
+ }
- $entity = new $class;
+ /**
+ * {@inheritdoc}
+ */
+ public function getRepository($entity): RepositoryInterface
+ {
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
$repositoryClass = $metaInformation->getRepository();
if (class_exists($repositoryClass)) {
- $repositoryInstance = new $repositoryClass($this, $entity);
+ $repositoryInstance = new $repositoryClass($this, $metaInformation);
if ($repositoryInstance instanceof Repository) {
return $repositoryInstance;
}
- throw new \RuntimeException(sprintf(
- '%s must extends the FS\SolrBundle\Repository\Repository',
- $repositoryClass
- ));
+ throw new SolrException(sprintf('%s must extends the FS\SolrBundle\Repository\Repository', $repositoryClass));
}
- return new Repository($this, $entity);
+ return new Repository($this, $metaInformation);
}
/**
- * @param object $entity
+ * {@inheritdoc}
*/
- public function removeDocument($entity)
+ public function createQueryBuilder($entity): QueryBuilderInterface
{
- $command = $this->commandFactory->get('identifier');
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
- $this->entityMapper->setMappingCommand($command);
+ return new QueryBuilder($this, $metaInformation);
+ }
- $metaInformations = $this->metaInformationFactory->loadInformation($entity);
+ /**
+ * {@inheritdoc}
+ */
+ public function removeDocument($entity)
+ {
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $event = new Event($this->solrClientCore, $metaInformation);
+ $this->eventManager->dispatch(Events::PRE_DELETE, $event);
- if ($document = $this->entityMapper->toDocument($metaInformations)) {
- $event = new Event($this->solrClientCore, $metaInformations);
- $this->eventManager->dispatch(Events::PRE_DELETE, $event);
+ if ($document = $this->entityMapper->toDocument($metaInformation)) {
try {
- $indexName = $metaInformations->getIndex();
+ $indexName = $metaInformation->getIndex();
- $client = new \FS\SolrBundle\Client\Client($this->solrClientCore);
+ $client = new SolariumMulticoreClient($this->solrClientCore);
$client->delete($document, $indexName);
} catch (\Exception $e) {
- $errorEvent = new ErrorEvent(null, $metaInformations, 'delete-document', $event);
+ $errorEvent = new ErrorEvent(null, $metaInformation, 'delete-document', $event);
$errorEvent->setException($e);
$this->eventManager->dispatch(Events::ERROR, $errorEvent);
+
+ throw new SolrException($e->getMessage(), $e->getCode(), $e);
}
$this->eventManager->dispatch(Events::POST_DELETE, $event);
@@ -190,35 +202,41 @@ public function removeDocument($entity)
}
/**
- * @param object $entity
+ * {@inheritdoc}
*/
- public function addDocument($entity)
+ public function addDocument($entity): bool
{
$metaInformation = $this->metaInformationFactory->loadInformation($entity);
if (!$this->addToIndex($metaInformation, $entity)) {
- return;
+ return false;
}
- $doc = $this->toDocument($metaInformation);
+ if ($metaInformation->isNested()) {
+ return false;
+ }
$event = new Event($this->solrClientCore, $metaInformation);
$this->eventManager->dispatch(Events::PRE_INSERT, $event);
+ $doc = $this->toDocument($metaInformation);
+
$this->addDocumentToIndex($doc, $metaInformation, $event);
$this->eventManager->dispatch(Events::POST_INSERT, $event);
+
+ return true;
}
/**
- * @param MetaInformation $metaInformation
- * @param object $entity
+ * @param MetaInformationInterface $metaInformation
+ * @param object $entity
*
* @return boolean
*
- * @throws \BadMethodCallException if callback method not exists
+ * @throws SolrException if callback method not exists
*/
- private function addToIndex(MetaInformation $metaInformation, $entity)
+ private function addToIndex(MetaInformationInterface $metaInformation, $entity): bool
{
if (!$metaInformation->hasSynchronizationFilter()) {
return true;
@@ -226,102 +244,145 @@ private function addToIndex(MetaInformation $metaInformation, $entity)
$callback = $metaInformation->getSynchronizationCallback();
if (!method_exists($entity, $callback)) {
- throw new \BadMethodCallException(sprintf('unknown method %s in entity %s', $callback, get_class($entity)));
+ throw new SolrException(sprintf('unknown method %s in entity %s', $callback, get_class($entity)));
}
return $entity->$callback();
}
/**
+ * Get select query
+ *
* @param AbstractQuery $query
*
- * @return array of found documents
+ * @return SolariumQuery
*/
- public function query(AbstractQuery $query)
+ public function getSelectQuery(AbstractQuery $query): SolariumQuery
{
- $entity = $query->getEntity();
+ $selectQuery = $this->solrClientCore->createSelect($query->getOptions());
+
+ $selectQuery->setQuery($query->getQuery());
+ $selectQuery->setFilterQueries($query->getFilterQueries());
+ $selectQuery->setSorts($query->getSorts());
+ $selectQuery->setFields($query->getFields());
+
+ return $selectQuery;
+ }
- $queryString = $query->getQuery();
- $query = $this->solrClientCore->createSelect($query->getOptions());
- $query->setQuery($queryString);
+ /**
+ * {@inheritdoc}
+ */
+ public function query(AbstractQuery $query): array
+ {
+ $entity = $query->getEntity();
+ $runQueryInIndex = $query->getIndex();
+ $selectQuery = $this->getSelectQuery($query);
try {
- $response = $this->solrClientCore->select($query);
+ $response = $this->solrClientCore->select($selectQuery, $runQueryInIndex);
+
+ $this->numberOfFoundDocuments = $response->getNumFound();
+
+ $entities = array();
+ foreach ($response as $document) {
+ $entities[] = $this->entityMapper->toEntity($document, $entity);
+ }
+
+ return $entities;
} catch (\Exception $e) {
$errorEvent = new ErrorEvent(null, null, 'query solr');
$errorEvent->setException($e);
$this->eventManager->dispatch(Events::ERROR, $errorEvent);
- return array();
- }
-
- $this->numberOfFoundDocuments = $response->getNumFound();
- if ($this->numberOfFoundDocuments == 0) {
- return array();
+ throw new SolrException($e->getMessage(), $e->getCode(), $e);
}
-
- $targetEntity = $entity;
- $mappedEntities = array();
- foreach ($response as $document) {
- $mappedEntities[] = $this->entityMapper->toEntity($document, $targetEntity);
- }
-
- return $mappedEntities;
}
/**
- * Number of results found by query
+ * Number of overall found documents for a given query
*
* @return integer
*/
- public function getNumFound()
+ public function getNumFound(): int
{
return $this->numberOfFoundDocuments;
}
/**
* clears the whole index by using the query *:*
+ *
+ * @throws SolrException if an error occurs
*/
public function clearIndex()
{
$this->eventManager->dispatch(Events::PRE_CLEAR_INDEX, new Event($this->solrClientCore));
try {
- $client = new \FS\SolrBundle\Client\Client($this->solrClientCore);
+ $client = new SolariumMulticoreClient($this->solrClientCore);
$client->clearCores();
} catch (\Exception $e) {
$errorEvent = new ErrorEvent(null, null, 'clear-index');
$errorEvent->setException($e);
$this->eventManager->dispatch(Events::ERROR, $errorEvent);
+
+ throw new SolrException($e->getMessage(), $e->getCode(), $e);
}
$this->eventManager->dispatch(Events::POST_CLEAR_INDEX, new Event($this->solrClientCore));
}
/**
- * @param object $entity
+ * @param array $entities
*/
- public function synchronizeIndex($entity)
+ public function synchronizeIndex($entities)
{
- $this->updateDocument($entity);
+ /** @var BufferedAdd $buffer */
+ $buffer = $this->solrClientCore->getPlugin('bufferedadd');
+ $buffer->setBufferSize(500);
+
+ $allDocuments = array();
+ foreach ($entities as $entity) {
+ $metaInformations = $this->metaInformationFactory->loadInformation($entity);
+
+ if (!$this->addToIndex($metaInformations, $entity)) {
+ continue;
+ }
+
+ $doc = $this->toDocument($metaInformations);
+
+ $allDocuments[$metaInformations->getIndex()][] = $doc;
+ }
+
+ foreach ($allDocuments as $core => $documents) {
+ $buffer->addDocuments($documents);
+
+ if ($core == '') {
+ $core = null;
+ }
+ $buffer->setEndpoint($core);
+
+ $buffer->commit();
+ }
}
/**
- * @param object $entity
- *
- * @return bool
+ * {@inheritdoc}
*/
- public function updateDocument($entity)
+ public function updateDocument($entity): bool
{
$metaInformations = $this->metaInformationFactory->loadInformation($entity);
- $doc = $this->toDocument($metaInformations);
+ if (!$this->addToIndex($metaInformations, $entity)) {
+ return false;
+ }
$event = new Event($this->solrClientCore, $metaInformations);
$this->eventManager->dispatch(Events::PRE_UPDATE, $event);
+ $doc = $this->toDocument($metaInformations);
+
$this->addDocumentToIndex($doc, $metaInformations, $event);
$this->eventManager->dispatch(Events::POST_UPDATE, $event);
@@ -330,31 +391,30 @@ public function updateDocument($entity)
}
/**
- * @param MetaInformation $metaInformation
+ * @param MetaInformationInterface $metaInformation
*
- * @return Document
+ * @return DocumentInterface
*/
- private function toDocument(MetaInformation $metaInformation)
+ private function toDocument(MetaInformationInterface $metaInformation): DocumentInterface
{
- $command = $this->commandFactory->get('all');
-
- $this->entityMapper->setMappingCommand($command);
$doc = $this->entityMapper->toDocument($metaInformation);
return $doc;
}
/**
- * @param object $doc
- * @param MetaInformation $metaInformation
- * @param Event $event
+ * @param object $doc
+ * @param MetaInformationInterface $metaInformation
+ * @param Event $event
+ *
+ * @throws SolrException if an error occurs
*/
- private function addDocumentToIndex($doc, MetaInformation $metaInformation, Event $event)
+ private function addDocumentToIndex($doc, MetaInformationInterface $metaInformation, Event $event)
{
try {
$indexName = $metaInformation->getIndex();
- $client = new \FS\SolrBundle\Client\Client($this->solrClientCore);
+ $client = new SolariumMulticoreClient($this->solrClientCore);
$client->update($doc, $indexName);
} catch (\Exception $e) {
@@ -362,6 +422,8 @@ private function addDocumentToIndex($doc, MetaInformation $metaInformation, Even
$errorEvent->setException($e);
$this->eventManager->dispatch(Events::ERROR, $errorEvent);
+
+ throw new SolrException($e->getMessage(), $e->getCode(), $e);
}
}
}
diff --git a/SolrException.php b/SolrException.php
new file mode 100644
index 00000000..61d8543e
--- /dev/null
+++ b/SolrException.php
@@ -0,0 +1,8 @@
+metaFactory = new MetaInformationFactory(new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader()));
+ $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class);
+ $this->mapper = $this->getMockBuilder(EntityMapperInterface::class)
+ ->disableOriginalConstructor()
+ ->setMethods(array('setMappingCommand', 'toDocument', 'toEntity', 'setHydrationMode'))
+ ->getMock();
+
+ $this->solrClientFake = $this->createMock(Client::class);
+
+ $this->solr = new Solr($this->solrClientFake, $this->eventDispatcher, $this->metaFactory, $this->mapper);
+ }
+
+ protected function assertUpdateQueryExecuted($index = null)
+ {
+ $updateQuery = $this->createMock(UpdateQuery::class);
+ $updateQuery->expects($this->once())
+ ->method('addDocument');
+
+ $updateQuery->expects($this->once())
+ ->method('addCommit');
+
+ $this->solrClientFake
+ ->expects($this->once())
+ ->method('createUpdate')
+ ->will($this->returnValue($updateQuery));
+
+ $this->solrClientFake
+ ->expects($this->once())
+ ->method('update')
+ ->with($updateQuery, $index);
+
+ return $updateQuery;
+ }
+
+ protected function assertUpdateQueryWasNotExecuted()
+ {
+ $updateQuery = $this->createMock(UpdateQuery::class);
+ $updateQuery->expects($this->never())
+ ->method('addDocument');
+
+ $updateQuery->expects($this->never())
+ ->method('addCommit');
+
+ $this->solrClientFake
+ ->expects($this->never())
+ ->method('createUpdate');
+ }
+
+ protected function assertDeleteQueryWasExecuted()
+ {
+ $deleteQuery = $this->createMock(UpdateQuery::class);
+ $deleteQuery->expects($this->once())
+ ->method('addDeleteQuery')
+ ->with($this->isType('string'));
+
+ $deleteQuery->expects($this->once())
+ ->method('addCommit');
+
+ $this->solrClientFake
+ ->expects($this->once())
+ ->method('createUpdate')
+ ->will($this->returnValue($deleteQuery));
+
+ $this->solrClientFake
+ ->expects($this->once())
+ ->method('update')
+ ->with($deleteQuery);
+ }
+
+ protected function setupMetaFactoryLoadOneCompleteInformation($metaInformation = null)
+ {
+ if (null === $metaInformation) {
+ $metaInformation = MetaTestInformationFactory::getMetaInformation();
+ }
+
+ $this->metaFactory->expects($this->once())
+ ->method('loadInformation')
+ ->will($this->returnValue($metaInformation));
+ }
+
+ protected function assertQueryWasExecuted($data = array(), $index)
+ {
+ $selectQuery = $this->createMock(SelectQuery::class);
+ $selectQuery->expects($this->once())
+ ->method('setQuery');
+
+ $queryResult = new ResultFake($data);
+
+ $this->solrClientFake
+ ->expects($this->once())
+ ->method('createSelect')
+ ->will($this->returnValue($selectQuery));
+
+ $this->solrClientFake
+ ->expects($this->once())
+ ->method('select')
+ ->will($this->returnValue($queryResult));
+ }
+
+ protected function mapOneDocument()
+ {
+ $this->mapper->expects($this->once())
+ ->method('toDocument')
+ ->will($this->returnValue($this->createMock(DocumentInterface::class)));
+ }
+}
\ No newline at end of file
diff --git a/Tests/Client/Solarium/SolariumClientBuilderTest.php b/Tests/Client/Solarium/SolariumClientBuilderTest.php
new file mode 100644
index 00000000..8a9723c0
--- /dev/null
+++ b/Tests/Client/Solarium/SolariumClientBuilderTest.php
@@ -0,0 +1,137 @@
+defaultEndpoints = [
+ 'unittest' => [
+ 'schema' => 'http',
+ 'host' => '127.0.0.1',
+ 'port' => 8983,
+ 'path' => '/solr',
+ 'timeout' => 5,
+ 'core' => null
+ ]
+ ];
+ }
+
+ public function testCreateClientWithoutDsn()
+ {
+ $actual = $this->createClientWithSettings($this->defaultEndpoints);
+
+ $endpoint = $actual->getEndpoint('unittest');
+ $this->assertEquals('http://127.0.0.1:8983/solr/', $endpoint->getBaseUri());
+ }
+
+ public function testCreateClientWithoutDsnWithCore()
+ {
+ $this->defaultEndpoints['unittest']['core'] = 'core0';
+
+ $actual = $this->createClientWithSettings($this->defaultEndpoints);
+
+ $endpoint = $actual->getEndpoint('unittest');
+ $this->assertEquals('http://127.0.0.1:8983/solr/core0/', $endpoint->getBaseUri());
+ }
+
+ /**
+ * @param string $dsn
+ * @param string $expectedBaseUri
+ * @param string $message
+ *
+ * @dataProvider dsnProvider
+ */
+ public function testCreateClientWithDsn($dsn, $expectedBaseUri, $message)
+ {
+ $settings = $this->defaultEndpoints;
+ $settings['unittest'] = [
+ 'dsn' => $dsn
+ ];
+
+ $actual = $this->createClientWithSettings($settings);
+
+ $endpoint = $actual->getEndpoint('unittest');
+ $this->assertEquals($expectedBaseUri, $endpoint->getBaseUri(), $message);
+ }
+
+ /**
+ * @param string $dsn
+ * @param string $expectedBaseUri
+ * @param string $message
+ *
+ * @dataProvider dsnProvider
+ */
+ public function testCreateClientWithDsnAndCore($dsn, $expectedBaseUri, $message)
+ {
+ $settings = $this->defaultEndpoints;
+ $settings['unittest'] = [
+ 'dsn' => $dsn,
+ 'core' => 'core0'
+ ];
+
+ $actual = $this->createClientWithSettings($settings);
+
+ $endpoint = $actual->getEndpoint('unittest');
+ $this->assertEquals($expectedBaseUri . 'core0/', $endpoint->getBaseUri(), $message . ' with core');
+ }
+
+ /**
+ * @return array
+ */
+ public function dsnProvider()
+ {
+ return [
+ [
+ 'http://example.com:1234',
+ 'http://example.com:1234/',
+ 'Test DSN without path and any authentication'
+ ],
+ [
+ 'http://example.com:1234/solr',
+ 'http://example.com:1234/solr/',
+ 'Test DSN without any authentication'
+ ],
+ [
+ 'http://user@example.com:1234/solr',
+ 'http://user@example.com:1234/solr/',
+ 'Test DSN with user-only authentication'
+ ],
+ [
+ 'http://user:secret@example.com:1234/solr',
+ 'http://user:secret@example.com:1234/solr/',
+ 'Test DSN with authentication'
+ ],
+ [
+ 'https://example.com:1234/solr',
+ 'https://example.com:1234/solr/',
+ 'Test DSN with HTTPS'
+ ]
+ ];
+ }
+
+ /**
+ * @param array $settings
+ *
+ * @return \Solarium\Client
+ */
+ private function createClientWithSettings(array $settings)
+ {
+ /** @var EventDispatcherInterface $eventDispatcherMock */
+ $eventDispatcherMock = $this->createMock(EventDispatcherInterface::class);
+
+ return (new SolariumClientBuilder($settings, $eventDispatcherMock))->build();
+ }
+}
diff --git a/Tests/Console/ConsoleCommandResultsTest.php b/Tests/Console/ConsoleCommandResultsTest.php
deleted file mode 100644
index c33697ed..00000000
--- a/Tests/Console/ConsoleCommandResultsTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-success(new CommandResult(1, 'a class'));
- $results->success(new CommandResult(2, 'a class'));
- $results->success(new CommandResult(3, 'a class'));
-
- $results->error(new CommandResult(2, 'a class'));
-
- $this->assertEquals(2, $results->getSucceed());
-
- $this->assertArrayHasKey(2, $results->getErrors());
-
- $this->assertArrayHasKey(1, $results->getSuccess());
- $this->assertArrayHasKey(3, $results->getSuccess());
- $this->assertArrayNotHasKey(2, $results->getSuccess());
- }
-}
-
\ No newline at end of file
diff --git a/Tests/Console/ConsoleResultFactoryTest.php b/Tests/Console/ConsoleResultFactoryTest.php
deleted file mode 100644
index 55e464df..00000000
--- a/Tests/Console/ConsoleResultFactoryTest.php
+++ /dev/null
@@ -1,66 +0,0 @@
-setException(new \Exception('message'));
-
- $factory = new ConsoleResultFactory();
- $result = $factory->fromEvent($error);
-
- $this->assertEquals('message', $result->getMessage());
- }
-
- /**
- * @test
- */
- public function resultNotContainsIdAndEntityWhenMetaInformationNull()
- {
- $event = new Event(null, null, '');
-
- $factory = new ConsoleResultFactory();
- $result = $factory->fromEvent($event);
-
- $this->assertEquals(null, $result->getResultId());
- $this->assertEquals('', $result->getEntity());
- $this->assertEquals('', $result->getMessage());
- }
-
- /**
- * @test
- */
- public function resultFromSuccessEventContainsNoMessage()
- {
- $entity = new ValidTestEntity();
- $entity->setId(1);
-
- $metaInformation = new MetaInformation();
- $metaInformation->setClassName('an entity');
- $metaInformation->setEntity($entity);
-
- $event = new Event(null, $metaInformation, '');
-
- $factory = new ConsoleResultFactory();
- $result = $factory->fromEvent($event);
-
- $this->assertEquals(1, $result->getResultId());
- $this->assertEquals('an entity', $result->getEntity());
- $this->assertEquals('', $result->getMessage());
- }
-}
-
\ No newline at end of file
diff --git a/Tests/DependencyInjection/FSSolrExtensionTest.php b/Tests/DependencyInjection/FSSolrExtensionTest.php
index 5baf0e5d..d83a755a 100644
--- a/Tests/DependencyInjection/FSSolrExtensionTest.php
+++ b/Tests/DependencyInjection/FSSolrExtensionTest.php
@@ -26,12 +26,12 @@ public function setUp()
private function enableOdmConfig()
{
- $this->container->setParameter('doctrine_mongodb.odm.document_managers', array('default'=>'odm.default.mananger'));
+ $this->container->setParameter('doctrine_mongodb.odm.document_managers', array('default' => 'odm.default.mananger'));
}
private function enableOrmConfig()
{
- $this->container->setParameter('doctrine.entity_managers', array('default'=>'orm.default.mananger'));
+ $this->container->setParameter('doctrine.entity_managers', array('default' => 'orm.default.mananger'));
}
private function commonConfig()
@@ -56,13 +56,9 @@ public function testDoctrineORMSetup()
$extension = new FSSolrExtension();
$extension->load($config, $this->container);
- $this->assertTrue($this->container->has('solr.update.document.orm.listener'), 'update listener');
- $this->assertTrue($this->container->has('solr.delete.document.orm.listener'), 'delete listener');
- $this->assertTrue($this->container->has('solr.add.document.orm.listener'), 'insert listener');
+ $this->assertTrue($this->container->has('solr.document.orm.subscriber'), 'orm subscriber');
- $this->assertDefinitionHasTag('solr.update.document.orm.listener', 'doctrine.event_listener');
- $this->assertDefinitionHasTag('solr.delete.document.orm.listener', 'doctrine.event_listener');
- $this->assertDefinitionHasTag('solr.add.document.orm.listener', 'doctrine.event_listener');
+ $this->assertDefinitionHasTag('solr.document.orm.subscriber', 'doctrine.event_subscriber');
$this->assertClassnameResolverHasOrmDefaultConfiguration();
}
@@ -75,13 +71,9 @@ public function testDoctrineODMSetup()
$extension = new FSSolrExtension();
$extension->load($config, $this->container);
- $this->assertTrue($this->container->has('solr.update.document.odm.listener'), 'update listener');
- $this->assertTrue($this->container->has('solr.delete.document.odm.listener'), 'delete listener');
- $this->assertTrue($this->container->has('solr.add.document.odm.listener'), 'insert listener');
+ $this->assertTrue($this->container->has('solr.document.odm.subscriber'), 'odm subscriber');
- $this->assertDefinitionHasTag('solr.update.document.odm.listener', 'doctrine_mongodb.odm.event_listener');
- $this->assertDefinitionHasTag('solr.delete.document.odm.listener', 'doctrine_mongodb.odm.event_listener');
- $this->assertDefinitionHasTag('solr.add.document.odm.listener', 'doctrine_mongodb.odm.event_listener');
+ $this->assertDefinitionHasTag('solr.document.odm.subscriber', 'doctrine_mongodb.odm.event_subscriber');
$this->assertClassnameResolverHasOdmDefaultConfiguration();
}
@@ -98,21 +90,11 @@ public function solrListensToOdmAndOrmEvents()
$extension = new FSSolrExtension();
$extension->load($config, $this->container);
- $this->assertTrue($this->container->has('solr.update.document.odm.listener'), 'update listener');
- $this->assertTrue($this->container->has('solr.delete.document.odm.listener'), 'delete listener');
- $this->assertTrue($this->container->has('solr.add.document.odm.listener'), 'insert listener');
+ $this->assertTrue($this->container->has('solr.document.odm.subscriber'), 'odm subscriber');
+ $this->assertDefinitionHasTag('solr.document.odm.subscriber', 'doctrine_mongodb.odm.event_subscriber');
- $this->assertDefinitionHasTag('solr.update.document.odm.listener', 'doctrine_mongodb.odm.event_listener');
- $this->assertDefinitionHasTag('solr.delete.document.odm.listener', 'doctrine_mongodb.odm.event_listener');
- $this->assertDefinitionHasTag('solr.add.document.odm.listener', 'doctrine_mongodb.odm.event_listener');
-
- $this->assertTrue($this->container->has('solr.update.document.orm.listener'), 'update listener');
- $this->assertTrue($this->container->has('solr.delete.document.orm.listener'), 'delete listener');
- $this->assertTrue($this->container->has('solr.add.document.orm.listener'), 'insert listener');
-
- $this->assertDefinitionHasTag('solr.update.document.orm.listener', 'doctrine.event_listener');
- $this->assertDefinitionHasTag('solr.delete.document.orm.listener', 'doctrine.event_listener');
- $this->assertDefinitionHasTag('solr.add.document.orm.listener', 'doctrine.event_listener');
+ $this->assertTrue($this->container->has('solr.document.orm.subscriber'), 'orm subscriber');
+ $this->assertDefinitionHasTag('solr.document.orm.subscriber', 'doctrine.event_subscriber');
}
private function assertClassnameResolverHasOrmDefaultConfiguration()
diff --git a/Tests/Doctrine/Annotation/AnnotationReaderTest.php b/Tests/Doctrine/Annotation/AnnotationReaderTest.php
index d32b41d6..508a4677 100644
--- a/Tests/Doctrine/Annotation/AnnotationReaderTest.php
+++ b/Tests/Doctrine/Annotation/AnnotationReaderTest.php
@@ -2,19 +2,22 @@
namespace FS\SolrBundle\Tests\Doctrine\Mapping\Mapper;
+use Doctrine\Common\Annotations\Reader;
use FS\SolrBundle\Doctrine\Annotation\Field;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidTestEntityIndexHandler;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidTestEntityIndexProperty;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidTestEntityNoBoost;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidTestEntityNoTypes;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidTestEntityFiltered;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidTestEntityFloatBoost;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidTestEntityNumericFields;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidTestEntityWithInvalidBoost;
-use FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity;
+use FS\SolrBundle\Tests\Fixtures\ValidEntityRepository;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityIndexHandler;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityIndexProperty;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityNoBoost;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityNoTypes;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityFiltered;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityFloatBoost;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityNumericFields;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityWithInvalidBoost;
+use FS\SolrBundle\Tests\Fixtures\ValidOdmTestDocument;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntity;
use FS\SolrBundle\Doctrine\Annotation\AnnotationReader;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\EntityWithRepository;
-use FS\SolrBundle\Tests\Doctrine\Mapper\NotIndexedEntity;
+use FS\SolrBundle\Tests\Fixtures\EntityWithRepository;
+use FS\SolrBundle\Tests\Fixtures\NotIndexedEntity;
/**
*
@@ -22,30 +25,33 @@
*/
class AnnotationReaderTest extends \PHPUnit_Framework_TestCase
{
+ /**
+ * @var AnnotationReader
+ */
+ private $reader;
- public function testGetFields_NoFieldsDected()
+ public function setUp()
{
- $reader = new AnnotationReader();
+ $this->reader = new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader());
+ }
- $fields = $reader->getFields(new NotIndexedEntity());
+ public function testGetFields_NoFieldsDected()
+ {
+ $fields = $this->reader->getFields(new NotIndexedEntity());
$this->assertEquals(0, count($fields));
}
public function testGetFields_ThreeFieldsDetected()
{
- $reader = new AnnotationReader();
+ $fields = $this->reader->getFields(new ValidTestEntity());
- $fields = $reader->getFields(new ValidTestEntity());
-
- $this->assertEquals(4, count($fields), '4 fields are mapped');
+ $this->assertEquals(5, count($fields), '5 fields are mapped');
}
public function testGetFields_OneFieldsOneTypes()
{
- $reader = new AnnotationReader();
-
- $fields = $reader->getFields(new ValidTestEntityNoTypes());
+ $fields = $this->reader->getFields(new ValidTestEntityNoTypes());
$this->assertEquals(1, count($fields), '1 fields are mapped');
@@ -55,49 +61,41 @@ public function testGetFields_OneFieldsOneTypes()
}
/**
- * @expectedException RuntimeException
+ * @expectedException \FS\SolrBundle\Doctrine\Annotation\AnnotationReaderException
+ * @expectedExceptionMessage no identifer declared in entity FS\SolrBundle\Tests\Fixtures\NotIndexedEntity
*/
public function testGetIdentifier_ShouldThrowException()
{
- $reader = new AnnotationReader();
-
- $reader->getIdentifier(new NotIndexedEntity());
+ $this->reader->getIdentifier(new NotIndexedEntity());
}
public function testGetIdentifier()
{
- $reader = new AnnotationReader();
-
- $id = $reader->getIdentifier(new ValidTestEntity());
+ $id = $this->reader->getIdentifier(new ValidTestEntity());
$this->assertEquals('id', $id->name);
+ $this->assertFalse($id->generateId);
}
public function testGetFieldMapping_ThreeMappingsAndId()
{
- $reader = new AnnotationReader();
-
- $fields = $reader->getFieldMapping(new ValidTestEntity());
+ $fields = $this->reader->getFieldMapping(new ValidTestEntity());
- $this->assertEquals(5, count($fields), 'five fields are mapped');
+ $this->assertEquals(6, count($fields), 'six fields are mapped');
$this->assertTrue(array_key_exists('title', $fields));
$this->assertTrue(array_key_exists('id', $fields));
}
public function testGetRepository_ValidRepositoryDeclared()
{
- $reader = new AnnotationReader();
- $repository = $reader->getRepository(new EntityWithRepository());
+ $repositoryClassname = $this->reader->getRepository(new EntityWithRepository());
- $expected = 'FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidEntityRepository';
- $actual = $repository;
- $this->assertEquals($expected, $actual, 'wrong declared repository');
+ $this->assertEquals(ValidEntityRepository::class, $repositoryClassname, 'wrong declared repository');
}
public function testGetRepository_NoRepositoryAttributSet()
{
- $reader = new AnnotationReader();
- $repository = $reader->getRepository(new ValidTestEntity());
+ $repository = $this->reader->getRepository(new ValidTestEntity());
$expected = '';
$actual = $repository;
@@ -106,56 +104,44 @@ public function testGetRepository_NoRepositoryAttributSet()
public function testGetBoost()
{
- $reader = new AnnotationReader();
- $boost = $reader->getEntityBoost(new ValidTestEntity());
+ $boost = $this->reader->getEntityBoost(new ValidTestEntity());
$this->assertEquals(1, $boost);
}
+ /**
+ * @expectedException \FS\SolrBundle\Doctrine\Annotation\AnnotationReaderException
+ * @expectedExceptionMessage Invalid boost value "aaaa" in class "FS\SolrBundle\Tests\Fixtures\ValidTestEntityWithInvalidBoost" configured
+ */
public function testGetBoost_BoostNotNumeric()
{
- $reader = new AnnotationReader();
-
- try {
- $boost = $reader->getEntityBoost(new ValidTestEntityWithInvalidBoost());
-
- $this->fail();
- } catch (\InvalidArgumentException $e) {
- $this->assertEquals(
- 'Invalid boost value aaaa for entity FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidTestEntityWithInvalidBoost',
- $e->getMessage()
- );
- }
+ $this->reader->getEntityBoost(new ValidTestEntityWithInvalidBoost());
}
public function testGetBoost_BoostIsNumberic()
{
- $reader = new AnnotationReader();
- $boost = $reader->getEntityBoost(new ValidTestEntityFloatBoost());
+ $boost = $this->reader->getEntityBoost(new ValidTestEntityFloatBoost());
$this->assertEquals(1.4, $boost);
}
public function testGetBoost_BoostIsNull()
{
- $reader = new AnnotationReader();
- $boost = $reader->getEntityBoost(new ValidTestEntityNoBoost());
+ $boost = $this->reader->getEntityBoost(new ValidTestEntityNoBoost());
$this->assertEquals(null, $boost);
}
public function testGetCallback_CallbackDefined()
{
- $reader = new AnnotationReader();
- $callback = $reader->getSynchronizationCallback(new ValidTestEntityFiltered());
+ $callback = $this->reader->getSynchronizationCallback(new ValidTestEntityFiltered());
$this->assertEquals('shouldBeIndex', $callback);
}
public function testGetCallback_NoCallbackDefined()
{
- $reader = new AnnotationReader();
- $callback = $reader->getSynchronizationCallback(new ValidTestEntity());
+ $callback = $this->reader->getSynchronizationCallback(new ValidTestEntity());
$this->assertEquals('', $callback);
}
@@ -165,8 +151,7 @@ public function testGetCallback_NoCallbackDefined()
*/
public function numericFieldTypeAreSupported()
{
- $reader = new AnnotationReader();
- $fields = $reader->getFields(new ValidTestEntityNumericFields());
+ $fields = $this->reader->getFields(new ValidTestEntityNumericFields());
$this->assertEquals(4, count($fields));
@@ -184,8 +169,7 @@ public function numericFieldTypeAreSupported()
*/
public function getIndexFromAnnotationProperty()
{
- $reader = new AnnotationReader();
- $index = $reader->getDocumentIndex(new ValidTestEntityIndexProperty());
+ $index = $this->reader->getDocumentIndex(new ValidTestEntityIndexProperty());
$this->assertEquals('my_core', $index);
}
@@ -195,8 +179,7 @@ public function getIndexFromAnnotationProperty()
*/
public function getIndexFromIndexHandler()
{
- $reader = new AnnotationReader();
- $index = $reader->getDocumentIndex(new ValidTestEntityIndexHandler());
+ $index = $this->reader->getDocumentIndex(new ValidTestEntityIndexHandler());
$this->assertEquals('my_core', $index);
}
@@ -206,11 +189,84 @@ public function getIndexFromIndexHandler()
*/
public function readAnnotationsFromBaseClass()
{
- $reader = new AnnotationReader();
- $fields = $reader->getFields(new ChildEntity());
+ $fields = $this->reader->getFields(new ChildEntity());
+
+ $this->assertEquals(3, count($fields));
+ $this->assertTrue($this->reader->hasDocumentDeclaration(new ChildEntity()));
+ }
+
+ /**
+ * @test
+ */
+ public function readAnnotationsOfNestedObject()
+ {
+ $this->assertTrue($this->reader->hasDocumentDeclaration(new NestedObject()));
+ }
+
+ /**
+ * @test
+ */
+ public function readAnnotationsFromMultipleClassHierarchy()
+ {
+ $fields = $this->reader->getFields(new ChildEntity2());
+
+ $this->assertEquals(4, count($fields));
+ }
+
+ /**
+ * @test
+ */
+ public function readGetterMethodWithParameters()
+ {
+ /** @var Field[] $fields */
+ $fields = $this->reader->getFields(new EntityWithObject());
+
+ $this->assertCount(1, $fields);
+ $this->assertEquals('format(\'d.m.Y\')', $fields[0]->getGetterName());
+
+ $this->assertEquals('object_dt', $fields[0]->getNameWithAlias());
+
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfPlainObjectIsNotDoctrineEntity()
+ {
+ $this->assertFalse($this->reader->isOrm(new ChildEntity()), 'is not a doctrine entity');
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfValidEntityIsDoctrineEntity()
+ {
+ $this->assertTrue($this->reader->isOrm(new ValidTestEntity()), 'is a doctrine entity');
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfPlainObjectIsNotDoctrineDocument()
+ {
+ $this->assertFalse($this->reader->isOdm(new ChildEntity()), 'is not a doctrine document');
+ }
+
+ /**
+ * @test
+ */
+ public function checkIfValidDocumentIsDoctrineDocument()
+ {
+ $this->assertTrue($this->reader->isOdm(new ValidOdmTestDocument()), 'is a doctrine document');
+ }
- $this->assertEquals(2, count($fields));
- $this->assertTrue($reader->hasDocumentDeclaration(new ChildEntity()));
+ /**
+ * @test
+ * @expectedException \FS\SolrBundle\Doctrine\Mapper\SolrMappingException
+ */
+ public function methodWithAnnotationMustHaveAField()
+ {
+ $this->reader->getMethods(new EntityMissingNameProperty());
}
}
@@ -222,11 +278,16 @@ public function readAnnotationsFromBaseClass()
*/
abstract class BaseEntity
{
+ /**
+ * @var mixed
+ */
+ protected $baseField1;
+
/**
*
* @Solr\Field(type="integer")
*/
- private $field;
+ protected $baseField2;
}
class ChildEntity extends BaseEntity
@@ -234,5 +295,41 @@ class ChildEntity extends BaseEntity
/**
* @Solr\Field(type="integer")
*/
- private $childField;
+ protected $baseField1;
+
+ /**
+ * @Solr\Field(type="integer")
+ */
+ protected $childField1;
+}
+
+class ChildEntity2 extends ChildEntity
+{
+ /**
+ * @Solr\Field(type="integer")
+ */
+ private $childField2;
+}
+
+class EntityWithObject
+{
+ /**
+ * @Solr\Field(type="datetime", getter="format('d.m.Y')")
+ */
+ private $object;
+}
+
+/**
+ * @Solr\Nested()
+ */
+class NestedObject {}
+
+/** @Solr\Document() */
+class EntityMissingNameProperty {
+
+ /** @Solr\Field(type="string") */
+ public function getPropertyValue2()
+ {
+ return 1234;
+ }
}
\ No newline at end of file
diff --git a/Tests/Doctrine/Annotation/Entities/EntityWithInvalidRepository.php b/Tests/Doctrine/Annotation/Entities/EntityWithInvalidRepository.php
deleted file mode 100644
index 1354a71b..00000000
--- a/Tests/Doctrine/Annotation/Entities/EntityWithInvalidRepository.php
+++ /dev/null
@@ -1,14 +0,0 @@
-knownAliases = $this->getMock('FS\SolrBundle\Doctrine\ClassnameResolver\KnownNamespaceAliases', array(), array(), '', false);
+ $this->knownAliases = $this->createMock(KnownNamespaceAliases::class);
}
/**
@@ -26,9 +28,7 @@ public function resolveClassnameOfCommonEntity()
{
$resolver = $this->getResolverWithKnowNamespace(self::ENTITY_NAMESPACE);
- $expectedClass = 'FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity';
-
- $this->assertEquals($expectedClass, $resolver->resolveFullQualifiedClassname('FSTest:ValidTestEntity'));
+ $this->assertEquals(ValidTestEntity::class, $resolver->resolveFullQualifiedClassname('FSTest:ValidTestEntity'));
}
/**
diff --git a/Tests/Doctrine/ClassnameResolver/KnownNamespaceAliasesTest.php b/Tests/Doctrine/ClassnameResolver/KnownNamespaceAliasesTest.php
index 4d899f10..ee50e4cb 100644
--- a/Tests/Doctrine/ClassnameResolver/KnownNamespaceAliasesTest.php
+++ b/Tests/Doctrine/ClassnameResolver/KnownNamespaceAliasesTest.php
@@ -3,21 +3,24 @@
namespace FS\SolrBundle\Tests\Doctrine\ClassnameResolver;
+use Doctrine\ORM\Configuration as OrmConfiguration;
+use Doctrine\ODM\MongoDB\Configuration as OdmConfiguration;
use FS\SolrBundle\Doctrine\ClassnameResolver\KnownNamespaceAliases;
-class KnownNamespaceAliasesTest extends \PHPUnit_Framework_TestCase {
+class KnownNamespaceAliasesTest extends \PHPUnit_Framework_TestCase
+{
/**
* @test
*/
public function addAliasFromMultipleOrmConfigurations()
{
- $config1 = $this->getMock('Doctrine\ORM\Configuration', array(), array(), '', false);
+ $config1 = $this->createMock(OrmConfiguration::class);
$config1->expects($this->once())
->method('getEntityNamespaces')
->will($this->returnValue(array('AcmeDemoBundle')));
- $config2 = $this->getMock('Doctrine\ORM\Configuration', array(), array(), '', false);
+ $config2 = $this->createMock(OrmConfiguration::class);
$config2->expects($this->once())
->method('getEntityNamespaces')
->will($this->returnValue(array('AcmeBlogBundle')));
@@ -35,12 +38,12 @@ public function addAliasFromMultipleOrmConfigurations()
*/
public function addAliasFromMultipleOdmConfigurations()
{
- $config1 = $this->getMock('Doctrine\ODM\MongoDB\Configuration', array(), array(), '', false);
+ $config1 = $this->createMock(OdmConfiguration::class);
$config1->expects($this->once())
->method('getDocumentNamespaces')
->will($this->returnValue(array('AcmeDemoBundle')));
- $config2 = $this->getMock('Doctrine\ODM\MongoDB\Configuration', array(), array(), '', false);
+ $config2 = $this->createMock(OdmConfiguration::class);
$config2->expects($this->once())
->method('getDocumentNamespaces')
->will($this->returnValue(array('AcmeBlogBundle')));
@@ -58,12 +61,12 @@ public function addAliasFromMultipleOdmConfigurations()
*/
public function knowAliasHasAValidNamespace()
{
- $config1 = $this->getMock('Doctrine\ODM\MongoDB\Configuration', array(), array(), '', false);
+ $config1 = $this->createMock(OdmConfiguration::class);
$config1->expects($this->once())
->method('getDocumentNamespaces')
->will($this->returnValue(array('AcmeDemoBundle' => 'Acme\DemoBundle\Document')));
- $config2 = $this->getMock('Doctrine\ODM\MongoDB\Configuration', array(), array(), '', false);
+ $config2 = $this->createMock(OdmConfiguration::class);
$config2->expects($this->once())
->method('getDocumentNamespaces')
->will($this->returnValue(array('AcmeBlogBundle' => 'Acme\BlogBundle\Document')));
diff --git a/Tests/Doctrine/Hydration/DoctrineHydratorTest.php b/Tests/Doctrine/Hydration/DoctrineHydratorTest.php
index 2d5564ac..7e3c756e 100644
--- a/Tests/Doctrine/Hydration/DoctrineHydratorTest.php
+++ b/Tests/Doctrine/Hydration/DoctrineHydratorTest.php
@@ -3,10 +3,22 @@
namespace FS\SolrBundle\Tests\Doctrine\Hydration;
+use Doctrine\Common\Persistence\ManagerRegistry;
+use Doctrine\Common\Persistence\ObjectManager;
+use Doctrine\Common\Persistence\ObjectRepository;
+use FS\SolrBundle\Doctrine\Annotation\AnnotationReader;
+use FS\SolrBundle\Doctrine\Annotation\Field;
use FS\SolrBundle\Doctrine\Hydration\DoctrineHydrator;
+use FS\SolrBundle\Doctrine\Hydration\DoctrineHydratorInterface;
+use FS\SolrBundle\Doctrine\Hydration\DoctrineValueHydrator;
+use FS\SolrBundle\Doctrine\Hydration\ValueHydrator;
+use FS\SolrBundle\Doctrine\Mapper\MetaInformation;
use FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory;
+use FS\SolrBundle\Doctrine\Mapper\MetaInformationInterface;
use FS\SolrBundle\Tests\Doctrine\Mapper\SolrDocumentStub;
-use FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity;
+use FS\SolrBundle\Tests\Fixtures\ValidOdmTestDocument;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntity;
+use Symfony\Component\Validator\Constraints\Valid;
/**
* @group hydration
@@ -14,6 +26,16 @@
class DoctrineHydratorTest extends \PHPUnit_Framework_TestCase
{
+ /**
+ * @var AnnotationReader
+ */
+ private $reader;
+
+ public function setUp()
+ {
+ $this->reader = new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader());
+ }
+
/**
* @test
*/
@@ -21,29 +43,58 @@ public function foundEntityInDbReplacesEntityOldTargetEntity()
{
$fetchedFromDoctrine = new ValidTestEntity();
- $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository');
+ $repository = $this->createMock(ObjectRepository::class);
$repository->expects($this->once())
->method('find')
->with(1)
->will($this->returnValue($fetchedFromDoctrine));
$entity = new ValidTestEntity();
+ $entity->setId(1);
- $metainformations = new MetaInformationFactory();
+ $metainformations = new MetaInformationFactory($this->reader);
$metainformations = $metainformations->loadInformation($entity);
- $doctrineRegistry = $this->setupDoctrineRegistry($metainformations, $repository);
+ $ormManager = $this->setupManager($metainformations, $repository);
- $obj = new SolrDocumentStub(array());
- $obj->id = 1;
+ $obj = new SolrDocumentStub(array('id' => 'document_1'));
- $hydrator = $this->getMock('FS\SolrBundle\Doctrine\Hydration\Hydrator');
- $hydrator->expects($this->once())
- ->method('hydrate')
- ->with($obj, $metainformations)
+ $doctrine = new DoctrineHydrator(new ValueHydrator());
+ $doctrine->setOrmManager($ormManager);
+ $hydratedDocument = $doctrine->hydrate($obj, $metainformations);
+
+ $this->assertEntityFromDBReplcesTargetEntity($metainformations, $fetchedFromDoctrine, $hydratedDocument);
+ }
+
+ /**
+ * @test
+ */
+ public function useOdmManagerIfObjectIsOdmDocument()
+ {
+ $fetchedFromDoctrine = new ValidOdmTestDocument();
+
+ $odmRepository = $this->createMock(ObjectRepository::class);
+ $odmRepository->expects($this->once())
+ ->method('find')
+ ->with(1)
->will($this->returnValue($fetchedFromDoctrine));
- $doctrine = new DoctrineHydrator($doctrineRegistry, $hydrator);
+ $entity = new ValidOdmTestDocument();
+ $entity->setId(1);
+
+ $metainformations = new MetaInformationFactory($this->reader);
+ $metainformations = $metainformations->loadInformation($entity);
+
+ $ormManager = $this->createMock(ObjectManager::class);
+ $ormManager->expects($this->never())
+ ->method('getRepository');
+ $odmManager = $this->setupManager($metainformations, $odmRepository);
+
+ $obj = new SolrDocumentStub(array('id' => 'document_1'));
+
+ $doctrine = new DoctrineHydrator(new ValueHydrator());
+ $doctrine->setOdmManager($odmManager);
+ $doctrine->setOrmManager($ormManager);
$hydratedDocument = $doctrine->hydrate($obj, $metainformations);
$this->assertEntityFromDBReplcesTargetEntity($metainformations, $fetchedFromDoctrine, $hydratedDocument);
@@ -52,33 +103,70 @@ public function foundEntityInDbReplacesEntityOldTargetEntity()
/**
* @test
*/
- public function entityFromDbNotFoundShouldNotModifyMetainformations()
+ public function hydrationShouldOverwriteComplexTypes()
{
- $fetchedFromDoctrine = new ValidTestEntity();
+ $entity1 = new ValidTestEntity();
+ $entity1->setTitle('title 1');
+
+ $entity2 = new ValidTestEntity();
+ $entity2->setTitle('title 2');
+
+ $relations = array($entity1, $entity2);
+
+ $targetEntity = new ValidTestEntity();
+ $targetEntity->setId(1);
+ $targetEntity->setPosts($relations);
+
+ $metainformations = new MetaInformationFactory($this->reader);
+ $metainformations = $metainformations->loadInformation($targetEntity);
+
+ $repository = $this->createMock(ObjectRepository::class);
+ $repository->expects($this->once())
+ ->method('find')
+ ->with(1)
+ ->will($this->returnValue($targetEntity));
+
+ $ormManager = $this->setupManager($metainformations, $repository);
- $repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository');
+ $obj = new SolrDocumentStub(array(
+ 'id' => 'document_1',
+ 'posts_ss' => array('title 1', 'title 2')
+ ));
+
+ $doctrineHydrator = new DoctrineHydrator(new DoctrineValueHydrator());
+ $doctrineHydrator->setOrmManager($ormManager);
+
+ /** @var ValidTestEntity $hydratedEntity */
+ $hydratedEntity = $doctrineHydrator->hydrate($obj, $metainformations);
+
+ $this->assertEquals($relations, $hydratedEntity->getPosts());
+ }
+
+ /**
+ * @test
+ */
+ public function entityFromDbNotFoundShouldNotModifyMetainformations()
+ {
+ $repository = $this->createMock(ObjectRepository::class);
$repository->expects($this->once())
->method('find')
->with(1)
->will($this->returnValue(null));
$entity = new ValidTestEntity();
+ $entity->setId(1);
- $metainformations = new MetaInformationFactory();
+ $metainformations = new MetaInformationFactory($this->reader);
$metainformations = $metainformations->loadInformation($entity);
- $doctrineRegistry = $this->setupDoctrineRegistry($metainformations, $repository);
+ $ormManager = $this->setupManager($metainformations, $repository);
- $obj = new SolrDocumentStub(array());
- $obj->id = 1;
+ $obj = new SolrDocumentStub(array('id' => 'document_1'));
- $hydrator = $this->getMock('FS\SolrBundle\Doctrine\Hydration\Hydrator');
- $hydrator->expects($this->once())
- ->method('hydrate')
- ->with($obj, $metainformations)
- ->will($this->returnValue($fetchedFromDoctrine));
+ $hydrator = new ValueHydrator();
- $doctrine = new DoctrineHydrator($doctrineRegistry, $hydrator);
+ $doctrine = new DoctrineHydrator($hydrator);
+ $doctrine->setOrmManager($ormManager);
$hydratedDocument = $doctrine->hydrate($obj, $metainformations);
$this->assertEquals($metainformations->getEntity(), $entity);
@@ -87,9 +175,9 @@ public function entityFromDbNotFoundShouldNotModifyMetainformations()
}
/**
- * @param $metainformations
- * @param $fetchedFromDoctrine
- * @param $hydratedDocument
+ * @param MetaInformation $metainformations
+ * @param object $fetchedFromDoctrine
+ * @param object $hydratedDocument
*/
private function assertEntityFromDBReplcesTargetEntity($metainformations, $fetchedFromDoctrine, $hydratedDocument)
{
@@ -98,24 +186,25 @@ private function assertEntityFromDBReplcesTargetEntity($metainformations, $fetch
}
/**
- * @param $metainformations
+ * @param MetaInformationInterface $metainformations
* @param $repository
- * @return mixed
+ *
+ * @return \PHPUnit_Framework_MockObject_MockObject
*/
- private function setupDoctrineRegistry($metainformations, $repository)
+ private function setupManager($metainformations, $repository)
{
- $manager = $this->getMock('Doctrine\Common\Persistence\ObjectManager');
+ $manager = $this->createMock(ObjectManager::class);
$manager->expects($this->once())
->method('getRepository')
->with($metainformations->getClassName())
->will($this->returnValue($repository));
- $doctrineRegistry = $this->getMock('Symfony\Bridge\Doctrine\RegistryInterface');
- $doctrineRegistry->expects($this->once())
+ $managerRegistry = $this->createMock(ManagerRegistry::class);
+ $managerRegistry->expects($this->once())
->method('getManager')
->will($this->returnValue($manager));
- return $doctrineRegistry;
+ return $managerRegistry;
}
}
diff --git a/Tests/Doctrine/Hydration/DoctrineValueHydratorTest.php b/Tests/Doctrine/Hydration/DoctrineValueHydratorTest.php
new file mode 100644
index 00000000..e4c3a26a
--- /dev/null
+++ b/Tests/Doctrine/Hydration/DoctrineValueHydratorTest.php
@@ -0,0 +1,55 @@
+assertFalse($hydrator->mapValue('createdAt', array(), new MetaInformation()));
+ }
+
+ /**
+ * @test
+ */
+ public function skipObjects()
+ {
+ $hydrator = new DoctrineValueHydrator();
+
+ $field = new Field(array('type' => 'datetime'));
+ $field->name = 'createdAt';
+ $field->getter = 'format(\'Y-m-d\TH:i:s.z\Z\')';
+
+ $metaInformation = new MetaInformation();
+ $metaInformation->setFields(array($field));
+
+ $this->assertFalse($hydrator->mapValue('createdAt', new \DateTime(), $metaInformation));
+ }
+
+ /**
+ * @test
+ */
+ public function mapCommonType()
+ {
+ $hydrator = new DoctrineValueHydrator();
+
+ $field = new Field(array('type' => 'string'));
+ $field->name = 'title';
+
+ $metaInformation = new MetaInformation();
+ $metaInformation->setFields(array($field));
+
+ $this->assertTrue($hydrator->mapValue('title_s', 'a title', $metaInformation));
+ }
+}
diff --git a/Tests/Doctrine/Hydration/NoDatabaseValueHydratorTest.php b/Tests/Doctrine/Hydration/NoDatabaseValueHydratorTest.php
new file mode 100644
index 00000000..192e52c8
--- /dev/null
+++ b/Tests/Doctrine/Hydration/NoDatabaseValueHydratorTest.php
@@ -0,0 +1,37 @@
+ '0003115-2231_S',
+ 'title_t' => 'fooo_bar'
+ ));
+
+ $entity = new ValidTestEntity();
+
+ $metainformations = new MetaInformationFactory($reader);
+ $metainformations = $metainformations->loadInformation($entity);
+
+ $entity = $hydrator->hydrate($document, $metainformations);
+
+ $this->assertEquals('0003115-2231_S', $entity->getId());
+ $this->assertEquals('fooo_bar', $entity->getTitle());
+ }
+}
diff --git a/Tests/Doctrine/Hydration/ValueHydratorTest.php b/Tests/Doctrine/Hydration/ValueHydratorTest.php
index f798cffd..e06522ef 100644
--- a/Tests/Doctrine/Hydration/ValueHydratorTest.php
+++ b/Tests/Doctrine/Hydration/ValueHydratorTest.php
@@ -3,29 +3,47 @@
namespace FS\SolrBundle\Tests\Doctrine\Hydration;
+use Doctrine\Common\Collections\ArrayCollection;
+use FS\SolrBundle\Doctrine\Annotation\AnnotationReader;
use FS\SolrBundle\Doctrine\Hydration\ValueHydrator;
+use FS\SolrBundle\Doctrine\Hydration\ValueHydratorInterface;
use FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory;
use FS\SolrBundle\Tests\Doctrine\Mapper\SolrDocumentStub;
-use FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntity;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityWithCollection;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityWithRelation;
/**
* @group hydration
*/
class ValueHydratorTest extends \PHPUnit_Framework_TestCase
{
+ /**
+ * @var AnnotationReader
+ */
+ private $reader;
+
+ public function setUp()
+ {
+ $this->reader = new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader());
+ }
+
/**
* @test
*/
public function documentShouldMapToEntity()
{
$obj = new SolrDocumentStub(array(
- 'id' => 1,
- 'title_t' => 'foo'
+ 'id' => 'document_1',
+ 'title_t' => 'foo',
+ 'publish_date_s' => '10.10.2016',
+ 'field_s' => 'value 1234',
+ 'unknown_field_s' => 'value'
));
$entity = new ValidTestEntity();
- $metainformations = new MetaInformationFactory();
+ $metainformations = new MetaInformationFactory($this->reader);
$metainformations = $metainformations->loadInformation($entity);
$hydrator = new ValueHydrator();
@@ -34,6 +52,8 @@ public function documentShouldMapToEntity()
$this->assertTrue($hydratedDocument instanceof $entity);
$this->assertEquals(1, $entity->getId());
$this->assertEquals('foo', $entity->getTitle());
+ $this->assertEquals('10.10.2016', $entity->getPublishDate());
+ $this->assertEquals('value 1234', $entity->getField());
}
/**
@@ -42,13 +62,13 @@ public function documentShouldMapToEntity()
public function underscoreFieldBecomeCamelCase()
{
$obj = new SolrDocumentStub(array(
- 'id' => 1,
+ 'id' => 'document_1',
'created_at_d' => 12345
));
$entity = new ValidTestEntity();
- $metainformations = new MetaInformationFactory();
+ $metainformations = new MetaInformationFactory($this->reader);
$metainformations = $metainformations->loadInformation($entity);
$hydrator = new ValueHydrator();
@@ -58,5 +78,60 @@ public function underscoreFieldBecomeCamelCase()
$this->assertEquals(1, $entity->getId());
$this->assertEquals(12345, $entity->getCreatedAt());
}
+
+ /**
+ * @test
+ */
+ public function doNotOverwriteComplexTypes_Collection()
+ {
+ $obj = new SolrDocumentStub(array(
+ 'id' => 'document_1',
+ 'title_t' => 'foo',
+ 'collection_ss' => array('title 1', 'title 2')
+ ));
+
+ $entity = new ValidTestEntityWithCollection();
+
+ $metainformations = new MetaInformationFactory($this->reader);
+ $metainformations = $metainformations->loadInformation($entity);
+
+ $hydrator = new ValueHydrator();
+ $hydratedDocument = $hydrator->hydrate($obj, $metainformations);
+
+ $this->assertTrue($hydratedDocument instanceof $entity);
+ $this->assertEquals(1, $entity->getId());
+ $this->assertEquals('foo', $entity->getTitle());
+ $this->assertEquals(array('title 1', 'title 2'), $entity->getCollection());
+ }
+
+ /**
+ * @test
+ */
+ public function doNotOverwriteComplexTypes_Relation()
+ {
+ $obj = new SolrDocumentStub(array(
+ 'id' => 'document_1',
+ 'title_t' => 'foo',
+ 'posts_ss' => array('title 1', 'title2')
+ ));
+
+ $entity1 = new ValidTestEntity();
+ $entity1->setTitle('title 1');
+
+ $entity = new ValidTestEntityWithRelation();
+ $entity->setRelation($entity1);
+
+ $metainformations = new MetaInformationFactory($this->reader);
+ $metainformations = $metainformations->loadInformation($entity);
+
+ $hydrator = new ValueHydrator();
+ $hydratedDocument = $hydrator->hydrate($obj, $metainformations);
+
+ $this->assertTrue($hydratedDocument instanceof $entity);
+ $this->assertEquals(1, $entity->getId());
+ $this->assertEquals('foo', $entity->getTitle());
+
+ $this->assertTrue($hydratedDocument->getRelation() === $entity1);
+ }
}
\ No newline at end of file
diff --git a/Tests/Doctrine/Mapper/EntityMapperObjectRelationTest.php b/Tests/Doctrine/Mapper/EntityMapperObjectRelationTest.php
new file mode 100644
index 00000000..9d366ee7
--- /dev/null
+++ b/Tests/Doctrine/Mapper/EntityMapperObjectRelationTest.php
@@ -0,0 +1,376 @@
+doctrineHydrator = $this->createMock(HydratorInterface::class);
+ $this->indexHydrator = $this->createMock(HydratorInterface::class);
+ $this->metaInformationFactory = new MetaInformationFactory(new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader()));
+
+ $this->mapper = new EntityMapper($this->doctrineHydrator, $this->indexHydrator, $this->metaInformationFactory);
+ }
+
+ /**
+ * @test
+ */
+ public function mapRelationFieldByGetter()
+ {
+ $collectionItem1 = new NestedEntity();
+ $collectionItem1->setId(uniqid());
+ $collectionItem1->setName('title 1');
+
+ $collectionItem2 = new NestedEntity();
+ $collectionItem2->setId(uniqid());
+ $collectionItem2->setName('title 2');
+
+ $collection = new ArrayCollection([$collectionItem1, $collectionItem2]);
+
+ $entity = new EntityNestedProperty();
+ $entity->setId(uniqid());
+ $entity->setCollectionValidGetter($collection);
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $document = $this->mapper->toDocument($metaInformation);
+
+ $this->assertArrayHasKey('_childDocuments_', $document->getFields());
+ $collectionField = $document->getFields()['_childDocuments_'];
+
+ $this->assertCollectionItemsMappedProperly($collectionField, 1);
+ }
+
+ /**
+ * @test
+ */
+ public function doNotIndexEmptyNestedCollection()
+ {
+ $collection = new ArrayCollection([]);
+
+ $entity = new EntityNestedProperty();
+ $entity->setId(uniqid());
+ $entity->setCollectionValidGetter($collection);
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $document = $this->mapper->toDocument($metaInformation);
+
+ $this->assertArrayNotHasKey('_childDocuments_', $document->getFields());
+ }
+
+ /**
+ * @test
+ * @expectedException \FS\SolrBundle\Doctrine\Mapper\SolrMappingException
+ * @expectedExceptionMessage No method "unknown()" found in class "FS\SolrBundle\Tests\Fixtures\EntityNestedProperty"
+ */
+ public function throwExceptionIfConfiguredGetterDoesNotExists()
+ {
+ $collection = new ArrayCollection([new \DateTime(), new \DateTime()]);
+
+ $entity = new EntityNestedProperty();
+ $entity->setId(uniqid());
+ $entity->setCollectionInvalidGetter($collection);
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $this->mapper->toDocument($metaInformation);
+ }
+
+ /**
+ * @test
+ */
+ public function mapRelationFieldAllFields()
+ {
+ $collectionItem1 = new NestedEntity();
+ $collectionItem1->setId(uniqid());
+ $collectionItem1->setName('title 1');
+
+ $collectionItem2 = new NestedEntity();
+ $collectionItem2->setId(uniqid());
+ $collectionItem2->setName('title 2');
+
+ $collection = [$collectionItem1, $collectionItem2];
+
+ $entity = new EntityNestedProperty();
+ $entity->setId(uniqid());
+ $entity->setCollection($collection);
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $document = $this->mapper->toDocument($metaInformation);
+
+ $this->assertArrayHasKey('_childDocuments_', $document->getFields());
+ $collectionField = $document->getFields()['_childDocuments_'];
+
+ $this->assertCollectionItemsMappedProperly($collectionField, 2);
+ }
+
+ /**
+ * @test
+ */
+ public function mapEntityWithRelation_singleObject()
+ {
+ $entity = new EntityNestedProperty();
+ $entity->setId(uniqid());
+
+ $nested1 = new NestedEntity();
+ $nested1->setId(uniqid());
+ $nested1->setName('nested document');
+
+ $entity->setNestedProperty($nested1);
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $document = $this->mapper->toDocument($metaInformation);
+
+ $fields = $document->getFields();
+
+ $this->assertArrayHasKey('_childDocuments_', $fields);
+
+ $subDocument = $fields['_childDocuments_'][0];
+
+ $this->assertArrayHasKey('id', $subDocument);
+ $this->assertArrayHasKey('name_t', $subDocument);
+ }
+
+ /**
+ * @test
+ */
+ public function indexEntityMultipleRelations()
+ {
+ $entity = new EntityNestedProperty();
+ $entity->setId(uniqid());
+
+ $nested1 = new NestedEntity();
+ $nested1->setId(uniqid());
+ $nested1->setName('nested document');
+
+ $entity->setNestedProperty($nested1);
+
+ $collectionItem1 = new NestedEntity();
+ $collectionItem1->setId(uniqid());
+ $collectionItem1->setName('collection item 1');
+
+ $collectionItem2 = new NestedEntity();
+ $collectionItem2->setId(uniqid());
+ $collectionItem2->setName('collection item 2');
+
+ $collection = new ArrayCollection([$collectionItem1, $collectionItem2]);
+
+ $entity->setCollection($collection);
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $document = $this->mapper->toDocument($metaInformation);
+
+ $fields = $document->getFields();
+
+ $this->assertEquals(3, count($fields['_childDocuments_']));
+ }
+
+ /**
+ * @test
+ */
+ public function mapRelationField_Getter()
+ {
+ $entity = new EntityNestedProperty();
+ $entity->setId(uniqid());
+
+ $object = new NestedEntity();
+ $object->setId(uniqid());
+ $object->setName('nested entity');
+
+ $entity->setSimpleGetter($object);
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $document = $this->mapper->toDocument($metaInformation);
+
+ $this->assertArrayHasKey('simple_getter_s', $document->getFields());
+
+ $collectionField = $document->getFields()['simple_getter_s'];
+
+ $this->assertEquals('nested entity', $collectionField);
+ }
+
+ /**
+ * @test
+ */
+ public function callGetterWithParameter_ObjectProperty()
+ {
+ $date = new \DateTime();
+
+ $entity = new EntityNestedProperty();
+ $entity->setId(uniqid());
+ $entity->setGetterWithParameters($date);
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $fields = $metaInformation->getFields();
+ $metaInformation->setFields($fields);
+
+ $document = $this->mapper->toDocument($metaInformation);
+
+ $fields = $document->getFields();
+ $this->assertArrayHasKey('getter_with_parameters_dt', $fields);
+ $this->assertEquals($date->format('d.m.Y'), $fields['getter_with_parameters_dt']);
+ }
+
+ /**
+ * @test
+ */
+ public function callGetterWithParameters_ObjectProperty()
+ {
+ $entity1 = new ValidTestEntity();
+
+ $metaInformation = MetaTestInformationFactory::getMetaInformation($entity1);
+ $metaInformation->setFields(array(
+ new Field(array('name' => 'test_field', 'type' => 'datetime', 'boost' => '1', 'value' => new TestObject(), 'getter' => "testGetter('string3', 'string1', 'string')"))
+ ));
+
+ $fields = $metaInformation->getFields();
+ $metaInformation->setFields($fields);
+
+ $document = $this->mapper->toDocument($metaInformation);
+
+ $fields = $document->getFields();
+
+ $this->assertArrayHasKey('test_field_dt', $fields);
+ $this->assertEquals(array('string3', 'string1', 'string'), $fields['test_field_dt']);
+ }
+
+ /**
+ * @test
+ */
+ public function callGetterWithParameter_SimpleProperty()
+ {
+ $data = ['key' => 'value'];
+
+ $date = new \DateTime();
+ $entity1 = new ValidTestEntity();
+ $entity1->setId(uniqid());
+ $entity1->setComplexDataType(json_encode($data));
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity1);
+
+ $document = $this->mapper->toDocument($metaInformation);
+
+ $fields = $document->getFields();
+
+ $this->assertArrayHasKey('complex_data_type', $fields);
+
+ $this->assertEquals($data, $fields['complex_data_type']);
+ }
+
+ /**
+ * @test
+ * @expectedException \FS\SolrBundle\Doctrine\Mapper\SolrMappingException
+ * @expectedExceptionMessage The configured getter "asString" in "FS\SolrBundle\Tests\Doctrine\Mapper\TestObject" must return a string or array, got object
+ */
+ public function callGetterWithObjectAsReturnValue()
+ {
+ $entity1 = new ValidTestEntity();
+
+ $metaInformation = MetaTestInformationFactory::getMetaInformation($entity1);
+ $metaInformation->setFields(array(
+ new Field(array('name' => 'test_field', 'type' => 'datetime', 'boost' => '1', 'value' => new TestObject(), 'getter' => "asString"))
+ ));
+
+ $fields = $metaInformation->getFields();
+ $metaInformation->setFields($fields);
+
+ $this->mapper->toDocument($metaInformation);
+ }
+
+ /**
+ * @test
+ */
+ public function callGetterToRetrieveFieldValue()
+ {
+ $metainformation = $this->metaInformationFactory->loadInformation(new TestObject());
+
+ $document = $this->mapper->toDocument($metainformation);
+
+ $fields = $document->getFields();
+
+ $this->assertArrayHasKey('property_s', $fields);
+ $this->assertEquals(1234, $fields['property_s']);
+ }
+
+ /**
+ * @param array $collectionField
+ * @param int $expectedItems
+ */
+ private function assertCollectionItemsMappedProperly($collectionField, $expectedItems)
+ {
+ $this->assertEquals($expectedItems, count($collectionField), 'should be 2 collection items');
+
+ foreach ($collectionField as $item) {
+ $this->assertArrayHasKey('id', $item);
+ $this->assertArrayHasKey('name_t', $item);
+ $this->assertEquals(2, count($item), 'field has 2 properties');
+ }
+ }
+}
+
+/** @Solr\Document() */
+class TestObject {
+
+ /** @Solr\Id */
+ private $id;
+
+ public function __construct()
+ {
+ $this->id = uniqid();
+ }
+
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /** @Solr\Field(type="string", name="property") */
+ public function getPropertyValue()
+ {
+ return 1234;
+ }
+
+ public function testGetter($para1, $para2, $para3)
+ {
+ return array($para1, $para2, $para3);
+ }
+
+ public function asString()
+ {
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/Tests/Doctrine/Mapper/EntityMapperTest.php b/Tests/Doctrine/Mapper/EntityMapperTest.php
index 38dec220..7cd18916 100644
--- a/Tests/Doctrine/Mapper/EntityMapperTest.php
+++ b/Tests/Doctrine/Mapper/EntityMapperTest.php
@@ -2,10 +2,26 @@
namespace FS\SolrBundle\Tests\Doctrine\Mapper;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Persistence\ManagerRegistry;
+use Doctrine\Common\Persistence\ObjectManager;
+use Doctrine\Common\Persistence\ObjectRepository;
use FS\SolrBundle\Doctrine\Annotation\AnnotationReader;
+use FS\SolrBundle\Doctrine\Hydration\DoctrineHydrator;
use FS\SolrBundle\Doctrine\Hydration\HydrationModes;
+use FS\SolrBundle\Doctrine\Hydration\HydratorInterface;
+use FS\SolrBundle\Doctrine\Hydration\IndexHydrator;
+use FS\SolrBundle\Doctrine\Hydration\NoDatabaseValueHydrator;
+use FS\SolrBundle\Doctrine\Hydration\ValueHydrator;
use FS\SolrBundle\Doctrine\Mapper\EntityMapper;
-use FS\SolrBundle\Doctrine\Mapper\Mapping\MapAllFieldsCommand;
+use FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory;
+use FS\SolrBundle\Doctrine\Annotation\Field;
+use FS\SolrBundle\Tests\Fixtures\EntityWithCustomId;
+use FS\SolrBundle\Tests\Fixtures\PartialUpdateEntity;
+use FS\SolrBundle\Tests\Fixtures\ValidOdmTestDocument;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntity;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityWithCollection;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityWithRelation;
use FS\SolrBundle\Tests\Util\MetaTestInformationFactory;
use Solarium\QueryType\Update\Query\Document\Document;
@@ -19,31 +35,48 @@ class EntityMapperTest extends \PHPUnit_Framework_TestCase
private $doctrineHydrator = null;
private $indexHydrator = null;
- public function setUp()
- {
- $this->doctrineHydrator = $this->getMock('FS\SolrBundle\Doctrine\Hydration\Hydrator');
- $this->indexHydrator = $this->getMock('FS\SolrBundle\Doctrine\Hydration\Hydrator');
- }
+ /**
+ * @var MetaInformationFactory
+ */
+ private $metaInformationFactory;
+
+ /**
+ * @var EntityMapper
+ */
+ private $mapper;
- public function testToDocument_EntityMayNotIndexed()
+ public function setUp()
{
- $mapper = new \FS\SolrBundle\Doctrine\Mapper\EntityMapper($this->doctrineHydrator, $this->indexHydrator);
+ $this->doctrineHydrator = $this->createMock(HydratorInterface::class);
+ $this->indexHydrator = $this->createMock(HydratorInterface::class);
+ $this->metaInformationFactory = new MetaInformationFactory(new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader()));
- $actual = $mapper->toDocument(MetaTestInformationFactory::getMetaInformation());
- $this->assertNull($actual);
+ $this->mapper = new EntityMapper($this->doctrineHydrator, $this->indexHydrator, $this->metaInformationFactory);
}
public function testToDocument_DocumentIsUpdated()
{
- $mapper = new \FS\SolrBundle\Doctrine\Mapper\EntityMapper($this->doctrineHydrator, $this->indexHydrator);
- $mapper->setMappingCommand(new MapAllFieldsCommand(new AnnotationReader()));
- $actual = $mapper->toDocument(MetaTestInformationFactory::getMetaInformation());
+ $actual = $this->mapper->toDocument(MetaTestInformationFactory::getMetaInformation());
$this->assertTrue($actual instanceof Document);
$this->assertNotNull($actual->id);
}
+ /**
+ * @test
+ */
+ public function setFieldModifier()
+ {
+ $entity = new PartialUpdateEntity();
+ $entity->setId(uniqid());
+
+ $actualDocument = $this->mapper->toDocument($this->metaInformationFactory->loadInformation($entity));
+
+ $this->assertEquals('set', $actualDocument->getFieldModifier('subtitle'));
+ $this->assertNull($actualDocument->getFieldModifier('title'));
+ }
+
public function testToEntity_WithDocumentStub_HydrateIndexOnly()
{
$targetEntity = new ValidTestEntity();
@@ -55,41 +88,175 @@ public function testToEntity_WithDocumentStub_HydrateIndexOnly()
$this->doctrineHydrator->expects($this->never())
->method('hydrate');
- $mapper = new \FS\SolrBundle\Doctrine\Mapper\EntityMapper($this->doctrineHydrator, $this->indexHydrator);
- $mapper->setHydrationMode(HydrationModes::HYDRATE_INDEX);
- $entity = $mapper->toEntity(new SolrDocumentStub(), $targetEntity);
+ $this->mapper->setHydrationMode(HydrationModes::HYDRATE_INDEX);
+ $entity = $this->mapper->toEntity(new SolrDocumentStub(), $targetEntity);
$this->assertTrue($entity instanceof $targetEntity);
}
- public function testToEntity_ConcreteDocumentClass_WithDoctrine()
+ public function testToEntity_ConcreteDocumentClass_WithDoctrineOrm()
{
$targetEntity = new ValidTestEntity();
+ $targetEntity->setField('a value');
- $this->indexHydrator->expects($this->once())
- ->method('hydrate')
- ->will($this->returnValue($targetEntity));
+ $this->indexHydrator = new IndexHydrator(new NoDatabaseValueHydrator());
- $this->doctrineHydrator->expects($this->once())
- ->method('hydrate')
- ->will($this->returnValue($targetEntity));
+ $this->doctrineHydrator = new DoctrineHydrator(new ValueHydrator());
+ $this->doctrineHydrator->setOrmManager($this->setupOrmManager($targetEntity, 1));
+
+ $this->mapper = new EntityMapper($this->doctrineHydrator, $this->indexHydrator, $this->metaInformationFactory);
+ $this->mapper->setHydrationMode(HydrationModes::HYDRATE_DOCTRINE);
+ $entity = $this->mapper->toEntity(new Document(array('id' => 'document_1', 'title' => 'value from index')), $targetEntity);
- $mapper = new \FS\SolrBundle\Doctrine\Mapper\EntityMapper($this->doctrineHydrator, $this->indexHydrator);
- $mapper->setHydrationMode(HydrationModes::HYDRATE_DOCTRINE);
- $entity = $mapper->toEntity(new Document(array()), $targetEntity);
+ $this->assertTrue($entity instanceof $targetEntity);
+
+ $this->assertEquals('a value', $entity->getField());
+ $this->assertEquals('value from index', $entity->getTitle());
+ }
+
+ public function testToEntity_ConcreteDocumentClass_WithDoctrineOdm()
+ {
+ $targetEntity = new ValidOdmTestDocument();
+ $targetEntity->setField('a value');
+
+ $this->indexHydrator = new IndexHydrator(new NoDatabaseValueHydrator());
+
+ $this->doctrineHydrator = new DoctrineHydrator(new ValueHydrator());
+ $this->doctrineHydrator->setOdmManager($this->setupOdmManager($targetEntity, 1));
+
+ $this->mapper = new EntityMapper($this->doctrineHydrator, $this->indexHydrator, $this->metaInformationFactory);
+ $this->mapper->setHydrationMode(HydrationModes::HYDRATE_DOCTRINE);
+ $entity = $this->mapper->toEntity(new Document(array('id' => 'document_1', 'title' => 'value from index')), $targetEntity);
$this->assertTrue($entity instanceof $targetEntity);
+
+ $this->assertEquals('a value', $entity->getField());
+ $this->assertEquals('value from index', $entity->getTitle());
}
- public function ToCamelCase()
+ /**
+ * @test
+ * @expectedException \FS\SolrBundle\Doctrine\Mapper\SolrMappingException
+ * @expectedExceptionMessage Please check your config. Given entity is not a Doctrine entity, but Doctrine hydration is enabled. Use setHydrationMode(HydrationModes::HYDRATE_DOCTRINE) to fix this.
+ */
+ public function throwExceptionIfGivenObjectIsNotEntityButItShould()
{
- $mapper = new EntityMapper($this->doctrineHydrator, $this->indexHydrator);
+ $targetEntity = new PlainObject();
+
+ $this->indexHydrator = new IndexHydrator(new NoDatabaseValueHydrator());
- $meta = new \ReflectionClass($mapper);
- $method = $meta->getMethod('toCamelCase');
- $method->setAccessible(true);
- $calmelCased = $method->invoke($mapper, 'test_underline');
- $this->assertEquals('testUnderline', $calmelCased);
+ $this->doctrineHydrator = new DoctrineHydrator(new ValueHydrator());
+
+ $this->mapper->toEntity(new Document(array('id' => 'document_1', 'title' => 'value from index')), $targetEntity);
+ }
+
+ /**
+ * @test
+ */
+ public function generatedDocumentIdIfRequired()
+ {
+ $entity = new EntityWithCustomId();
+
+ $this->indexHydrator = new IndexHydrator(new NoDatabaseValueHydrator());
+
+ $this->doctrineHydrator = new DoctrineHydrator(new ValueHydrator());
+
+ $metainformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $this->mapper = new EntityMapper($this->doctrineHydrator, $this->indexHydrator, $this->metaInformationFactory);
+ $document = $this->mapper->toDocument($metainformation);
+
+ $fields = $document->getFields();
+ $this->assertArrayHasKey('id', $fields);
+ $this->assertNotNull($fields['id']);
+ }
+
+ public function testMapEntity_DocumentShouldContainThreeFields()
+ {
+ $document = $this->mapper->toDocument(MetaTestInformationFactory::getMetaInformation());
+
+ $this->assertTrue($document instanceof Document, 'is a Document');
+ $this->assertEquals(4, $document->count(), 'three fields are mapped');
+
+ $this->assertEquals(1, $document->getBoost(), 'document boost should be 1');
+
+ $boostTitleField = $document->getFieldBoost('title');
+ $this->assertEquals(1.8, $boostTitleField, 'boost value of field title_s should be 1.8');
+
+ $this->assertArrayHasKey('id', $document);
+ $this->assertArrayHasKey('title', $document);
+ $this->assertArrayHasKey('text_t', $document);
+ $this->assertArrayHasKey('created_at_dt', $document);
+ }
+
+ /**
+ * @test
+ * @expectedException \FS\SolrBundle\Doctrine\Mapper\SolrMappingException
+ * @expectedExceptionMessage No entity id set for "FS\SolrBundle\Tests\Fixtures\ValidTestEntity"
+ */
+ public function throwExceptionIfEntityHasNoId()
+ {
+ $entity = new ValidTestEntity;
+
+ $metaInformation = $this->metaInformationFactory->loadInformation($entity);
+
+ $this->mapper->toDocument($metaInformation);
+ }
+
+ private function setupOrmManager($entity, $expectedEntityId)
+ {
+ $repository = $this->createMock(ObjectRepository::class);
+ $repository->expects($this->once())
+ ->method('find')
+ ->with($expectedEntityId)
+ ->will($this->returnValue($entity));
+
+ $manager = $this->createMock(ObjectManager::class);
+ $manager->expects($this->once())
+ ->method('getRepository')
+ ->will($this->returnValue($repository));
+
+ $managerRegistry = $this->createMock(ManagerRegistry::class);
+ $managerRegistry->expects($this->once())
+ ->method('getManager')
+ ->will($this->returnValue($manager));
+
+ return $managerRegistry;
+ }
+
+ private function setupOdmManager($entity, $expectedEntityId)
+ {
+ $repository = $this->createMock(ObjectRepository::class);
+ $repository->expects($this->once())
+ ->method('find')
+ ->with($expectedEntityId)
+ ->will($this->returnValue($entity));
+
+ $manager = $this->createMock(ObjectManager::class);
+ $manager->expects($this->once())
+ ->method('getRepository')
+ ->will($this->returnValue($repository));
+
+ $managerRegistry = $this->createMock(ManagerRegistry::class);
+ $managerRegistry->expects($this->once())
+ ->method('getManager')
+ ->will($this->returnValue($manager));
+
+ return $managerRegistry;
}
}
+use FS\SolrBundle\Doctrine\Annotation as Solr;
+
+/**
+ * @Solr\Document(boost="1")
+ */
+class PlainObject
+{
+ /**
+ * @var int
+ *
+ * @Solr\Id
+ */
+ private $id;
+}
\ No newline at end of file
diff --git a/Tests/Doctrine/Mapper/Mapping/MapAllFieldsCommandTest.php b/Tests/Doctrine/Mapper/Mapping/MapAllFieldsCommandTest.php
deleted file mode 100644
index 85a6ec55..00000000
--- a/Tests/Doctrine/Mapper/Mapping/MapAllFieldsCommandTest.php
+++ /dev/null
@@ -1,37 +0,0 @@
-createDocument(MetaTestInformationFactory::getMetaInformation());
-
- $this->assertTrue($actual instanceof Document, 'is a Document');
- $this->assertFieldCount(3, $actual, 'three fields are mapped');
-
- $this->assertEquals(1, $actual->getBoost(), 'document boost should be 1');
-
- $boostTitleField = $actual->getFieldBoost('title_s');
- $this->assertEquals(1.8, $boostTitleField, 'boost value of field title_s should be 1.8');
-
- $this->assertHasDocumentFields($actual, self::$MAPPED_FIELDS);
- }
-}
-
diff --git a/Tests/Doctrine/Mapper/Mapping/MapIdentifierCommandTest.php b/Tests/Doctrine/Mapper/Mapping/MapIdentifierCommandTest.php
deleted file mode 100644
index 1292c2f6..00000000
--- a/Tests/Doctrine/Mapper/Mapping/MapIdentifierCommandTest.php
+++ /dev/null
@@ -1,29 +0,0 @@
-createDocument(MetaTestInformationFactory::getMetaInformation());
-
- $this->assertEquals(2, $document->count(), 'fieldcount is two');
- $this->assertEquals(2, $document->id, 'id is 2');
-
- }
-
-}
-
diff --git a/Tests/Doctrine/Mapper/Mapping/SolrDocumentTest.php b/Tests/Doctrine/Mapper/Mapping/SolrDocumentTest.php
deleted file mode 100644
index d819880e..00000000
--- a/Tests/Doctrine/Mapper/Mapping/SolrDocumentTest.php
+++ /dev/null
@@ -1,23 +0,0 @@
-getFields();
- foreach ($expectedFields as $expectedField) {
- $this->assertTrue(array_key_exists($expectedField, $actualFields), 'field' . $expectedField . ' not in document');
- }
- }
-
- protected function assertFieldCount($expectedCount, Document $document, $message = '')
- {
- $this->assertEquals($expectedCount + self::FIELDS_ALWAYS_MAPPED, $document->count(), $message);
- }
-}
-
diff --git a/Tests/Doctrine/Mapper/MetaInformationFactoryTest.php b/Tests/Doctrine/Mapper/MetaInformationFactoryTest.php
index a98c9a3c..6fadad25 100644
--- a/Tests/Doctrine/Mapper/MetaInformationFactoryTest.php
+++ b/Tests/Doctrine/Mapper/MetaInformationFactoryTest.php
@@ -1,9 +1,18 @@
reader = new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader());
+ }
+
private function getClassnameResolver($namespace)
{
- $doctrineConfiguration = $this->getMock('FS\SolrBundle\Doctrine\ClassnameResolver\ClassnameResolver', array(), array(), '', false);
+ $doctrineConfiguration = $this->createMock(ClassnameResolver::class);
$doctrineConfiguration->expects($this->any())
->method('resolveFullQualifiedClassname')
->will($this->returnValue($namespace));
@@ -23,7 +42,7 @@ private function getClassnameResolver($namespace)
private function getClassnameResolverCouldNotResolveClassname()
{
- $doctrineConfiguration = $this->getMock('FS\SolrBundle\Doctrine\ClassnameResolver\ClassnameResolver', array(), array(), '', false);
+ $doctrineConfiguration = $this->createMock(ClassnameResolver::class);
$doctrineConfiguration->expects($this->any())
->method('resolveFullQualifiedClassname')
->will($this->throwException(new ClassnameResolverException('could not resolve classname for entity')));
@@ -38,17 +57,17 @@ public function testLoadInformation_ShouldLoadAll()
$expectedDocumentName = 'validtestentity';
- $classnameResolver = $this->getClassnameResolver('FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity');
+ $classnameResolver = $this->getClassnameResolver(ValidTestEntity::class);
- $factory = new MetaInformationFactory();
+ $factory = new MetaInformationFactory($this->reader);
$factory->setClassnameResolver($classnameResolver);
$actual = $factory->loadInformation('FSBlogBundle:ValidTestEntity');
$this->assertTrue($actual instanceof MetaInformation);
$this->assertEquals($expectedClassName, $actual->getClassName(), 'wrong classname');
$this->assertEquals($expectedDocumentName, $actual->getDocumentName(), 'wrong documentname');
- $this->assertEquals(4, count($actual->getFields()), '4 fields are set');
- $this->assertEquals(5, count($actual->getFieldMapping()), '5 fields are mapped');
+ $this->assertEquals(5, count($actual->getFields()), '5 fields are set');
+ $this->assertEquals(6, count($actual->getFieldMapping()), '5 fields are mapped');
}
public function testLoadInformation_LoadInformationFromObject()
@@ -58,49 +77,54 @@ public function testLoadInformation_LoadInformationFromObject()
$expectedDocumentName = 'validtestentity';
- $doctrineConfiguration = $this->getClassnameResolver('FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity');
+ $doctrineConfiguration = $this->getClassnameResolver(ValidTestEntity::class);
- $factory = new MetaInformationFactory();
+ $factory = new MetaInformationFactory($this->reader);
$factory->setClassnameResolver($doctrineConfiguration);
$actual = $factory->loadInformation($testEntity);
$this->assertTrue($actual instanceof MetaInformation);
$this->assertEquals($expectedClassName, $actual->getClassName(), 'wrong classname');
$this->assertEquals($expectedDocumentName, $actual->getDocumentName(), 'wrong documentname');
- $this->assertEquals(4, count($actual->getFields()), '4 fields are mapped');
+ $this->assertEquals(5, count($actual->getFields()), '5 fields are mapped');
+
+ $this->assertTrue($actual->hasField('title'), 'field should be able to located by field-name');
+ $this->assertTrue($actual->hasField('text_t'), 'field should be able to located by field-name with suffix');
+
+ $this->assertTrue($actual->getField('title') instanceof Field);
}
/**
- * @expectedException RuntimeException
+ * @expectedException \FS\SolrBundle\Doctrine\Mapper\SolrMappingException
* @expectedExceptionMessage no declaration for document found in entity
*/
public function testLoadInformation_EntityHasNoDocumentDeclaration_ShouldThrowException()
{
- $doctrineConfiguration = $this->getClassnameResolver('FS\SolrBundle\Tests\Doctrine\Mapper\NotIndexedEntity');
+ $doctrineConfiguration = $this->getClassnameResolver(NotIndexedEntity::class);
- $factory = new MetaInformationFactory();
+ $factory = new MetaInformationFactory($this->reader);
$factory->setClassnameResolver($doctrineConfiguration);
$factory->loadInformation('FSBlogBundle:NotIndexedEntity');
}
/**
- * @expectedException FS\SolrBundle\Doctrine\ClassnameResolver\ClassnameResolverException
+ * @expectedException \FS\SolrBundle\Doctrine\ClassnameResolver\ClassnameResolverException
* @expectedExceptionMessage could not resolve classname for entity
*/
public function testLoadInformation_EntityDoesNoExists()
{
$doctrineConfiguration = $this->getClassnameResolverCouldNotResolveClassname();
- $factory = new MetaInformationFactory();
+ $factory = new MetaInformationFactory($this->reader);
$factory->setClassnameResolver($doctrineConfiguration);
$factory->loadInformation('FSBlogBundle:UnknownEntity');
}
public function testLoadInformation_FromObject()
{
- $doctrineConfiguration = $this->getClassnameResolver('FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity');
+ $doctrineConfiguration = $this->getClassnameResolver(ValidTestEntity::class);
- $factory = new MetaInformationFactory();
+ $factory = new MetaInformationFactory($this->reader);
$factory->setClassnameResolver($doctrineConfiguration);
$testEntity = new ValidTestEntity();
@@ -112,9 +136,9 @@ public function testLoadInformation_FromObject()
public function testLoadInformation_FromFullClassname()
{
- $doctrineConfiguration = $this->getClassnameResolver('FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity');
+ $doctrineConfiguration = $this->getClassnameResolver(ValidTestEntity::class);
- $factory = new MetaInformationFactory();
+ $factory = new MetaInformationFactory($this->reader);
$factory->setClassnameResolver($doctrineConfiguration);
$entityClassname = get_class(new ValidTestEntity());
@@ -123,5 +147,60 @@ public function testLoadInformation_FromFullClassname()
$expected = $entityClassname;
$this->assertEquals($expected, $informations->getClassName(), 'class from fullclassname not discovered');
}
+
+ /**
+ * @test
+ */
+ public function determineDoctrineMapperTypeFromEntity()
+ {
+ $factory = new MetaInformationFactory($this->reader);
+ $metainformation = $factory->loadInformation(new ValidTestEntity());
+
+ $this->assertEquals(MetaInformationInterface::DOCTRINE_MAPPER_TYPE_RELATIONAL, $metainformation->getDoctrineMapperType());
+ }
+
+ /**
+ * @test
+ */
+ public function determineDoctrineMapperTypeFromDocument()
+ {
+ $factory = new MetaInformationFactory($this->reader);
+ $metainformation = $factory->loadInformation(new ValidOdmTestDocument());
+
+ $this->assertEquals(MetaInformationInterface::DOCTRINE_MAPPER_TYPE_DOCUMENT, $metainformation->getDoctrineMapperType());
+ }
+
+ /**
+ * @test
+ */
+ public function useCachedEntityInstanceIfItIsSet()
+ {
+ $factory = new MetaInformationFactory($this->reader);
+ $metainformation1 = $factory->loadInformation(new ValidTestEntity());
+ $metainformation2 = $factory->loadInformation(new ValidTestEntity());
+
+ $this->assertEquals($metainformation1->getEntity(), $metainformation2->getEntity());
+ }
+
+ /**
+ * @test
+ */
+ public function includeNestedFieldsInFieldmapping()
+ {
+ $entity = new EntityNestedProperty();
+
+ $nested1 = new NestedEntity();
+ $nested2 = new NestedEntity();
+ $entity->setCollection([$nested1, $nested2]);
+
+ $factory = new MetaInformationFactory($this->reader);
+ $metainformation = $factory->loadInformation($entity);
+
+ $this->assertArrayNotHasKey('collection', $metainformation->getFieldMapping());
+ $this->assertArrayHasKey('collection.id', $metainformation->getFieldMapping());
+ $this->assertArrayHasKey('collection.name_t', $metainformation->getFieldMapping());
+
+
+ }
}
diff --git a/Tests/Doctrine/Mapper/MetaInformationTest.php b/Tests/Doctrine/Mapper/MetaInformationTest.php
index 339d9fdf..f50e2ab4 100644
--- a/Tests/Doctrine/Mapper/MetaInformationTest.php
+++ b/Tests/Doctrine/Mapper/MetaInformationTest.php
@@ -18,55 +18,6 @@ private function createFieldObject($name, $value)
return $value;
}
- public function testHasField_FieldExists()
- {
- $value1 = $this->createFieldObject('field1', 'oldfieldvalue');
- $value2 = $this->createFieldObject('field2', true);
-
- $fields = array(
- 'field1' => $value1,
- 'field2' => $value2
- );
-
- $information = new MetaInformation();
- $information->setFields($fields);
-
- $this->assertTrue($information->hasField('field2'), 'metainformation should have field2');
- }
-
- public function testHasField_FieldNotExists()
- {
- $value1 = $this->createFieldObject('field1', 'oldfieldvalue');
-
- $fields = array(
- 'field1' => $value1,
- );
-
- $information = new MetaInformation();
- $information->setFields($fields);
-
- $this->assertFalse($information->hasField('field2'), 'unknown field field2');
- }
-
- public function testSetFieldValue()
- {
- $value1 = $this->createFieldObject('field1', 'oldfieldvalue');
- $value2 = $this->createFieldObject('field2', true);
-
- $fields = array(
- 'field1' => $value1,
- 'field2' => $value2
- );
-
- $information = new MetaInformation();
- $information->setFields($fields);
-
- $expectedValue = 'newFieldValue';
- $information->setFieldValue('field2', $expectedValue);
-
- $this->assertEquals($expectedValue, $information->getField('field2')->value, 'field2 should have new value');
- }
-
public function testHasCallback_CallbackSet()
{
$information = new MetaInformation();
diff --git a/Tests/Doctrine/Mapper/NoIdEntity.php b/Tests/Doctrine/Mapper/NoIdEntity.php
deleted file mode 100644
index 466b947c..00000000
--- a/Tests/Doctrine/Mapper/NoIdEntity.php
+++ /dev/null
@@ -1,29 +0,0 @@
-id;
- }
-}
-
diff --git a/Tests/Doctrine/Mapper/NotIndexedEntity.php b/Tests/Doctrine/Mapper/NotIndexedEntity.php
deleted file mode 100644
index 2ceab1ee..00000000
--- a/Tests/Doctrine/Mapper/NotIndexedEntity.php
+++ /dev/null
@@ -1,8 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
+ $this->solr = $this->createMock(SolrInterface::class);
+ $this->metaInformationFactory = new MetaInformationFactory(new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader()));
+
+ $this->subscriber = new EntityIndexerSubscriber($this->solr, $this->metaInformationFactory, $this->logger);
+ }
+
+ /**
+ * @test
+ */
+ public function separteDeletedRootEntitiesFromNested()
+ {
+ $nested = new NestedEntity();
+ $nested->setId(uniqid());
+
+ $entity = new ValidTestEntityWithCollection();
+ $entity->setId(uniqid());
+ $entity->setCollection(new ArrayCollection([$nested]));
+
+ $objectManager = $this->createMock(ObjectManager::class);
+
+ $this->solr->expects($this->at(0))
+ ->method('removeDocument')
+ ->with($this->callback(function(ValidTestEntityWithCollection $entity) {
+ if (count($entity->getCollection())) {
+ return false;
+ }
+
+ return true;
+ }));
+
+ $this->solr->expects($this->at(1))
+ ->method('removeDocument')
+ ->with($this->callback(function($entity) {
+ if (!$entity instanceof NestedEntity) {
+ return false;
+ }
+
+ return true;
+ }));
+
+ $deleteRootEntityEvent = new LifecycleEventArgs($entity, $objectManager);
+ $this->subscriber->preRemove($deleteRootEntityEvent);
+
+ $deleteNestedEntityEvent = new LifecycleEventArgs($nested, $objectManager);
+ $this->subscriber->preRemove($deleteNestedEntityEvent);
+
+ $entityManager = $this->createMock(EntityManagerInterface::class);
+
+ $this->subscriber->postFlush(new PostFlushEventArgs($entityManager));
+ }
+
+ /**
+ * @test
+ */
+ public function indexOnlyModifiedEntites()
+ {
+ $changedEntity = new ValidTestEntityWithCollection();
+ $this->solr->expects($this->once())
+ ->method('updateDocument')
+ ->with($changedEntity);
+
+ $unitOfWork = $this->createMock(UnitOfWork::class);
+ $unitOfWork->expects($this->at(0))
+ ->method('getEntityChangeSet')
+ ->willReturn(['title' => 'value']);
+
+ $unitOfWork->expects($this->at(1))
+ ->method('getEntityChangeSet')
+ ->willReturn([]);
+
+ $objectManager = $this->createMock(EntityManagerInterface::class);
+ $objectManager->expects($this->any())
+ ->method('getUnitOfWork')
+ ->willReturn($unitOfWork);
+
+ $updateEntityEvent1 = new LifecycleEventArgs($changedEntity, $objectManager);
+
+ $unmodifiedEntity = new ValidTestEntityWithCollection();
+ $updateEntityEvent2 = new LifecycleEventArgs($unmodifiedEntity, $objectManager);
+
+ $this->subscriber->postUpdate($updateEntityEvent1);
+ $this->subscriber->postUpdate($updateEntityEvent2);
+ }
+
+ /**
+ * @test
+ */
+ public function doNotFailHardIfNormalEntityIsPersisted()
+ {
+ $this->solr->expects($this->never())
+ ->method('addDocument');
+
+ $this->solr->expects($this->never())
+ ->method('removeDocument');
+
+ $entity = new NotIndexedEntity();
+
+ $objectManager = $this->createMock(EntityManagerInterface::class);
+
+ $lifecycleEventArgs = new LifecycleEventArgs($entity, $objectManager);
+
+ $this->subscriber->postPersist($lifecycleEventArgs);
+ $this->subscriber->preRemove($lifecycleEventArgs);
+
+
+
+ $this->subscriber->postFlush(new PostFlushEventArgs($objectManager));
+ }
+}
diff --git a/Tests/DocumentStub.php b/Tests/DocumentStub.php
index 6fe17962..b134b8ce 100644
--- a/Tests/DocumentStub.php
+++ b/Tests/DocumentStub.php
@@ -19,4 +19,24 @@ public function __construct(array $fields = array(), array $boosts = array(), ar
{
}
+
+ /**
+ * @param string $fieldName
+ *
+ * @return mixed
+ */
+ public function getField($fieldName)
+ {
+ $fields = array('id' => $this->id, 'document_name' => $this->document_name_s);
+
+ return $fields[$fieldName];
+ }
+
+ /**
+ * @return array
+ */
+ public function getFields()
+ {
+ return array('id' => $this->id, 'document_name' => $this->document_name_s);
+ }
}
\ No newline at end of file
diff --git a/Tests/Fixtures/EntityCore0.php b/Tests/Fixtures/EntityCore0.php
new file mode 100644
index 00000000..157881b7
--- /dev/null
+++ b/Tests/Fixtures/EntityCore0.php
@@ -0,0 +1,57 @@
+id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getText()
+ {
+ return $this->text;
+ }
+
+ /**
+ * @param mixed $id
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * @param string $text
+ */
+ public function setText($text)
+ {
+ $this->text = $text;
+ }
+
+
+}
\ No newline at end of file
diff --git a/Tests/Fixtures/EntityCore1.php b/Tests/Fixtures/EntityCore1.php
new file mode 100644
index 00000000..0f11ee61
--- /dev/null
+++ b/Tests/Fixtures/EntityCore1.php
@@ -0,0 +1,57 @@
+id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getText()
+ {
+ return $this->text;
+ }
+
+ /**
+ * @param mixed $id
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * @param string $text
+ */
+ public function setText($text)
+ {
+ $this->text = $text;
+ }
+
+
+}
\ No newline at end of file
diff --git a/Tests/Fixtures/EntityNestedProperty.php b/Tests/Fixtures/EntityNestedProperty.php
new file mode 100644
index 00000000..f1fe0f02
--- /dev/null
+++ b/Tests/Fixtures/EntityNestedProperty.php
@@ -0,0 +1,142 @@
+id;
+ }
+
+ /**
+ * @param mixed $id
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ public function sliceCollection()
+ {
+ return [$this->collectionValidGetter[0]];
+ }
+
+ /**
+ * @param string $name
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+
+ /**
+ * @param array $collection
+ */
+ public function setCollection($collection)
+ {
+ $this->collection = $collection;
+ }
+
+ /**
+ * @param object $nestedProperty
+ */
+ public function setNestedProperty($nestedProperty)
+ {
+ $this->nestedProperty = $nestedProperty;
+ }
+
+ /**
+ * @param array $collectionValidGetter
+ */
+ public function setCollectionValidGetter($collectionValidGetter)
+ {
+ $this->collectionValidGetter = $collectionValidGetter;
+ }
+
+ /**
+ * @param array $collectionInvalidGetter
+ */
+ public function setCollectionInvalidGetter($collectionInvalidGetter)
+ {
+ $this->collectionInvalidGetter = $collectionInvalidGetter;
+ }
+
+ /**
+ * @param mixed $objectToSimpleFormat
+ */
+ public function setGetterWithParameters($getterWithParameters)
+ {
+ $this->getterWithParameters = $getterWithParameters;
+ }
+
+ /**
+ * @param mixed $simpleGetter
+ */
+ public function setSimpleGetter($simpleGetter)
+ {
+ $this->simpleGetter = $simpleGetter;
+ }
+}
\ No newline at end of file
diff --git a/Tests/Fixtures/EntityWithCustomId.php b/Tests/Fixtures/EntityWithCustomId.php
new file mode 100644
index 00000000..05a0fb1e
--- /dev/null
+++ b/Tests/Fixtures/EntityWithCustomId.php
@@ -0,0 +1,31 @@
+id;
+ }
+}
\ No newline at end of file
diff --git a/Tests/Fixtures/EntityWithInvalidRepository.php b/Tests/Fixtures/EntityWithInvalidRepository.php
new file mode 100644
index 00000000..5a3969fc
--- /dev/null
+++ b/Tests/Fixtures/EntityWithInvalidRepository.php
@@ -0,0 +1,18 @@
+id;
+ }
+
+ /**
+ * @param mixed $id
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * @param string $name
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+
+}
\ No newline at end of file
diff --git a/Tests/Fixtures/NotIndexedEntity.php b/Tests/Fixtures/NotIndexedEntity.php
new file mode 100644
index 00000000..cdc5511c
--- /dev/null
+++ b/Tests/Fixtures/NotIndexedEntity.php
@@ -0,0 +1,9 @@
+subtitle;
+ }
+
+ /**
+ * @param string $subtitle
+ */
+ public function setSubtitle($subtitle)
+ {
+ $this->subtitle = $subtitle;
+ }
+}
\ No newline at end of file
diff --git a/Tests/Doctrine/Annotation/Entities/ValidEntityRepository.php b/Tests/Fixtures/ValidEntityRepository.php
similarity index 62%
rename from Tests/Doctrine/Annotation/Entities/ValidEntityRepository.php
rename to Tests/Fixtures/ValidEntityRepository.php
index ec85d06f..4c7e8191 100644
--- a/Tests/Doctrine/Annotation/Entities/ValidEntityRepository.php
+++ b/Tests/Fixtures/ValidEntityRepository.php
@@ -1,5 +1,6 @@
id;
+ }
+
+ /**
+ * @param int $id
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * @return string $text
+ */
+ public function getText()
+ {
+ return $this->text;
+ }
+
+ /**
+ * @return string $title
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param string $text
+ */
+ public function setText($text)
+ {
+ $this->text = $text;
+ }
+
+ /**
+ * @param string $title
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ }
+
+ /**
+ * @param string $costomField
+ */
+ public function setCostomField($costomField)
+ {
+ $this->costomField = $costomField;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCostomField()
+ {
+ return $this->costomField;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreatedAt()
+ {
+ return $this->created_at;
+ }
+
+ /**
+ * @param \DateTime $created_at
+ */
+ public function setCreatedAt($created_at)
+ {
+ $this->created_at = $created_at;
+ }
+
+ /**
+ * @return ValidTestEntity[]
+ */
+ public function getPosts()
+ {
+ return $this->posts;
+ }
+
+ /**
+ * @param ValidTestEntity[] $posts
+ */
+ public function setPosts($posts)
+ {
+ $this->posts = $posts;
+ }
+
+ /**
+ * @param string $field
+ */
+ public function setField($field)
+ {
+ $this->privateField = $field;
+ }
+
+ /**
+ * @return string
+ */
+ public function getField()
+ {
+ return $this->privateField;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPublishDate()
+ {
+ return $this->publishDate;
+ }
+
+ /**
+ * @param string $publishDate
+ */
+ public function setPublishDate($publishDate)
+ {
+ $this->publishDate = $publishDate;
+ }
+}
+
diff --git a/Tests/Fixtures/ValidTestEntity.php b/Tests/Fixtures/ValidTestEntity.php
new file mode 100644
index 00000000..af79518d
--- /dev/null
+++ b/Tests/Fixtures/ValidTestEntity.php
@@ -0,0 +1,219 @@
+id;
+ }
+
+ /**
+ * @param int $id
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * @return string $text
+ */
+ public function getText()
+ {
+ return $this->text;
+ }
+
+ /**
+ * @return string $title
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param string $text
+ */
+ public function setText($text)
+ {
+ $this->text = $text;
+ }
+
+ /**
+ * @param string $title
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ }
+
+ /**
+ * @param string $costomField
+ */
+ public function setCostomField($costomField)
+ {
+ $this->costomField = $costomField;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCostomField()
+ {
+ return $this->costomField;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreatedAt()
+ {
+ return $this->created_at;
+ }
+
+ /**
+ * @param \DateTime $created_at
+ */
+ public function setCreatedAt($created_at)
+ {
+ $this->created_at = $created_at;
+ }
+
+ /**
+ * @return ValidTestEntity[]
+ */
+ public function getPosts()
+ {
+ return $this->posts;
+ }
+
+ /**
+ * @param ValidTestEntity[] $posts
+ */
+ public function setPosts($posts)
+ {
+ $this->posts = $posts;
+ }
+
+ /**
+ * @param string $field
+ */
+ public function setField($field)
+ {
+ $this->privateField = $field;
+ }
+
+ /**
+ * @return string
+ */
+ public function getField()
+ {
+ return $this->privateField;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPublishDate()
+ {
+ return $this->publishDate;
+ }
+
+ /**
+ * @param string $publishDate
+ */
+ public function setPublishDate($publishDate)
+ {
+ $this->publishDate = $publishDate;
+ }
+
+ /**
+ * @return array
+ */
+ public function getComplexDataType()
+ {
+ return $this->complexDataType;
+ }
+
+ /**
+ * @param string $complexDataType
+ */
+ public function setComplexDataType($complexDataType)
+ {
+ $this->complexDataType = $complexDataType;
+ }
+
+ public function getComplexData()
+ {
+ return json_decode($this->complexDataType, true);
+ }
+}
+
diff --git a/Tests/Fixtures/ValidTestEntityAllCores.php b/Tests/Fixtures/ValidTestEntityAllCores.php
new file mode 100644
index 00000000..8301ded2
--- /dev/null
+++ b/Tests/Fixtures/ValidTestEntityAllCores.php
@@ -0,0 +1,192 @@
+id;
+ }
+
+ /**
+ * @param int $id
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * @return string $text
+ */
+ public function getText()
+ {
+ return $this->text;
+ }
+
+ /**
+ * @return string $title
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param string $text
+ */
+ public function setText($text)
+ {
+ $this->text = $text;
+ }
+
+ /**
+ * @param string $title
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ }
+
+ /**
+ * @param string $costomField
+ */
+ public function setCostomField($costomField)
+ {
+ $this->costomField = $costomField;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCostomField()
+ {
+ return $this->costomField;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreatedAt()
+ {
+ return $this->created_at;
+ }
+
+ /**
+ * @param \DateTime $created_at
+ */
+ public function setCreatedAt($created_at)
+ {
+ $this->created_at = $created_at;
+ }
+
+ /**
+ * @return ValidTestEntity[]
+ */
+ public function getPosts()
+ {
+ return $this->posts;
+ }
+
+ /**
+ * @param ValidTestEntity[] $posts
+ */
+ public function setPosts($posts)
+ {
+ $this->posts = $posts;
+ }
+
+ /**
+ * @param string $field
+ */
+ public function setField($field)
+ {
+ $this->privateField = $field;
+ }
+
+ /**
+ * @return string
+ */
+ public function getField()
+ {
+ return $this->privateField;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPublishDate()
+ {
+ return $this->publishDate;
+ }
+
+ /**
+ * @param string $publishDate
+ */
+ public function setPublishDate($publishDate)
+ {
+ $this->publishDate = $publishDate;
+ }
+}
+
diff --git a/Tests/Doctrine/Annotation/Entities/ValidTestEntityFiltered.php b/Tests/Fixtures/ValidTestEntityFiltered.php
similarity index 76%
rename from Tests/Doctrine/Annotation/Entities/ValidTestEntityFiltered.php
rename to Tests/Fixtures/ValidTestEntityFiltered.php
index 9e5ffc2f..67106dfb 100644
--- a/Tests/Doctrine/Annotation/Entities/ValidTestEntityFiltered.php
+++ b/Tests/Fixtures/ValidTestEntityFiltered.php
@@ -1,15 +1,22 @@
id;
+ }
+
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * @param string $costomField
+ */
+ public function setCostomField($costomField)
+ {
+ $this->costomField = $costomField;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCostomField()
+ {
+ return $this->costomField;
+ }
+
+ /**
+ * @return ArrayCollection
+ */
+ public function getCollection()
+ {
+ return $this->collection;
+ }
+
+ /**
+ * @param ArrayCollection $collection
+ */
+ public function setCollection($collection)
+ {
+ $this->collection = $collection;
+ }
+
+ /**
+ * @return string
+ */
+ public function getText()
+ {
+ return $this->text;
+ }
+
+ /**
+ * @param string $text
+ */
+ public function setText($text)
+ {
+ $this->text = $text;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param string $title
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreatedAt()
+ {
+ return $this->created_at;
+ }
+
+ /**
+ * @param \DateTime $created_at
+ */
+ public function setCreatedAt($created_at)
+ {
+ $this->created_at = $created_at;
+ }
+
+ /**
+ * @return ArrayCollection
+ */
+ public function getCollectionNoGetter()
+ {
+ return $this->collectionNoGetter;
+ }
+
+ /**
+ * @param ArrayCollection $collectionNoGetter
+ */
+ public function setCollectionNoGetter(ArrayCollection $collectionNoGetter)
+ {
+ $this->collectionNoGetter = $collectionNoGetter;
+ }
+}
+
diff --git a/Tests/Doctrine/Annotation/Entities/ValidTestEntityWithInvalidBoost.php b/Tests/Fixtures/ValidTestEntityWithInvalidBoost.php
similarity index 74%
rename from Tests/Doctrine/Annotation/Entities/ValidTestEntityWithInvalidBoost.php
rename to Tests/Fixtures/ValidTestEntityWithInvalidBoost.php
index c14fcfea..21b3093b 100644
--- a/Tests/Doctrine/Annotation/Entities/ValidTestEntityWithInvalidBoost.php
+++ b/Tests/Fixtures/ValidTestEntityWithInvalidBoost.php
@@ -1,10 +1,10 @@
id;
@@ -54,55 +68,87 @@ public function setId($id)
}
/**
- * @return the $text
+ * @param string $costomField
*/
- public function getText()
+ public function setCostomField($costomField)
{
- return $this->text;
+ $this->costomField = $costomField;
}
/**
- * @return the $title
+ * @return string
*/
- public function getTitle()
+ public function getCostomField()
{
- return $this->title;
+ return $this->costomField;
}
/**
- * @param \FS\BlogBundle\Tests\Solr\Doctrine\Mapper\text $text
+ * @return object
*/
- public function setText($text)
+ public function getRelation()
{
- $this->text = $text;
+ return $this->relation;
}
/**
- * @param \FS\BlogBundle\Tests\Solr\Doctrine\Mapper\text $title
+ * @param object $relation
*/
- public function setTitle($title)
+ public function setRelation($relation)
{
- $this->title = $title;
+ $this->relation = $relation;
}
/**
- * @param string $costomField
+ * @return object
*/
- public function setCostomField($costomField)
+ public function getPosts()
{
- $this->costomField = $costomField;
+ return $this->posts;
+ }
+
+ /**
+ * @param object $posts
+ */
+ public function setPosts($posts)
+ {
+ $this->posts = $posts;
}
/**
* @return string
*/
- public function getCostomField()
+ public function getText()
{
- return $this->costomField;
+ return $this->text;
+ }
+
+ /**
+ * @param string $text
+ */
+ public function setText($text)
+ {
+ $this->text = $text;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param string $title
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
}
/**
- * @return \FS\SolrBundle\Tests\Doctrine\Mapper\date
+ * @return \DateTime
*/
public function getCreatedAt()
{
@@ -110,7 +156,7 @@ public function getCreatedAt()
}
/**
- * @param \FS\SolrBundle\Tests\Doctrine\Mapper\date $created_at
+ * @param \DateTime $created_at
*/
public function setCreatedAt($created_at)
{
diff --git a/Tests/Integration/Bootstrap/CrudFeatureContext.php b/Tests/Integration/Bootstrap/CrudFeatureContext.php
deleted file mode 100644
index 5ce049ed..00000000
--- a/Tests/Integration/Bootstrap/CrudFeatureContext.php
+++ /dev/null
@@ -1,110 +0,0 @@
-solr = $this->getMainContext()->getSolrInstance();
-
- $this->entity = new \FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity();
- $this->entity->setId(\FS\SolrBundle\Tests\Util\EntityIdentifier::generate());
- $this->entity->setText('a Text');
- }
-
- /**
- * @When /^I add this entity to Solr$/
- */
- public function iAddThisEntityToSolr()
- {
- $this->solr->addDocument($this->entity);
- }
-
- /**
- * @Then /^should no error occurre$/
- */
- public function shouldNoErrorOccurre()
- {
- $eventDispatcher = $this->getMainContext()->getEventDispatcher();
-
- if ($eventDispatcher->errorsOccurred()) {
- throw new RuntimeException(sprintf('error occurred while indexing: %s', $eventDispatcher->getOccurredErrors()));
- }
-
- $this->getMainContext()->assertInsertSuccessful();
- }
-
- /**
- * @When /^I update one attribute$/
- */
- public function iUpdateOneAttribute()
- {
- $this->entity->setText('text has changed');
- }
-
- /**
- * @Then /^the index should be updated$/
- */
- public function theIndexShouldBeUpdated()
- {
- $client = $this->getMainContext()->getSolrClient();
- $entityId = $this->entity->getId();
-
- $query = $client->createSelect();
- $query->setQuery(sprintf('id:%s', $entityId));
- $resultset = $client->select($query);
-
- if ($resultset->getNumFound() == 0) {
- throw new RuntimeException(sprintf('could not find document with id %s after update', $entityId));
- }
-
- foreach ($resultset as $document) {
- $changedFieldValue = $document['text_t'];
-
- if ($changedFieldValue != $this->entity->getText()) {
- throw new RuntimeException(sprintf('updated entity with id %s was not updated in solr', $entityId));
- }
- }
- }
-
- /**
- * @When /^I delete the entity$/
- */
- public function iDeleteTheEntity()
- {
- $this->solr->removeDocument($this->entity);
- }
-
- /**
- * @Then /^I should not find the entity in Solr$/
- */
- public function iShouldNotFindTheEntityInSolr()
- {
- $client = $this->getMainContext()->getSolrClient();
- $entityId = $this->entity->getId();
-
- $query = $client->createSelect();
- $query->setQuery(sprintf('id:%s', $entityId));
- $resultset = $client->select($query);
-
- if ($resultset->getNumFound() > 0) {
- throw new \RuntimeException(sprintf('document with id %s should not found in the index', $entityId));
- }
- }
-
-}
\ No newline at end of file
diff --git a/Tests/Integration/Bootstrap/FeatureContext.php b/Tests/Integration/Bootstrap/FeatureContext.php
deleted file mode 100644
index d7c48d05..00000000
--- a/Tests/Integration/Bootstrap/FeatureContext.php
+++ /dev/null
@@ -1,158 +0,0 @@
-useContext('crud', new CrudFeatureContext());
-
- $this->eventDispatcher = new \FS\SolrBundle\Tests\Integration\EventDispatcherFake();
- }
-
- /**
- * @return \FS\SolrBundle\Tests\Integration\EventDispatcherFake
- */
- public function getEventDispatcher()
- {
- return $this->eventDispatcher;
- }
-
- /**
- * @return \Solarium\Client
- */
- public function getSolrClient()
- {
- return $this->solrClient;
- }
-
- /**
- * @return \FS\SolrBundle\Solr
- */
- public function getSolrInstance()
- {
- \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists');
- \Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver::registerAnnotationClasses();
-
- $this->solrClient = $this->setupSolrClient();
- $factory = $this->setupCommandFactory();
- $metaFactory = $this->setupMetaInformationFactory();
- $entityMapper = $this->setupEntityMapper();
-
- $solr = new \FS\SolrBundle\Solr(
- $this->solrClient,
- $factory,
- $this->eventDispatcher,
- $metaFactory,
- $entityMapper
- );
-
- return $solr;
- }
-
- private function setupEntityMapper()
- {
- $registry = new \FS\SolrBundle\Tests\Integration\DoctrineRegistryFake();
-
- $entityMapper = new \FS\SolrBundle\Doctrine\Mapper\EntityMapper(
- new \FS\SolrBundle\Doctrine\Hydration\DoctrineHydrator(
- $registry,
- new \FS\SolrBundle\Doctrine\Hydration\ValueHydrator()
- ),
- new \FS\SolrBundle\Doctrine\Hydration\IndexHydrator(
- new \FS\SolrBundle\Doctrine\Hydration\ValueHydrator()
- )
- );
-
- return $entityMapper;
- }
-
- /**
- * @return \FS\SolrBundle\Doctrine\Mapper\Mapping\CommandFactory
- */
- private function setupCommandFactory()
- {
- $factory = new \FS\SolrBundle\Doctrine\Mapper\Mapping\CommandFactory();
- $factory->add(new \FS\SolrBundle\Doctrine\Mapper\Mapping\MapAllFieldsCommand(), 'all');
- $factory->add(new \FS\SolrBundle\Doctrine\Mapper\Mapping\MapIdentifierCommand(), 'identifier');
-
- return $factory;
- }
-
- /**
- * @return \FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory
- */
- private function setupMetaInformationFactory()
- {
- $ormConfiguration = new Doctrine\ORM\Configuration();
- $ormConfiguration->addEntityNamespace('FSTest:ValidTestEntity', 'FS\SolrBundle\Tests\Doctrine\Mapper');
-
- $knowNamespaces = new \FS\SolrBundle\Doctrine\ClassnameResolver\KnownNamespaceAliases();
- $knowNamespaces->addEntityNamespaces($ormConfiguration);
-
- $classnameResolver = new \FS\SolrBundle\Doctrine\ClassnameResolver\ClassnameResolver($knowNamespaces);
-
- $metaFactory = new \FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory();
- $metaFactory->setClassnameResolver(
- $classnameResolver
- );
-
- return $metaFactory;
- }
-
- /**
- * @return \Solarium\Client
- */
- private function setupSolrClient()
- {
- $config = array(
- 'default' => array(
- 'host' => 'localhost',
- 'port' => 8983,
- 'path' => '/solr/',
- )
- );
-
- $builder = new \FS\SolrBundle\Builder\SolrBuilder($config);
- $solrClient = $builder->build();
-
- return $solrClient;
- }
-
- public function assertInsertSuccessful()
- {
- if (!$this->eventDispatcher->eventOccurred(\FS\SolrBundle\Event\Events::POST_INSERT) ||
- !$this->eventDispatcher->eventOccurred(\FS\SolrBundle\Event\Events::PRE_INSERT)) {
- throw new RuntimeException('Insert was not successful');
- }
- }
-}
diff --git a/Tests/Integration/Entity/Category.php b/Tests/Integration/Entity/Category.php
new file mode 100644
index 00000000..27be6226
--- /dev/null
+++ b/Tests/Integration/Entity/Category.php
@@ -0,0 +1,122 @@
+posts = new ArrayCollection();
+ }
+
+ /**
+ * @return int
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @param int $id
+ */
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * @param string $title
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ }
+
+ /**
+ * @return Post[]
+ */
+ public function getPosts()
+ {
+ return $this->posts;
+ }
+
+ /**
+ * @param Post[] $posts
+ */
+ public function setPosts($posts)
+ {
+ $this->posts = $posts;
+ }
+
+ public function addPost($post)
+ {
+ $this->posts->add($post);
+ }
+
+ /**
+ * @param string $info
+ */
+ public function setInfo($info)
+ {
+ $this->info = $info;
+ }
+
+}
\ No newline at end of file
diff --git a/Tests/Integration/Entity/Post.php b/Tests/Integration/Entity/Post.php
new file mode 100644
index 00000000..2c5623de
--- /dev/null
+++ b/Tests/Integration/Entity/Post.php
@@ -0,0 +1,294 @@
+intField = 3;
+ $this->tags = new ArrayCollection();
+ }
+
+ public function setId($id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * Get id
+ *
+ * @return integer
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Set title
+ *
+ * @param string $title
+ * @return Post
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+
+ return $this;
+ }
+
+ /**
+ * Get title
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * Set text
+ *
+ * @param string $text
+ * @return Post
+ */
+ public function setText($text)
+ {
+ $this->text = $text;
+
+ return $this;
+ }
+
+ /**
+ * Get text
+ *
+ * @return string
+ */
+ public function getText()
+ {
+ return $this->text;
+ }
+
+ /**
+ * Set created
+ *
+ * @param \DateTime $created
+ * @return Post
+ */
+ public function setCreated($created)
+ {
+ $this->created = $created;
+
+ return $this;
+ }
+
+ /**
+ * Get created
+ *
+ * @return \DateTime
+ */
+ public function getCreated()
+ {
+ return $this->created;
+ }
+
+ public function selectCore()
+ {
+ if ($this->lang == 'en') {
+ return 'core0';
+ }
+
+ return 'core1';
+ }
+
+ /**
+ * @return Category
+ */
+ public function getCategory()
+ {
+ return $this->category;
+ }
+
+ /**
+ * @param Category $category
+ */
+ public function setCategory($category)
+ {
+ $this->category = $category;
+ }
+
+ /**
+ * @return Tag[]
+ */
+ public function getTags()
+ {
+ return $this->tags;
+ }
+
+ /**
+ * @param Tag[] $tags
+ */
+ public function setTags($tags)
+ {
+ $this->tags = $tags;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSlug()
+ {
+ return $this->slug;
+ }
+
+ /**
+ * @param string $slug
+ */
+ public function setSlug($slug)
+ {
+ $this->slug = $slug;
+ }
+
+ /**
+ * @return array
+ */
+ public function getMultivalues()
+ {
+ return $this->multivalues;
+ }
+
+ /**
+ * @param array $multivalues
+ */
+ public function setMultivalues($multivalues)
+ {
+ $this->multivalues = $multivalues;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isIsParent() : bool
+ {
+ return $this->isParent;
+ }
+
+ /**
+ * @param bool $isParent
+ */
+ public function setIsParent(bool $isParent)
+ {
+ $this->isParent = $isParent;
+ }
+}
diff --git a/Tests/Integration/Entity/Tag.php b/Tests/Integration/Entity/Tag.php
new file mode 100644
index 00000000..250a2784
--- /dev/null
+++ b/Tests/Integration/Entity/Tag.php
@@ -0,0 +1,114 @@
+name = $name;
+ }
+
+ /**
+ * Get id
+ *
+ * @return integer
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @param int $id
+ */
+ public function setId(int $id)
+ {
+ $this->id = $id;
+ }
+
+ /**
+ * Set name
+ *
+ * @param string $name
+ *
+ * @return Tag
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * Get name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return Post
+ */
+ public function getPost()
+ {
+ return $this->post;
+ }
+
+ /**
+ * @param Post $post
+ */
+ public function setPost($post)
+ {
+ $this->post = $post;
+ }
+
+
+}
+
diff --git a/Tests/Integration/EventDispatcherFake.php b/Tests/Integration/EventDispatcherFake.php
index 12827a50..68e1a282 100644
--- a/Tests/Integration/EventDispatcherFake.php
+++ b/Tests/Integration/EventDispatcherFake.php
@@ -20,13 +20,18 @@ class EventDispatcherFake implements EventDispatcherInterface
*/
private $events = array();
+ public function getEvents()
+ {
+ return $this->events;
+ }
+
/**
* Dispatches an event to all registered listeners.
*
* @param string $eventName The name of the event to dispatch. The name of
* the event is the name of the method that is
* invoked on listeners.
- * @param Event $event The event to pass to the event handlers/listeners.
+ * @param Event $event The event to pass to the event handlers/listeners.
* If not supplied, an empty Event instance is created.
*
* @return Event
@@ -88,9 +93,9 @@ public function eventOccurred($eventName)
/**
* Adds an event listener that listens on the specified events.
*
- * @param string $eventName The event to listen on
+ * @param string $eventName The event to listen on
* @param callable $listener The listener
- * @param integer $priority The higher this value, the earlier an event
+ * @param integer $priority The higher this value, the earlier an event
* listener will be triggered in the chain (defaults to 0)
*
* @api
@@ -119,7 +124,7 @@ public function addSubscriber(EventSubscriberInterface $subscriber)
* Removes an event listener from the specified events.
*
* @param string|array $eventName The event(s) to remove a listener from
- * @param callable $listener The listener to remove
+ * @param callable $listener The listener to remove
*/
public function removeListener($eventName, $listener)
{
@@ -160,4 +165,19 @@ public function hasListeners($eventName = null)
// TODO: Implement hasListeners() method.
}
+ /**
+ * Gets the listener priority for a specific event.
+ *
+ * Returns null if the event or the listener does not exist.
+ *
+ * @param string $eventName The name of the event
+ * @param callable $listener The listener
+ *
+ * @return int|null The event listener priority
+ */
+ public function getListenerPriority($eventName, $listener)
+ {
+ // TODO: Implement getListenerPriority() method.
+ }
+
}
\ No newline at end of file
diff --git a/Tests/Integration/Features/crud.feature b/Tests/Integration/Features/crud.feature
deleted file mode 100644
index f576bad9..00000000
--- a/Tests/Integration/Features/crud.feature
+++ /dev/null
@@ -1,21 +0,0 @@
-Feature: I can index entities to a Solr instance
-
- Scenario: I index one entity
- Given I have a Doctrine entity
- When I add this entity to Solr
- Then should no error occurre
-
- Scenario: I can update one entity
- Given I have a Doctrine entity
- When I add this entity to Solr
- Then should no error occurre
- When I update one attribute
- And I add this entity to Solr
- Then the index should be updated
-
- Scenario: I can delete a entity
- Given I have a Doctrine entity
- When I add this entity to Solr
- Then should no error occurre
- When I delete the entity
- Then I should not find the entity in Solr
\ No newline at end of file
diff --git a/Tests/Integration/IndexingTest.php b/Tests/Integration/IndexingTest.php
new file mode 100644
index 00000000..45ec84af
--- /dev/null
+++ b/Tests/Integration/IndexingTest.php
@@ -0,0 +1,252 @@
+eventDispatcher = new EventDispatcherFake();
+ $this->client = $this->setupSolrClient();
+
+ try {
+ $this->client->ping(new Query());
+ } catch (\Exception $e) {
+ $this->markTestSkipped('solr is not running on localhost:8983');
+ return;
+ }
+
+ $metainformationFactory = new MetaInformationFactory(new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader()));
+ $logger = $this->createMock(LoggerInterface::class);
+
+ $this->solr = new Solr(
+ $this->client,
+ $this->eventDispatcher,
+ $metainformationFactory,
+ new EntityMapper(
+ new DoctrineHydrator(new ValueHydrator()),
+ new IndexHydrator(new ValueHydrator()),
+ $metainformationFactory
+ )
+ );
+
+ $this->solr->clearIndex();
+
+ $this->doctrineSubscriber = new EntityIndexerSubscriber($this->solr, $metainformationFactory, $logger);
+ }
+
+ /**
+ * Solarium Client with two cores (core0, core1)
+ *
+ * @return Client
+ */
+ private function setupSolrClient()
+ {
+ $config = array(
+ 'core0' => array(
+ 'host' => 'localhost',
+ 'port' => 8983,
+ 'path' => '/solr/core0',
+ )
+ );
+
+ $builder = new SolariumClientBuilder($config, $this->eventDispatcher);
+ $solrClient = $builder->build();
+
+ return $solrClient;
+ }
+
+ /**
+ * @test
+ */
+ public function indexSingleEntity()
+ {
+ $currentDate = date('c') . 'Z';
+
+ $post = new Post();
+ $post->setId(1);
+ $post->setTitle('indexSingleEntity');
+ $post->setCreated($currentDate);
+ $post->setMultivalues(['foo', 'bar']);
+
+ $objectManager = $this->createMock(EntityManagerInterface::class);
+
+ $lifecycleEventArgs = new LifecycleEventArgs($post, $objectManager);
+
+ $this->doctrineSubscriber->postPersist($lifecycleEventArgs);
+
+ $this->doctrineSubscriber->postFlush(new PostFlushEventArgs($objectManager));
+
+ $events = $this->eventDispatcher->getEvents();
+
+ $expectedRequest = 'post_1indexSingleEntity' . $currentDate . 'foobar';
+
+ $this->assertEquals($expectedRequest, $events['solarium.core.preExecuteRequest']->getRequest()->getRawData());
+ }
+
+ /**
+ * @test
+ */
+ public function deleteEntityWithNested()
+ {
+ $post = new Post();
+ $post->setId(1);
+ $post->setTitle('deleteEntityWithOneToOne');
+
+ $category = new Category();
+ $category->setId(1);
+ $category->setTitle('deleteEntityWithOneToOne category');
+
+ $post->setCategory($category);
+
+ $objectManager = $this->createMock(EntityManagerInterface::class);
+
+ $this->doctrineSubscriber->postPersist(new LifecycleEventArgs($post, $objectManager));
+ $this->doctrineSubscriber->postFlush(new PostFlushEventArgs($objectManager));
+
+ $this->assertEntityExists('deleteEntityWithOneToOne', 'deleteEntityWithOneToOne category');
+
+ $this->doctrineSubscriber->preRemove(new LifecycleEventArgs($category, $objectManager));
+ $this->doctrineSubscriber->preRemove(new LifecycleEventArgs($post, $objectManager));
+ $this->doctrineSubscriber->postFlush(new PostFlushEventArgs($objectManager));
+
+ $this->assertEntityNotExists('deleteEntityWithOneToOne', 'deleteEntityWithOneToOne category');
+ }
+
+ private function assertEntityExists($postName, $categoryName)
+ {
+ $query = $this->client->createSelect();
+ $query->setQuery('title_s:' . $postName);
+
+ $result = $this->client->execute($query);
+
+ $this->assertEquals(1, $result->getData()['response']['numFound']);
+
+ $query->setQuery('title_s:"'. $categoryName .'"');
+
+ $result = $this->client->execute($query);
+
+ $this->assertEquals(1, $result->getData()['response']['numFound']);
+ }
+
+ private function assertEntityNotExists($postName, $categoryName)
+ {
+ $query = $this->client->createSelect();
+ $query->setQuery('title_s:' . $postName);
+
+ $result = $this->client->execute($query);
+
+ $this->assertEquals(0, $result->getData()['response']['numFound']);
+
+ $query->setQuery('title_s:"'. $categoryName .'"');
+
+ $result = $this->client->execute($query);
+
+ $this->assertEquals(0, $result->getData()['response']['numFound']);
+ }
+
+ /**
+ * @test
+ */
+ public function indexEntityWithOneToOne()
+ {
+ $post = new Post();
+ $post->setId(1);
+ $post->setTitle('indexEntityWithOneToOne');
+
+ $category = new Category();
+ $category->setId(1);
+ $category->setTitle('indexEntityWithOneToOne category');
+
+ $post->setCategory($category);
+
+ $objectManager = $this->createMock(EntityManagerInterface::class);
+
+ $lifecycleEventArgs = new LifecycleEventArgs($post, $objectManager);
+
+ $this->doctrineSubscriber->postPersist($lifecycleEventArgs);
+
+ $this->doctrineSubscriber->postFlush(new PostFlushEventArgs($objectManager));
+
+ $events = $this->eventDispatcher->getEvents();
+
+ $expectedRequest = 'post_1indexEntityWithOneToOnecategory_1indexEntityWithOneToOne category';
+
+ $this->assertEquals($expectedRequest, $events['solarium.core.preExecuteRequest']->getRequest()->getRawData());
+
+ $this->assertEntityExists('indexEntityWithOneToOne', 'indexEntityWithOneToOne category');
+ }
+
+ /**
+ * @test
+ */
+ public function indexEntityWithOneToMany()
+ {
+ $post = new Post();
+ $post->setId(1);
+ $post->setTitle('indexEntityWithOneToMany');
+
+ $tag1 = new Tag('tag indexEntityWithOneToMany 1');
+ $tag1->setId(1);
+ $tag2 = new Tag('tag indexEntityWithOneToMany 2');
+ $tag2->setId(2);
+
+ $post->setTags([$tag1, $tag2]);
+
+ $objectManager = $this->createMock(EntityManagerInterface::class);
+
+
+ $this->doctrineSubscriber->postPersist(new LifecycleEventArgs($tag1, $objectManager));
+ $this->doctrineSubscriber->postPersist(new LifecycleEventArgs($tag2, $objectManager));
+ $this->doctrineSubscriber->postPersist(new LifecycleEventArgs($post, $objectManager));
+
+ $this->doctrineSubscriber->postFlush(new PostFlushEventArgs($objectManager));
+
+ $events = $this->eventDispatcher->getEvents();
+
+ $expectedRequest = 'post_1indexEntityWithOneToManytag_1tag indexEntityWithOneToMany 1tag_2tag indexEntityWithOneToMany 2';
+
+ $this->assertEquals($expectedRequest, $events['solarium.core.preExecuteRequest']->getRequest()->getRawData());
+ }
+}
\ No newline at end of file
diff --git a/Tests/Integration/SolariumClientFake.php b/Tests/Integration/SolariumClientFake.php
new file mode 100644
index 00000000..44d07b5b
--- /dev/null
+++ b/Tests/Integration/SolariumClientFake.php
@@ -0,0 +1,10 @@
+getMock('Solarium\QueryType\Update\Query\Query', array(), array(), '', false);
+ $updateQuery = $this->createMock(Query::class);
$updateQuery->expects($this->once())
->method('addDocument');
@@ -24,6 +32,8 @@ private function assertUpdateQueryExecuted()
->expects($this->once())
->method('createUpdate')
->will($this->returnValue($updateQuery));
+
+ return $updateQuery;
}
/**
@@ -31,13 +41,11 @@ private function assertUpdateQueryExecuted()
*/
public function addDocumentToAllCores()
{
- $this->assertUpdateQueryExecuted();
+ $updateQuery = $this->assertUpdateQueryExecuted();
$this->eventDispatcher->expects($this->any())
->method('dispatch');
- $this->mapOneDocument();
-
$this->solrClientFake->expects($this->once())
->method('getEndpoints')
->will($this->returnValue(array(
@@ -45,15 +53,20 @@ public function addDocumentToAllCores()
'core1' => array()
)));
- $this->solrClientFake->expects($this->exactly(2))
- ->method('update');
+ $this->solrClientFake->expects($this->at(2))
+ ->method('update')
+ ->with($updateQuery, 'core0');
- $metaInformation = MetaTestInformationFactory::getMetaInformation();
- $metaInformation->setIndex('*');
- $this->setupMetaFactoryLoadOneCompleteInformation($metaInformation);
+ $this->solrClientFake->expects($this->at(3))
+ ->method('update')
+ ->with($updateQuery, 'core1');
+
+ $this->mapper->expects($this->once())
+ ->method('toDocument')
+ ->will($this->returnValue(new DocumentStub()));
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $solr->addDocument(new ValidTestEntity());
+ $solr = new Solr($this->solrClientFake, $this->eventDispatcher, $this->metaFactory, $this->mapper);
+ $solr->addDocument(new ValidTestEntityAllCores());
}
/**
@@ -61,13 +74,11 @@ public function addDocumentToAllCores()
*/
public function updateDocumentInAllCores()
{
- $this->assertUpdateQueryExecuted();
+ $updateQuery = $this->assertUpdateQueryExecuted();
$this->eventDispatcher->expects($this->exactly(2))
->method('dispatch');
- $this->mapOneDocument();
-
$this->solrClientFake->expects($this->once())
->method('getEndpoints')
->will($this->returnValue(array(
@@ -75,16 +86,20 @@ public function updateDocumentInAllCores()
'core1' => array()
)));
- $this->solrClientFake->expects($this->exactly(2))
- ->method('update');
+ $this->solrClientFake->expects($this->at(2))
+ ->method('update')
+ ->with($updateQuery, 'core0');
+ $this->solrClientFake->expects($this->at(3))
+ ->method('update')
+ ->with($updateQuery, 'core1');
- $metaInformation = MetaTestInformationFactory::getMetaInformation();
- $metaInformation->setIndex('*');
- $this->setupMetaFactoryLoadOneCompleteInformation($metaInformation);
+ $this->mapper->expects($this->once())
+ ->method('toDocument')
+ ->will($this->returnValue(new DocumentStub()));
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $solr->updateDocument(new ValidTestEntity());
+ $solr = new Solr($this->solrClientFake, $this->eventDispatcher, $this->metaFactory, $this->mapper);
+ $solr->updateDocument(new ValidTestEntityAllCores());
}
/**
@@ -92,10 +107,6 @@ public function updateDocumentInAllCores()
*/
public function removeDocumentFromAllCores()
{
- $metaInformation = MetaTestInformationFactory::getMetaInformation();
- $metaInformation->setIndex('*');
- $this->setupMetaFactoryLoadOneCompleteInformation($metaInformation);
-
$this->mapper->expects($this->once())
->method('toDocument')
->will($this->returnValue(new DocumentStub()));
@@ -107,7 +118,7 @@ public function removeDocumentFromAllCores()
'core1' => array()
)));
- $deleteQuery = $this->getMock('Solarium\QueryType\Update\Query\Query', array(), array(), '', false);
+ $deleteQuery = $this->createMock(Query::class);
$deleteQuery->expects($this->once())
->method('addDeleteQuery')
->with($this->isType('string'));
@@ -123,8 +134,8 @@ public function removeDocumentFromAllCores()
$this->solrClientFake->expects($this->exactly(2))
->method('update');
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $solr->removeDocument(new ValidTestEntity());
+ $solr = new Solr($this->solrClientFake, $this->eventDispatcher, $this->metaFactory, $this->mapper);
+ $solr->removeDocument(new ValidTestEntityAllCores());
}
}
\ No newline at end of file
diff --git a/Tests/Query/FindByDocumentNameQueryTest.php b/Tests/Query/FindByDocumentNameQueryTest.php
index 4833b028..9a511333 100644
--- a/Tests/Query/FindByDocumentNameQueryTest.php
+++ b/Tests/Query/FindByDocumentNameQueryTest.php
@@ -16,28 +16,26 @@ class FindByDocumentNameQueryTest extends \PHPUnit_Framework_TestCase
public function testGetQuery_SearchInAllFields()
{
$document = new Document();
- $document->addField('document_name_s', 'validtestentity');
+ $document->addField('id', 'validtestentity_1');
$query = new FindByDocumentNameQuery();
+ $query->setDocumentName('validtestentity');
$query->setDocument($document);
- $queryString = $query->getQuery();
-
- $this->assertEquals('document_name_s:validtestentity', $queryString, 'filter query');
+ $this->assertEquals('*:*', $query->getQuery(), 'filter query');
+ $this->assertEquals('id:validtestentity_*', $query->getFilterQuery('id')->getQuery());
}
+ /**
+ * @expectedException FS\SolrBundle\Query\Exception\QueryException
+ * @expectedExceptionMessage documentName should not be null
+ */
public function testGetQuery_DocumentnameMissing()
{
$query = new FindByDocumentNameQuery();
$query->setDocument(new Document());
- try {
- $query->getQuery();
-
- $this->fail('an exception should be thrown');
- } catch (\RuntimeException $e) {
- $this->assertTrue(true);
- }
+ $query->getQuery();
}
}
diff --git a/Tests/Query/FindByIdentifierQueryTest.php b/Tests/Query/FindByIdentifierQueryTest.php
index 3cdfe7bf..35f5920d 100644
--- a/Tests/Query/FindByIdentifierQueryTest.php
+++ b/Tests/Query/FindByIdentifierQueryTest.php
@@ -14,46 +14,25 @@ class FindByIdentifierQueryTest extends \PHPUnit_Framework_TestCase
public function testGetQuery_SearchInAllFields()
{
$document = new Document();
- $document->addField('id', '1');
- $document->addField('document_name_s', 'validtestentity');
+ $document->setKey('id', 'validtestentity_1');
- $expectedQuery = 'id:1 AND document_name_s:validtestentity';
$query = new FindByIdentifierQuery();
+ $query->setDocumentKey('validtestentity_1');
$query->setDocument($document);
- $queryString = $query->getQuery();
-
- $this->assertEquals($expectedQuery, $queryString);
- }
-
- public function testGetQuery_DocumentNameMissing()
- {
- $document = new Document();
- $document->addField('id', '1');
-
- $query = new FindByIdentifierQuery();
- $query->setDocument($document);
-
- try {
- $query->getQuery();
-
- $this->fail('an exception should be thrown');
- } catch (\RuntimeException $e) {
- $this->assertEquals('documentName should not be null', $e->getMessage());
- }
+ $this->assertEquals('*:*', $query->getQuery());
+ $this->assertEquals('id:validtestentity_1', $query->getFilterQuery('id')->getQuery());
}
+ /**
+ * @expectedException FS\SolrBundle\Query\Exception\QueryException
+ * @expectedExceptionMessage id should not be null
+ */
public function testGetQuery_IdMissing()
{
$query = new FindByIdentifierQuery();
$query->setDocument(new Document());
- try {
- $query->getQuery();
-
- $this->fail('an exception should be thrown');
- } catch (\RuntimeException $e) {
- $this->assertEquals('id should not be null', $e->getMessage());
- }
+ $query->getQuery();
}
}
diff --git a/Tests/Query/QueryBuilderTest.php b/Tests/Query/QueryBuilderTest.php
new file mode 100644
index 00000000..594cb1c7
--- /dev/null
+++ b/Tests/Query/QueryBuilderTest.php
@@ -0,0 +1,172 @@
+solr = $this->createMock(SolrInterface::class);
+ }
+
+ /**
+ * @test
+ */
+ public function christmasReadme()
+ {
+ $metaInformation = $this->setupMetainformation();
+
+ $builder = new QueryBuilder($this->solr, $metaInformation);
+
+ $nearNorthPole = $builder->where('position')->nearCircle(38.116181, -86.929463, 100.5);
+ self::assertEquals("{!bbox pt=38.116181,-86.929463 sfield=position_s d=100.5}", $nearNorthPole->getQuery()->getCustomQuery());
+
+ $builder = new QueryBuilder($this->solr, $metaInformation);
+ $santaClaus = $builder->where('santa-name')->contains(['Noel', 'Claus', 'Natale', 'Baba', 'Nicolas'])
+ ->andWhere('santa-beard-exists')->is(true)
+ ->andWhere('santa-beard-lenght')->between(5.5, 10.0)
+ ->andWhere('santa-beard-color')->startsWith('whi')->endsWith('te')
+ ->andWhere($nearNorthPole);
+
+ self::assertEquals("santa-name_ss:(*Noel* *Claus* *Natale* *Baba* *Nicolas*) AND santa-beard-exists_b:true AND santa-beard-lenght_f:[5.5 TO 10] AND santa-beard-color_s:(whi* *te) AND {!bbox pt=38.116181,-86.929463 sfield=position_s d=100.5}", $santaClaus->getQuery()->getCustomQuery());
+
+ $builder = new QueryBuilder($this->solr, $metaInformation);
+ $goodPeople = $builder->where('good-actions')->greaterThanEqual(10)
+ ->orWhere('bad-actions')->lessThanEqual(5);
+
+ self::assertEquals('good-actions_i:[10 TO *] OR bad-actions_i:[* TO 5]', $goodPeople->getQuery()->getCustomQuery());
+
+ $builder = new QueryBuilder($this->solr, $metaInformation);
+ $gifts = $builder->where('gift-name')->sloppy('LED TV GoPro Oculus Tablet Laptop', 2)
+ ->andWhere('gift-type')->fuzzy('information', 0.4)->startsWith('tech')
+ ->andWhere('__query__')->expression('{!dismax qf=myfield}how now brown cow');
+
+ self::assertEquals('gift-name_s:"LED TV GoPro Oculus Tablet Laptop"~2 AND gift-type_s:(information~0.4 tech*) AND __query___s:{!dismax qf=myfield}how now brown cow', $gifts->getQuery()->getCustomQuery());
+
+ $builder1 = new QueryBuilder($this->solr, $metaInformation);
+
+ $builder2 = new QueryBuilder($this->solr, $metaInformation);
+
+ $christmas = new \DateTime('2016-12-25');
+ $contributors = ['Christoph', 'Philipp', 'Francisco', 'Fabio'];
+ $giftReceivers = $builder1->where('gift-received')->is(null)
+ ->andWhere('chimney')->isNotNull()
+ ->andWhere('date')->is($christmas)->greaterThanEqual(new \Datetime('1970-01-01'))
+ ->andWhere($santaClaus)
+ ->andWhere($gifts)
+ ->andWhere(
+ $builder2->where('name')->in($contributors)->boost(2.0)
+ ->orWhere($goodPeople)
+ );
+
+ self::assertEquals("-gift-received_s:[* TO *] AND chimney_s:[* TO *] AND date_dt:(2016\\-12\\-25T00\\:00\\:00Z [1970\\-01\\-01T00\\:00\\:00Z TO *]) AND (santa-name_ss:(*Noel* *Claus* *Natale* *Baba* *Nicolas*) AND santa-beard-exists_b:true AND santa-beard-lenght_f:[5.5 TO 10] AND santa-beard-color_s:(whi* *te) AND {!bbox pt=38.116181,-86.929463 sfield=position_s d=100.5}) AND (gift-name_s:\"LED TV GoPro Oculus Tablet Laptop\"~2 AND gift-type_s:(information~0.4 tech*) AND __query___s:{!dismax qf=myfield}how now brown cow) AND (name_s:(Christoph Philipp Francisco Fabio)^2.0 OR (good-actions_i:[10 TO *] OR bad-actions_i:[* TO 5]))", $giftReceivers->getQuery()->getCustomQuery());
+ }
+
+ /**
+ * @test
+ */
+ public function doNotAddIdFieldTwice()
+ {
+ $builder = new QueryBuilder($this->solr, $this->setupMetainformation());
+
+ $query = $builder
+ ->where('santa-beard-exists')->is(true)
+ ->andWhere('santa-beard-lenght')->between(5.5, 10.0)
+ ->andWhere('santa-beard-color')->startsWith('whi')->endsWith('te')
+ ->andWhere('id')->is('post_1')
+ ->getQuery()->getQuery();
+
+ $this->assertEquals('santa-beard-exists_b:true AND santa-beard-lenght_f:[5.5 TO 10] AND santa-beard-color_s:(whi* *te) AND id:post_1', $query);
+ }
+
+ /**
+ * @test
+ * @expectedException \FS\SolrBundle\Doctrine\Mapper\SolrMappingException
+ * @expectedExceptionMessage $fieldName must not be empty
+ */
+ public function setEmpty()
+ {
+ $builder = new QueryBuilder($this->solr, $this->setupMetainformation());
+ $query = $builder
+ ->where('')
+ ->getQuery()->getQuery();
+ }
+
+ /**
+ * @return MetaInformation
+ */
+ private function setupMetainformation()
+ {
+ $metaInformation = new MetaInformation();
+
+ $field1 = new Field(array());
+ $field1->name = 'position';
+ $field1->type = 'string';
+
+ $field2 = new Field(array());
+ $field2->name = 'santa-beard-exists';
+ $field2->type = 'boolean';
+
+ $field3 = new Field(array());
+ $field3->name = 'santa-beard-lenght';
+ $field3->type = 'float';
+
+ $field4 = new Field(array());
+ $field4->name = 'santa-beard-color';
+ $field4->type = 'string';
+
+ $field5 = new Field(array());
+ $field5->name = 'good-actions';
+ $field5->type = 'integer';
+
+ $field6 = new Field(array());
+ $field6->name = 'gift-name';
+ $field6->type = 'string';
+
+ $field7 = new Field(array());
+ $field7->name = 'gift-type';
+ $field7->type = 'string';
+
+ $field8 = new Field(array());
+ $field8->name = 'gift-received';
+ $field8->type = 'string';
+
+ $field9 = new Field(array());
+ $field9->name = 'chimney';
+ $field9->type = 'string';
+
+ $field10 = new Field(array());
+ $field10->name = 'date';
+ $field10->type = 'datetime';
+
+ $field11 = new Field(array());
+ $field11->name = 'santa-name';
+ $field11->type = 'strings';
+
+ $field12 = new Field(array());
+ $field12->name = 'bad-actions';
+ $field12->type = 'integer';
+
+ $field13 = new Field(array());
+ $field13->name = '__query__';
+ $field13->type = 'string';
+
+ $field14 = new Field(array());
+ $field14->name = 'name';
+ $field14->type = 'string';
+
+ $field15 = new Field(array());
+ $field15->name = 'id';
+
+ $metaInformation->setFields(array($field1, $field2, $field3, $field4, $field5, $field6, $field7, $field8, $field9, $field10, $field11, $field12, $field13, $field14, $field15));
+
+ return $metaInformation;
+ }
+}
\ No newline at end of file
diff --git a/Tests/Query/SolrQueryTest.php b/Tests/Query/SolrQueryTest.php
index 9bc57926..e973c589 100644
--- a/Tests/Query/SolrQueryTest.php
+++ b/Tests/Query/SolrQueryTest.php
@@ -3,7 +3,12 @@
namespace FS\SolrBundle\Tests\Query;
use FS\SolrBundle\Doctrine\Annotation\AnnotationReader;
+use FS\SolrBundle\Doctrine\Annotation\Id;
+use FS\SolrBundle\Doctrine\Mapper\MetaInformation;
+use FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory;
+use FS\SolrBundle\Query\Exception\UnknownFieldException;
use FS\SolrBundle\Query\SolrQuery;
+use FS\SolrBundle\SolrInterface;
use FS\SolrBundle\SolrQueryFacade;
/**
@@ -16,26 +21,37 @@ class SolrQueryTest extends \PHPUnit_Framework_TestCase
private function getFieldMapping()
{
return array(
+ 'id' => 'id',
'title_s' => 'title',
'text_t' => 'text',
'created_at_dt' => 'created_at'
);
}
+ /**
+ * @return SolrQuery
+ */
private function createQueryWithFieldMapping()
{
- $solr = $this->getMock('FS\SolrBundle\Solr', array(), array(), '', false);
+ $solr = $this->createMock(SolrInterface::class);
+
+ $idField = new Id(array());
+ $idField->name = 'id';
+
+ $metaInformation = new MetaInformation();
+ $metaInformation->setDocumentName('post');
+ $metaInformation->setIdentifier($idField);
$solrQuery = new SolrQuery();
$solrQuery->setSolr($solr);
$solrQuery->setMappedFields($this->getFieldMapping());
+ $solrQuery->setMetaInformation($metaInformation);
return $solrQuery;
}
/**
- *
- * @return \FS\SolrBundle\SolrQuery
+ * @return SolrQuery
*/
private function createQueryWithSearchTerms()
{
@@ -76,11 +92,12 @@ public function testAddField_OneFieldOfTwoNotMapped()
public function testGetSolrQuery_QueryTermShouldCorrect()
{
- $expected = 'title_s:*foo* OR text_t:*bar*';
+ $expected = 'title_s:foo OR text_t:bar';
$query = $this->createQueryWithSearchTerms();
$this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
}
@@ -97,56 +114,163 @@ public function testAddSearchTerm_AllFieldsAreMapped()
$this->assertTrue(array_key_exists('text_t', $terms), 'text_t not in terms');
}
- public function testAddSearchTerm_OneFieldOfTwoNotMapped()
- {
- $solrQuery = $this->createQueryWithFieldMapping();
-
- $solrQuery->addSearchTerm('title', 'foo')
- ->addSearchTerm('foo', 'bar');
-
- $terms = $solrQuery->getSearchTerms();
-
- $this->assertTrue(array_key_exists('title_s', $terms), 'title_s not in terms');
- $this->assertEquals(1, count($terms));
- }
-
+ /**
+ * @expectedException \FS\SolrBundle\Query\Exception\UnknownFieldException
+ */
public function testAddSearchTerm_UnknownField()
{
$solrQuery = $this->createQueryWithFieldMapping();
$solrQuery->addSearchTerm('unknownfield', 'foo');
-
- $terms = $solrQuery->getSearchTerms();
-
- $this->assertEquals(0, count($terms));
}
public function testGetQuery_TermsConcatWithOr()
{
- $expected = 'title_s:*foo* OR text_t:*bar*';
+ $expected = 'title_s:foo OR text_t:bar';
$query = $this->createQueryWithSearchTerms();
$this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
}
public function testGetQuery_TermsConcatWithAnd()
{
- $expected = 'title_s:*foo* AND text_t:*bar*';
+ $expected = 'title_s:foo AND text_t:bar';
$query = $this->createQueryWithSearchTerms();
$query->setUseAndOperator(true);
$this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
}
public function testGetQuery_SearchInAllFields()
{
- $solrQuery = $this->createQueryWithFieldMapping();
- $solrQuery->queryAllFields('foo');
+ $query = $this->createQueryWithFieldMapping();
+ $query->queryAllFields('foo');
- $expected = 'title_s:*foo* OR text_t:*foo* OR created_at_dt:*foo*';
+ $expected = 'title_s:foo OR text_t:foo OR created_at_dt:foo';
+
+ $this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
+ }
+
+ public function testGetQuery_SurroundTermWithDoubleQuotes()
+ {
+ $query = $this->createQueryWithFieldMapping();
+ $query->queryAllFields('foo 12');
+
+ $expected = 'title_s:"foo 12" OR text_t:"foo 12" OR created_at_dt:"foo 12"';
+
+ $this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
+ }
+
+ public function testGetQuery_SurroundWildcardTermWithDoubleQuotes()
+ {
+ $query = $this->createQueryWithFieldMapping();
+ $query->queryAllFields('foo 12');
+ $query->setUseWildcard(true);
+
+ $expected = 'title_s:"*foo 12*" OR text_t:"*foo 12*" OR created_at_dt:"*foo 12*"';
+
+ $this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
+ }
+
+ public function testGetQuery_NoWildcard_Word()
+ {
+ $query = $this->createQueryWithFieldMapping();
+ $query->setUseWildcard(false);
+ $query->addSearchTerm('title', 'a_word');
+
+ $expected = 'title_s:a_word';
+
+ $this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
+ }
+
+ public function testGetQuery_NoSearchTerm()
+ {
+ $query = $this->createQueryWithFieldMapping();
+
+ $expected = '*:*';
+
+ $this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
+ }
+
+ public function testGetQuery_CustomQuery()
+ {
+ $query = $this->createQueryWithFieldMapping();
+ $query->setCustomQuery('title_s:[*:*]');
+
+ $expected = 'title_s:[*:*]';
+
+ $this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
+ }
+
+ /**
+ * @test
+ */
+ public function searchInSetMultipleValues()
+ {
+ $query = $this->createQueryWithFieldMapping();
+ $query->addSearchTerm('title', array('value2', 'value1'));
+
+ $expected = 'title_s:["value1" TO "value2"]';
+
+ $this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
+ }
+
+ /**
+ * @test
+ */
+ public function searchInSetSingleValues()
+ {
+ $query = $this->createQueryWithFieldMapping();
+ $query->addSearchTerm('title', array('value #1'));
+
+ $expected = 'title_s:"value #1"';
+
+ $this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_*', $query->getFilterQuery('id')->getQuery());
+ }
+
+ /**
+ * @test
+ */
+ public function doNotAddIdFieldTwice()
+ {
+ $expected = '*:*';
+
+ $query = $this->createQueryWithFieldMapping();
+ $query->addSearchTerm('id', 'post_1');
+
+ $this->assertEquals($expected, $query->getQuery());
+ $this->assertEquals('id:post_1', $query->getFilterQuery('id')->getQuery());
+ }
+
+ /**
+ * @test
+ */
+ public function generateQueryForNestedDocuments()
+ {
+ $mapping = [
+ 'id' => 'id',
+ 'title_s' => 'title',
+ 'collection.id' => 'collection.id',
+ 'collection.name_s' => 'collection.name'
+ ];
+
+ $query = $this->createQueryWithFieldMapping();
+ $query->setMappedFields($mapping);
+ $query->addSearchTerm('collection.name', 'test*bar');
+ $query->addSearchTerm('title', 'test post');
- $this->assertEquals($expected, $solrQuery->getQuery());
+ $this->assertEquals('title_s:"test post" OR {!parent which="id:post_*"}name_s:test*bar', $query->getQuery());
}
}
diff --git a/Tests/Repository/RepositoryTest.php b/Tests/Repository/RepositoryTest.php
index b270cd22..e58e0145 100644
--- a/Tests/Repository/RepositoryTest.php
+++ b/Tests/Repository/RepositoryTest.php
@@ -2,17 +2,43 @@
namespace FS\SolrBundle\Tests\Solr\Repository;
+use FS\SolrBundle\Doctrine\Annotation\AnnotationReader;
+use FS\SolrBundle\Doctrine\Hydration\HydrationModes;
+use FS\SolrBundle\Doctrine\Mapper\EntityMapper;
+use FS\SolrBundle\Doctrine\Mapper\EntityMapperInterface;
+use FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory;
+use FS\SolrBundle\Query\AbstractQuery;
+use FS\SolrBundle\Query\FindByDocumentNameQuery;
+use FS\SolrBundle\Query\FindByIdentifierQuery;
+use FS\SolrBundle\Tests\Fixtures\EntityNestedProperty;
+use FS\SolrBundle\Tests\SolrClientFake;
use FS\SolrBundle\Tests\Util\MetaTestInformationFactory;
use FS\SolrBundle\Tests\Util\CommandFactoryStub;
+use Solarium\Core\Query\Helper;
use Solarium\QueryType\Update\Query\Document\Document;
use FS\SolrBundle\Repository\Repository;
-use FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntity;
/**
* @group repository
*/
class RepositoryTest extends \PHPUnit_Framework_TestCase
{
+ /**
+ * @var MetaTestInformationFactory
+ */
+ private $metaInformationFactory;
+
+ private $mapper;
+
+ protected function setUp()
+ {
+ $this->metaInformationFactory = new MetaInformationFactory($reader = new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader()));
+ $this->mapper = $this->createMock(EntityMapperInterface::class);
+ $this->mapper->expects($this->once())
+ ->method('setHydrationMode')
+ ->with(HydrationModes::HYDRATE_DOCTRINE);
+ }
public function testFind_DocumentIsKnown()
{
@@ -20,92 +46,42 @@ public function testFind_DocumentIsKnown()
$document->addField('id', 2);
$document->addField('document_name_s', 'post');
- $metaFactory = $this->getMock(
- 'FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory',
- array(),
- array(),
- '',
- false
- );
- $metaFactory->expects($this->once())
- ->method('loadInformation')
- ->will($this->returnValue(MetaTestInformationFactory::getMetaInformation()));
-
- $mapper = $this->getMock('FS\SolrBundle\Doctrine\Mapper\EntityMapper', array(), array(), '', false);
- $mapper->expects($this->once())
- ->method('toDocument')
- ->will($this->returnValue($document));
-
- $solr = $this->getMock('FS\SolrBundle\Solr', array(), array(), '', false);
- $solr->expects($this->exactly(2))
- ->method('getMapper')
- ->will($this->returnValue($mapper));
-
- $solr->expects($this->once())
- ->method('getCommandFactory')
- ->will($this->returnValue(CommandFactoryStub::getFactoryWithAllMappingCommand()));
-
- $solr->expects($this->once())
- ->method('getMetaFactory')
- ->will($this->returnValue($metaFactory));
+ $metaInformation = MetaTestInformationFactory::getMetaInformation();
$entity = new ValidTestEntity();
- $solr->expects($this->once())
- ->method('query')
- ->will($this->returnValue(array($entity)));
- $repo = new Repository($solr, $entity);
+ $solr = new SolrClientFake();
+ $solr->mapper = $this->mapper;
+ $solr->response = array($entity);
+
+ $repo = new Repository($solr, $metaInformation);
$actual = $repo->find(2);
$this->assertTrue($actual instanceof ValidTestEntity, 'find return no entity');
+
+ $this->assertTrue($solr->query instanceof FindByIdentifierQuery);
+ $this->assertEquals('*:*', $solr->query->getQuery());
+ $this->assertEquals('id:validtestentity_2', $solr->query->getFilterQuery('id')->getQuery());
}
public function testFindAll()
{
- $document = new Document();
- $document->addField('id', 2);
- $document->addField('document_name_s', 'post');
-
- $metaFactory = $this->getMock(
- 'FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory',
- array(),
- array(),
- '',
- false
- );
- $metaFactory->expects($this->once())
- ->method('loadInformation')
- ->will($this->returnValue(MetaTestInformationFactory::getMetaInformation()));
-
- $mapper = $this->getMock('FS\SolrBundle\Doctrine\Mapper\EntityMapper', array(), array(), '', false);
- $mapper->expects($this->once())
- ->method('toDocument')
- ->will($this->returnValue($document));
-
- $solr = $this->getMock('FS\SolrBundle\Solr', array(), array(), '', false);
- $solr->expects($this->exactly(2))
- ->method('getMapper')
- ->will($this->returnValue($mapper));
-
- $solr->expects($this->once())
- ->method('getCommandFactory')
- ->will($this->returnValue(CommandFactoryStub::getFactoryWithAllMappingCommand()));
-
- $solr->expects($this->once())
- ->method('getMetaFactory')
- ->will($this->returnValue($metaFactory));
+ $metaInformation = MetaTestInformationFactory::getMetaInformation();
$entity = new ValidTestEntity();
- $solr->expects($this->once())
- ->method('query')
- ->will($this->returnValue(array($entity)));
- $repo = new Repository($solr, $entity);
+ $solr = new SolrClientFake();
+ $solr->mapper = $this->mapper;
+ $solr->response = array($entity);
+
+ $repo = new Repository($solr, $metaInformation);
$actual = $repo->findAll();
$this->assertTrue(is_array($actual));
- $this->assertNull($document->id, 'id was removed');
+ $this->assertTrue($solr->query instanceof FindByDocumentNameQuery);
+ $this->assertEquals('*:*', $solr->query->getQuery());
+ $this->assertEquals('id:validtestentity_*', $solr->query->getFilterQuery('id')->getQuery());
}
public function testFindBy()
@@ -115,27 +91,74 @@ public function testFindBy()
'text' => 'bar'
);
- $solr = $this->getMock('FS\SolrBundle\Solr', array(), array(), '', false);
- $query = $this->getMock('FS\SolrBundle\Query\SolrQuery', array(), array(), '', false);
- $query->expects($this->exactly(2))
- ->method('addSearchTerm');
+ $metaInformation = MetaTestInformationFactory::getMetaInformation();
- $solr->expects($this->once())
- ->method('createQuery')
- ->will($this->returnValue($query));
+ $entity = new ValidTestEntity();
- $solr->expects($this->once())
- ->method('query')
- ->with($query)
- ->will($this->returnValue(array()));
+ $solr = new SolrClientFake();
+ $solr->mapper = $this->mapper;
+ $solr->response = array($entity);
+ $solr->metaFactory = $this->metaInformationFactory;
- $entity = new ValidTestEntity();
- $repo = new Repository($solr, $entity);
+ $repo = new Repository($solr, $metaInformation);
$found = $repo->findBy($fields);
$this->assertTrue(is_array($found));
+
+ $this->assertTrue($solr->query instanceof AbstractQuery);
+ $this->assertEquals('title:foo AND text_t:bar', $solr->query->getQuery());
+ $this->assertEquals('id:validtestentity_*', $solr->query->getFilterQuery('id')->getQuery());
}
+ public function testFindOneBy()
+ {
+ $fields = array(
+ 'title' => 'foo',
+ 'text' => 'bar'
+ );
+
+ $metaInformation = MetaTestInformationFactory::getMetaInformation();
+
+ $entity = new ValidTestEntity();
+
+ $solr = new SolrClientFake();
+ $solr->mapper = $this->mapper;
+ $solr->response = array($entity);
+ $solr->metaFactory = $this->metaInformationFactory;
+
+ $repo = new Repository($solr, $metaInformation);
+
+ $found = $repo->findOneBy($fields);
+
+ $this->assertEquals($entity, $found);
+
+ $this->assertTrue($solr->query instanceof AbstractQuery);
+ $this->assertEquals('title:foo AND text_t:bar', $solr->query->getQuery());
+ $this->assertEquals('id:validtestentity_*', $solr->query->getFilterQuery('id')->getQuery());
+ }
+
+ /**
+ * @test
+ */
+ public function findOneByNestedField()
+ {
+ $metaInformation = $this->metaInformationFactory->loadInformation(EntityNestedProperty::class);
+
+ $entity = new ValidTestEntity();
+
+ $solr = new SolrClientFake();
+ $solr->mapper = $this->mapper;
+ $solr->response = array($entity);
+ $solr->metaFactory = $this->metaInformationFactory;
+
+ $repo = new Repository($solr, $metaInformation);
+
+ $found = $repo->findOneBy([
+ 'collection.name' => '*test*test*'
+ ]);
+
+ $this->assertEquals('{!parent which="id:entitynestedproperty_*"}name_t:*test*test*', $solr->query->getQuery());
+ }
}
diff --git a/Tests/Resources/config/schema.xml b/Tests/Resources/config/schema.xml
new file mode 100644
index 00000000..0f9a969b
--- /dev/null
+++ b/Tests/Resources/config/schema.xml
@@ -0,0 +1,721 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/SolrClientFake.php b/Tests/SolrClientFake.php
index e0318009..ed20801a 100644
--- a/Tests/SolrClientFake.php
+++ b/Tests/SolrClientFake.php
@@ -1,13 +1,45 @@
mapper;
+ }
+
+ public function getCommandFactory()
+ {
+ return $this->commandFactory;
+ }
- public function addDocument($doc)
+ public function getMetaFactory()
{
+ return $this->metaFactory;
+ }
+
+ public function addDocument($doc): bool
+ {
+ return true;
}
public function deleteByQuery($query)
@@ -24,8 +56,10 @@ public function isCommited()
return $this->commit;
}
- public function query()
+ public function query(AbstractQuery $query): array
{
+ $this->query = $query;
+
return $this->response;
}
@@ -38,4 +72,45 @@ public function getOptions()
{
return array();
}
+
+ public function createQuery($entity)
+ {
+ $metaInformation = $this->metaFactory->loadInformation($entity);
+ $class = $metaInformation->getClassName();
+ $entity = new $class;
+
+ $query = new SolrQuery();
+ $query->setSolr($this);
+ $query->setEntity($entity);
+ $query->setIndex($metaInformation->getIndex());
+ $query->setMetaInformation($metaInformation);
+ $query->setMappedFields($metaInformation->getFieldMapping());
+
+ return $query;
+ }
+
+ public function removeDocument($entity)
+ {
+ // TODO: Implement removeDocument() method.
+ }
+
+ public function updateDocument($entity): bool
+ {
+ // TODO: Implement updateDocument() method.
+ }
+
+ public function getRepository($entity): RepositoryInterface
+ {
+ // TODO: Implement getRepository() method.
+ }
+
+ public function computeChangeSet(array $doctrineChangeSet, $entity)
+ {
+ // TODO: Implement computeChangeSet() method.
+ }
+
+ public function createQueryBuilder($entity): QueryBuilderInterface
+ {
+ // TODO: Implement createQueryBuilder() method.
+ }
}
diff --git a/Tests/SolrTest.php b/Tests/SolrTest.php
index b442952d..cd489043 100644
--- a/Tests/SolrTest.php
+++ b/Tests/SolrTest.php
@@ -2,155 +2,51 @@
namespace FS\SolrBundle\Tests;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\InvalidTestEntityFiltered;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidTestEntityFiltered;
+use FS\SolrBundle\Query\QueryBuilderInterface;
+use FS\SolrBundle\Tests\Fixtures\EntityWithInvalidRepository;
+use FS\SolrBundle\Tests\Fixtures\InvalidTestEntityFiltered;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntityFiltered;
+use FS\SolrBundle\Tests\Fixtures\EntityCore0;
+use FS\SolrBundle\Tests\Fixtures\EntityCore1;
use FS\SolrBundle\Tests\Doctrine\Mapper\SolrDocumentStub;
-use FS\SolrBundle\Tests\ResultFake;
-use FS\SolrBundle\Tests\SolrResponseFake;
use FS\SolrBundle\Query\FindByDocumentNameQuery;
-use FS\SolrBundle\Event\EventManager;
-use FS\SolrBundle\Tests\SolrClientFake;
-use FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\EntityWithRepository;
-use FS\SolrBundle\Doctrine\Mapper\MetaInformation;
-use FS\SolrBundle\Tests\Util\MetaTestInformationFactory;
-use FS\SolrBundle\Solr;
-use FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidEntityRepository;
-use FS\SolrBundle\Tests\Util\CommandFactoryStub;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntity;
+use FS\SolrBundle\Tests\Fixtures\EntityWithRepository;
+use FS\SolrBundle\Tests\Fixtures\ValidEntityRepository;
use FS\SolrBundle\Query\SolrQuery;
+use Solarium\Plugin\BufferedAdd\BufferedAdd;
use Solarium\QueryType\Update\Query\Document\Document;
/**
*
* @group facade
*/
-class SolrTest extends \PHPUnit_Framework_TestCase
+class SolrTest extends AbstractSolrTest
{
- protected $metaFactory = null;
- protected $config = null;
- protected $commandFactory = null;
- protected $eventDispatcher = null;
- protected $mapper = null;
- protected $solrClientFake = null;
-
- public function setUp()
- {
- $this->metaFactory = $metaFactory = $this->getMock(
- 'FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory',
- array(),
- array(),
- '',
- false
- );
- $this->config = $this->getMock('FS\SolrBundle\SolrConnection', array(), array(), '', false);
- $this->commandFactory = CommandFactoryStub::getFactoryWithAllMappingCommand();
- $this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcher', array(), array(), '', false);
- $this->mapper = $this->getMock('FS\SolrBundle\Doctrine\Mapper\EntityMapper', array(), array(), '', false);
-
- $this->solrClientFake = $this->getMock('Solarium\Client', array(), array(), '', false);
- }
-
- private function assertUpdateQueryExecuted()
- {
- $updateQuery = $this->getMock('Solarium\QueryType\Update\Query\Query', array(), array(), '', false);
- $updateQuery->expects($this->once())
- ->method('addDocument');
-
- $updateQuery->expects($this->once())
- ->method('addCommit');
-
- $this->solrClientFake
- ->expects($this->once())
- ->method('createUpdate')
- ->will($this->returnValue($updateQuery));
- }
-
- private function assertUpdateQueryWasNotExecuted()
- {
- $updateQuery = $this->getMock('Solarium\QueryType\Update\Query\Query', array(), array(), '', false);
- $updateQuery->expects($this->never())
- ->method('addDocument');
-
- $updateQuery->expects($this->never())
- ->method('addCommit');
-
- $this->solrClientFake
- ->expects($this->never())
- ->method('createUpdate');
- }
-
- protected function assertDeleteQueryWasExecuted()
- {
- $deleteQuery = $this->getMock('Solarium\QueryType\Update\Query\Query', array(), array(), '', false);
- $deleteQuery->expects($this->once())
- ->method('addDeleteQuery')
- ->with($this->isType('string'));
-
- $deleteQuery->expects($this->once())
- ->method('addCommit');
-
- $this->solrClientFake
- ->expects($this->once())
- ->method('createUpdate')
- ->will($this->returnValue($deleteQuery));
-
- $this->solrClientFake
- ->expects($this->once())
- ->method('update')
- ->with($deleteQuery);
- }
-
- protected function setupMetaFactoryLoadOneCompleteInformation($metaInformation = null)
- {
- if (null === $metaInformation) {
- $metaInformation = MetaTestInformationFactory::getMetaInformation();
- }
-
- $this->metaFactory->expects($this->once())
- ->method('loadInformation')
- ->will($this->returnValue($metaInformation));
- }
-
public function testCreateQuery_ValidEntity()
{
- $this->setupMetaFactoryLoadOneCompleteInformation();
-
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $query = $solr->createQuery('FSBlogBundle:ValidTestEntity');
+ $query = $this->solr->createQuery(ValidTestEntity::class);
$this->assertTrue($query instanceof SolrQuery);
- $this->assertEquals(4, count($query->getMappedFields()));
+ $this->assertEquals(6, count($query->getMappedFields()));
}
public function testGetRepository_UserdefinedRepository()
{
- $metaInformation = new MetaInformation();
- $metaInformation->setClassName(get_class(new EntityWithRepository()));
- $metaInformation->setRepository('FS\SolrBundle\Tests\Doctrine\Annotation\Entities\ValidEntityRepository');
-
- $this->setupMetaFactoryLoadOneCompleteInformation($metaInformation);
-
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $actual = $solr->getRepository('Tests:EntityWithRepository');
+ $actual = $this->solr->getRepository(EntityWithRepository::class);
$this->assertTrue($actual instanceof ValidEntityRepository);
}
/**
- * @expectedException RuntimeException
+ * @expectedException \FS\SolrBundle\SolrException
+ * @expectedExceptionMessage FS\SolrBundle\Tests\Fixtures\InvalidEntityRepository must extends the FS\SolrBundle\Repository\Repository
*/
public function testGetRepository_UserdefinedInvalidRepository()
{
- $metaInformation = new MetaInformation();
- $metaInformation->setClassName(get_class(new EntityWithRepository()));
- $metaInformation->setRepository('FS\SolrBundle\Tests\Doctrine\Annotation\Entities\InvalidEntityRepository');
-
- $this->setupMetaFactoryLoadOneCompleteInformation($metaInformation);
-
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $solr->getRepository('Tests:EntityWithInvalidRepository');
+ $this->solr->getRepository(EntityWithInvalidRepository::class);
}
public function testAddDocument()
@@ -160,12 +56,14 @@ public function testAddDocument()
$this->eventDispatcher->expects($this->exactly(2))
->method('dispatch');
- $this->mapOneDocument();
+ $this->mapper->expects($this->once())
+ ->method('toDocument')
+ ->will($this->returnValue(new DocumentStub()));
- $this->setupMetaFactoryLoadOneCompleteInformation();
+ $entity = new ValidTestEntity();
+ $entity->setTitle('title');
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $solr->addDocument(new ValidTestEntity());
+ $this->solr->addDocument($entity);
}
public function testUpdateDocument()
@@ -175,12 +73,27 @@ public function testUpdateDocument()
$this->eventDispatcher->expects($this->exactly(2))
->method('dispatch');
- $this->mapOneDocument();
+ $this->mapper->expects($this->once())
+ ->method('toDocument')
+ ->will($this->returnValue(new DocumentStub()));
+
+ $entity = new ValidTestEntity();
+ $entity->setTitle('title');
+
+ $this->solr->updateDocument($entity);
+ }
+
+ public function testDoNotUpdateDocumentIfDocumentCallbackAvoidIt()
+ {
+ $this->eventDispatcher->expects($this->never())
+ ->method('dispatch');
+
+ $this->assertUpdateQueryWasNotExecuted();
- $this->setupMetaFactoryLoadOneCompleteInformation();
+ $filteredEntity = new ValidTestEntityFiltered();
+ $filteredEntity->shouldIndex = false;
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $solr->updateDocument(new ValidTestEntity());
+ $this->solr->updateDocument($filteredEntity);
}
public function testRemoveDocument()
@@ -190,14 +103,11 @@ public function testRemoveDocument()
$this->eventDispatcher->expects($this->exactly(2))
->method('dispatch');
- $this->setupMetaFactoryLoadOneCompleteInformation();
-
$this->mapper->expects($this->once())
->method('toDocument')
->will($this->returnValue(new DocumentStub()));
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $solr->removeDocument(new ValidTestEntity());
+ $this->solr->removeDocument(new ValidTestEntity());
}
public function testClearIndex()
@@ -211,42 +121,22 @@ public function testClearIndex()
$this->assertDeleteQueryWasExecuted();
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $solr->clearIndex();
- }
-
- private function assertQueryWasExecuted($data = array())
- {
- $selectQuery = $this->getMock('Solarium\QueryType\Select\Query\Query', array(), array(), '', false);
- $selectQuery->expects($this->once())
- ->method('setQuery');
-
- $queryResult = new ResultFake($data);
-
- $this->solrClientFake
- ->expects($this->once())
- ->method('createSelect')
- ->will($this->returnValue($selectQuery));
-
- $this->solrClientFake
- ->expects($this->once())
- ->method('select')
- ->with($selectQuery)
- ->will($this->returnValue($queryResult));
+ $this->solr->clearIndex();
}
public function testQuery_NoResponseKeyInResponseSet()
{
- $this->assertQueryWasExecuted();
-
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
-
$document = new Document();
$document->addField('document_name_s', 'name');
+
$query = new FindByDocumentNameQuery();
+ $query->setDocumentName('name');
$query->setDocument($document);
+ $query->setIndex('index0');
- $entities = $solr->query($query);
+ $this->assertQueryWasExecuted(array(), 'index0');
+
+ $entities = $this->solr->query($query);
$this->assertEquals(0, count($entities));
}
@@ -254,17 +144,18 @@ public function testQuery_OneDocumentFound()
{
$arrayObj = new SolrDocumentStub(array('title_s' => 'title'));
- $this->assertQueryWasExecuted(array($arrayObj));
-
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
-
$document = new Document();
$document->addField('document_name_s', 'name');
+
$query = new FindByDocumentNameQuery();
+ $query->setDocumentName('name');
$query->setDocument($document);
$query->setEntity(new ValidTestEntity());
+ $query->setIndex('index0');
+
+ $this->assertQueryWasExecuted(array($arrayObj), 'index0');
- $entities = $solr->query($query);
+ $entities = $this->solr->query($query);
$this->assertEquals(1, count($entities));
}
@@ -277,38 +168,33 @@ public function testAddEntity_ShouldNotIndexEntity()
$entity = new ValidTestEntityFiltered();
- $information = new MetaInformation();
- $information->setSynchronizationCallback('shouldBeIndex');
- $this->setupMetaFactoryLoadOneCompleteInformation($information);
-
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $solr->addDocument($entity);
+ $this->solr->addDocument($entity);
$this->assertTrue($entity->getShouldBeIndexedWasCalled(), 'filter method was not called');
}
public function testAddEntity_ShouldIndexEntity()
{
- $this->assertUpdateQueryExecuted();
+ $this->assertUpdateQueryExecuted('index0');
- $this->eventDispatcher->expects($this->exactly(2))
+ $this->eventDispatcher->expects($this->any())
->method('dispatch');
$entity = new ValidTestEntityFiltered();
$entity->shouldIndex = true;
- $information = MetaTestInformationFactory::getMetaInformation();
- $information->setSynchronizationCallback('shouldBeIndex');
- $this->setupMetaFactoryLoadOneCompleteInformation($information);
-
- $this->mapOneDocument();
+ $this->mapper->expects($this->once())
+ ->method('toDocument')
+ ->will($this->returnValue(new DocumentStub()));
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- $solr->addDocument($entity);
+ $this->solr->addDocument($entity);
$this->assertTrue($entity->getShouldBeIndexedWasCalled(), 'filter method was not called');
}
+ /**
+ * @expectedException \FS\SolrBundle\SolrException
+ */
public function testAddEntity_FilteredEntityWithUnknownCallback()
{
$this->assertUpdateQueryWasNotExecuted();
@@ -316,25 +202,84 @@ public function testAddEntity_FilteredEntityWithUnknownCallback()
$this->eventDispatcher->expects($this->never())
->method('dispatch');
- $information = MetaTestInformationFactory::getMetaInformation();
- $information->setSynchronizationCallback('shouldBeIndex');
- $this->setupMetaFactoryLoadOneCompleteInformation($information);
+ $this->solr->addDocument(new InvalidTestEntityFiltered());
+ }
+
+ /**
+ * @test
+ */
+ public function indexDocumentsGroupedByCore()
+ {
+ $entity = new ValidTestEntity();
+ $entity->setTitle('title field');
+
+ $bufferPlugin = $this->createMock(BufferedAdd::class);
+
+ $bufferPlugin->expects($this->once())
+ ->method('setEndpoint')
+ ->with(null);
+
+ $bufferPlugin->expects($this->once())
+ ->method('commit');
+
+ $this->solrClientFake->expects($this->once())
+ ->method('getPlugin')
+ ->with('bufferedadd')
+ ->will($this->returnValue($bufferPlugin));
+
- $solr = new Solr($this->solrClientFake, $this->commandFactory, $this->eventDispatcher, $this->metaFactory, $this->mapper);
- try {
- $solr->addDocument(new InvalidTestEntityFiltered());
+ $this->mapper->expects($this->once())
+ ->method('toDocument')
+ ->will($this->returnValue(new DocumentStub()));
- $this->fail('BadMethodCallException expected');
- } catch (\BadMethodCallException $e) {
- $this->assertTrue(true);
- }
+ $this->solr->synchronizeIndex(array($entity));
}
- protected function mapOneDocument()
+ /**
+ * @test
+ */
+ public function setCoreToNullIfNoIndexExists()
{
- $this->mapper->expects($this->once())
+ $entity1 = new EntityCore0();
+ $entity1->setText('a text');
+
+ $entity2 = new EntityCore1();
+ $entity2->setText('a text');
+
+ $bufferPlugin = $this->createMock(BufferedAdd::class);
+
+ $bufferPlugin->expects($this->at(2))
+ ->method('setEndpoint')
+ ->with('core0');
+
+ $bufferPlugin->expects($this->at(5))
+ ->method('setEndpoint')
+ ->with('core1');
+
+ $bufferPlugin->expects($this->exactly(2))
+ ->method('commit');
+
+ $this->solrClientFake->expects($this->once())
+ ->method('getPlugin')
+ ->with('bufferedadd')
+ ->will($this->returnValue($bufferPlugin));
+
+
+ $this->mapper->expects($this->exactly(2))
->method('toDocument')
- ->will($this->returnValue($this->getMock('Solarium\QueryType\Update\Query\Document\DocumentInterface')));
+ ->will($this->returnValue(new DocumentStub()));
+
+ $this->solr->synchronizeIndex(array($entity1, $entity2));
+ }
+
+ /**
+ * @test
+ */
+ public function createQueryBuilder()
+ {
+ $queryBuilder = $this->solr->createQueryBuilder(ValidTestEntity::class);
+
+ $this->assertTrue($queryBuilder instanceof QueryBuilderInterface);
}
}
diff --git a/Tests/Util/CommandFactoryStub.php b/Tests/Util/CommandFactoryStub.php
index d907b7b3..4f148a47 100644
--- a/Tests/Util/CommandFactoryStub.php
+++ b/Tests/Util/CommandFactoryStub.php
@@ -5,6 +5,7 @@
use FS\SolrBundle\Doctrine\Mapper\Mapping\CommandFactory;
use FS\SolrBundle\Doctrine\Mapper\Mapping\MapAllFieldsCommand;
use FS\SolrBundle\Doctrine\Mapper\Mapping\MapIdentifierCommand;
+use FS\SolrBundle\Doctrine\Mapper\MetaInformationFactory;
class CommandFactoryStub
{
@@ -14,8 +15,10 @@ class CommandFactoryStub
*/
public static function getFactoryWithAllMappingCommand()
{
+ $reader = new AnnotationReader(new \Doctrine\Common\Annotations\AnnotationReader());
+
$commandFactory = new CommandFactory();
- $commandFactory->add(new MapAllFieldsCommand(), 'all');
+ $commandFactory->add(new MapAllFieldsCommand(new MetaInformationFactory($reader)), 'all');
$commandFactory->add(new MapIdentifierCommand(), 'identifier');
return $commandFactory;
diff --git a/Tests/Util/EntityIdentifier.php b/Tests/Util/EntityIdentifier.php
index 286aeef1..203e1873 100644
--- a/Tests/Util/EntityIdentifier.php
+++ b/Tests/Util/EntityIdentifier.php
@@ -7,6 +7,6 @@ class EntityIdentifier
{
public static function generate()
{
- return rand(1, 15);
+ return rand(1, 100000000);
}
}
\ No newline at end of file
diff --git a/Tests/Util/MetaTestInformationFactory.php b/Tests/Util/MetaTestInformationFactory.php
index 6d40955e..ad9fcfc8 100644
--- a/Tests/Util/MetaTestInformationFactory.php
+++ b/Tests/Util/MetaTestInformationFactory.php
@@ -2,22 +2,27 @@
namespace FS\SolrBundle\Tests\Util;
use FS\SolrBundle\Doctrine\Annotation\Field;
-use FS\SolrBundle\Tests\Doctrine\Mapper\ValidTestEntity;
+use FS\SolrBundle\Doctrine\Annotation\Id;
+use FS\SolrBundle\Tests\Fixtures\ValidTestEntity;
use FS\SolrBundle\Doctrine\Mapper\MetaInformation;
class MetaTestInformationFactory
{
/**
+ * @param object $entity
+ *
* @return MetaInformation
*/
- public static function getMetaInformation()
+ public static function getMetaInformation($entity = null)
{
- $entity = new ValidTestEntity();
+ if ($entity === null) {
+ $entity = new ValidTestEntity();
+ }
$entity->setId(2);
$metaInformation = new MetaInformation();
- $title = new Field(array('name' => 'title', 'type' => 'string', 'boost' => '1.8', 'value' => 'A title'));
+ $title = new Field(array('name' => 'title', 'boost' => '1.8', 'value' => 'A title'));
$text = new Field(array('name' => 'text', 'type' => 'text', 'value' => 'A text'));
$createdAt = new Field(array('name' => 'created_at', 'type' => 'date', 'boost' => '1', 'value' => 'A created at'));
@@ -29,6 +34,7 @@ public static function getMetaInformation()
'text_t' => 'text',
'created_at_dt' => 'created_at'
);
+ $metaInformation->setIdentifier(new Id(array()));
$metaInformation->setBoost(1);
$metaInformation->setFieldMapping($fieldMapping);
$metaInformation->setEntity($entity);
diff --git a/behat.phar b/behat.phar
deleted file mode 100644
index 48089f28..00000000
Binary files a/behat.phar and /dev/null differ
diff --git a/behat.yml b/behat.yml
deleted file mode 100644
index c13e6411..00000000
--- a/behat.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-default:
- paths:
- features: Tests/Integration/Features
- bootstrap: Tests/Integration/Bootstrap
\ No newline at end of file
diff --git a/composer.json b/composer.json
index 496f7d15..16cf4272 100644
--- a/composer.json
+++ b/composer.json
@@ -1,33 +1,49 @@
{
- "name": "floriansemm/solr-bundle",
- "type": "symfony-bundle",
- "description": "Symfony2 Solr integration bundle",
- "keywords": ["search", "index", "symfony", "solr"],
- "homepage": "https://github.com/floriansemm/SolrBundle",
- "license": "MIT",
- "require": {
- "php": ">=5.3.2",
- "solarium/solarium": "*"
- },
- "require-dev": {
- "doctrine/doctrine-module": "*",
- "doctrine/doctrine-orm-module": "*",
- "doctrine/mongodb": "*",
- "doctrine/mongodb-odm": "*",
- "symfony/dependency-injection": "*",
- "symfony/http-kernel": "*",
- "symfony/config": "*",
- "symfony/doctrine-bridge": "*"
- },
- "minimum-stability": "alpha",
- "autoload": {
- "psr-0": {
- "FS\\SolrBundle": ""
- }
- },
- "suggest": {
- "doctrine/mongodb": "Required if you want to use the MongoDB ODM features",
- "doctrine/mongodb-odm": "Required if you want to use the MongoDB ODM features"
- },
- "target-dir": "FS/SolrBundle"
+ "name": "floriansemm/solr-bundle",
+ "type": "symfony-bundle",
+ "description": "Symfony Solr integration bundle",
+ "keywords": [
+ "search",
+ "index",
+ "symfony",
+ "solr"
+ ],
+ "homepage": "https://github.com/floriansemm/SolrBundle",
+ "license": "MIT",
+ "require": {
+ "php": "^7.0",
+ "solarium/solarium": "^4.0",
+ "symfony/dependency-injection": "^2.3|^3.0|^4.0",
+ "symfony/http-kernel": "^2.3|^3.0|^4.0",
+ "symfony/config": "^2.3|^3.0|^4.0",
+ "symfony/doctrine-bridge": "^2.3|^3.0|^4.0",
+ "minimalcode/search": "^1.0",
+ "ramsey/uuid": "^3.5",
+ "myclabs/deep-copy": "^1.6",
+ "doctrine/annotations": "^1.4"
+ },
+ "require-dev": {
+ "doctrine/mongodb-odm-bundle": "*",
+ "doctrine/orm": "^2.3",
+ "phpunit/phpunit": "^5.4"
+ },
+ "minimum-stability": "RC",
+ "autoload": {
+ "psr-0": {
+ "FS\\SolrBundle": ""
+ }
+ },
+ "config": {
+ "bin-dir": "bin"
+ },
+ "extras": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "suggest": {
+ "doctrine/orm": "Required if you want to use the Doctrine ORM",
+ "doctrine/mongodb-odm-bundle": "Required if you want to use the MongoDB ODM"
+ },
+ "target-dir": "FS/SolrBundle"
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index c7fb3206..b6a426e3 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -10,7 +10,7 @@
stopOnFailure="false"
syntaxCheck="false"
bootstrap="Tests/bootstrap.php"
->
+ >