IndexPackagesCommand.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <?php
  2. /*
  3. * This file is part of Packagist.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. * Nils Adermann <naderman@naderman.de>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Packagist\WebBundle\Command;
  12. use Packagist\WebBundle\Entity\Package;
  13. use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
  14. use Symfony\Component\Console\Input\InputInterface;
  15. use Symfony\Component\Console\Input\InputOption;
  16. use Symfony\Component\Console\Input\InputArgument;
  17. use Symfony\Component\Console\Output\OutputInterface;
  18. /**
  19. * @author Igor Wiedler <igor@wiedler.ch>
  20. */
  21. class IndexPackagesCommand extends ContainerAwareCommand
  22. {
  23. /**
  24. * {@inheritdoc}
  25. */
  26. protected function configure()
  27. {
  28. $this
  29. ->setName('packagist:index')
  30. ->setDefinition(array(
  31. new InputOption('force', null, InputOption::VALUE_NONE, 'Force a re-indexing of all packages'),
  32. new InputOption('all', null, InputOption::VALUE_NONE, 'Index all packages without clearing the index first'),
  33. new InputArgument('package', InputArgument::OPTIONAL, 'Package name to index'),
  34. ))
  35. ->setDescription('Indexes packages in Solr')
  36. ;
  37. }
  38. /**
  39. * {@inheritdoc}
  40. */
  41. protected function execute(InputInterface $input, OutputInterface $output)
  42. {
  43. $verbose = $input->getOption('verbose');
  44. $force = $input->getOption('force');
  45. $indexAll = $input->getOption('all');
  46. $package = $input->getArgument('package');
  47. $deployLock = $this->getContainer()->getParameter('kernel.cache_dir').'/deploy.globallock';
  48. if (file_exists($deployLock)) {
  49. if ($verbose) {
  50. $output->writeln('Aborting, '.$deployLock.' file present');
  51. }
  52. return;
  53. }
  54. $doctrine = $this->getContainer()->get('doctrine');
  55. $solarium = $this->getContainer()->get('solarium.client');
  56. $redis = $this->getContainer()->get('snc_redis.default');
  57. $lock = $this->getContainer()->getParameter('kernel.cache_dir').'/composer-indexer.lock';
  58. $timeout = 600;
  59. // another dumper is still active
  60. if (file_exists($lock) && filemtime($lock) > time() - $timeout) {
  61. if ($verbose) {
  62. $output->writeln('Aborting, '.$lock.' file present');
  63. }
  64. return;
  65. }
  66. touch($lock);
  67. if ($package) {
  68. $packages = array(array('id' => $doctrine->getRepository('PackagistWebBundle:Package')->findOneByName($package)->getId()));
  69. } elseif ($force || $indexAll) {
  70. $packages = $doctrine->getManager()->getConnection()->fetchAll('SELECT id FROM package ORDER BY id ASC');
  71. $doctrine->getManager()->getConnection()->executeQuery('UPDATE package SET indexedAt = NULL');
  72. } else {
  73. $packages = $doctrine->getRepository('PackagistWebBundle:Package')->getStalePackagesForIndexing();
  74. }
  75. $ids = array();
  76. foreach ($packages as $row) {
  77. $ids[] = $row['id'];
  78. }
  79. // clear index before a full-update
  80. if ($force && !$package) {
  81. if ($verbose) {
  82. $output->writeln('Deleting existing index');
  83. }
  84. $update = $solarium->createUpdate();
  85. $update->addDeleteQuery('*:*');
  86. $update->addCommit();
  87. $solarium->update($update);
  88. }
  89. $total = count($ids);
  90. $current = 0;
  91. // update package index
  92. while ($ids) {
  93. $packages = $doctrine->getRepository('PackagistWebBundle:Package')->getPackagesWithVersions(array_splice($ids, 0, 50));
  94. $update = $solarium->createUpdate();
  95. foreach ($packages as $package) {
  96. $current++;
  97. if ($verbose) {
  98. $output->writeln('['.sprintf('%'.strlen($total).'d', $current).'/'.$total.'] Indexing '.$package->getName());
  99. }
  100. try {
  101. $document = $update->createDocument();
  102. $this->updateDocumentFromPackage($document, $package, $redis);
  103. $update->addDocument($document);
  104. $package->setIndexedAt(new \DateTime);
  105. } catch (\Exception $e) {
  106. $output->writeln('<error>Exception: '.$e->getMessage().', skipping package '.$package->getName().'.</error>');
  107. }
  108. foreach ($package->getVersions() as $version) {
  109. // abort when a non-dev version shows up since dev ones are ordered first
  110. if (!$version->isDevelopment()) {
  111. break;
  112. }
  113. if (count($provide = $version->getProvide())) {
  114. foreach ($version->getProvide() as $provide) {
  115. try {
  116. $document = $update->createDocument();
  117. $document->setField('id', $provide->getPackageName());
  118. $document->setField('name', $provide->getPackageName());
  119. $document->setField('description', '');
  120. $document->setField('type', 'virtual-package');
  121. $document->setField('trendiness', 100);
  122. $document->setField('repository', '');
  123. $document->setField('abandoned', 0);
  124. $document->setField('replacementPackage', '');
  125. $update->addDocument($document);
  126. } catch (\Exception $e) {
  127. $output->writeln('<error>Exception: '.$e->getMessage().', skipping package '.$package->getName().':provide:'.$provide->getPackageName().'</error>');
  128. }
  129. }
  130. }
  131. }
  132. }
  133. $doctrine->getManager()->flush();
  134. $doctrine->getManager()->clear();
  135. unset($packages);
  136. $update->addCommit();
  137. $solarium->update($update);
  138. }
  139. unlink($lock);
  140. }
  141. private function updateDocumentFromPackage(\Solarium_Document_ReadWrite $document, Package $package, $redis)
  142. {
  143. $document->setField('id', $package->getId());
  144. $document->setField('name', $package->getName());
  145. $document->setField('description', $package->getDescription());
  146. $document->setField('type', $package->getType());
  147. $document->setField('trendiness', $redis->zscore('downloads:trending', $package->getId()));
  148. $document->setField('repository', $package->getRepository());
  149. if ($package->isAbandoned()) {
  150. $document->setField('abandoned', 1);
  151. $document->setField('replacementPackage', $package->getReplacementPackage() ?: '');
  152. } else {
  153. $document->setField('abandoned', 0);
  154. $document->setField('replacementPackage', '');
  155. }
  156. $tags = array();
  157. foreach ($package->getVersions() as $version) {
  158. foreach ($version->getTags() as $tag) {
  159. $tags[mb_strtolower($tag->getName(), 'UTF-8')] = true;
  160. }
  161. }
  162. $document->setField('tags', array_keys($tags));
  163. }
  164. }