/**
  * @test
  */
 public function databaseResultIsUsedWhenNoCachedResultIsPresent()
 {
     $fakeConfiguration = array('select.' => array('checkRootPageId' => true, 'checkLanguage' => true, 'SELECT' => '', 'FROM' => '', 'ADD_WHERE' => '', 'GROUP_BY' => '', 'ORDER_BY' => ''), 'limit');
     $this->configurationMock->expects($this->once())->method('getSearchFrequentSearchesConfiguration')->will($this->returnValue($fakeConfiguration));
     $this->databaseMock->expects($this->once())->method('exec_SELECTgetRows')->will($this->returnValue(array(array('search_term' => 'my search', 'hits' => 22))));
     //we fake that we have no frequent searches in the cache and therefore expect that the database will be queried
     $this->fakeIdentifierNotInCache();
     $frequentTerms = $this->frequentSearchesService->getFrequentSearchTerms();
     $this->assertSame($frequentTerms, array('my search' => 22), 'Could not retrieve frequent search terms');
 }
Example #2
0
 /**
  * Returns an URL that switches sorting to the given sort option
  *
  * @param array $arguments
  * @return string
  */
 public function execute(array $arguments = array())
 {
     $sortUrl = '';
     $urlParameters = GeneralUtility::_GP('tx_solr');
     $urlSortParameters = GeneralUtility::trimExplode(',', $urlParameters['sort']);
     $sortOptions = GeneralUtility::trimExplode(',', $arguments[0]);
     $currentSortOption = '';
     $sortHelper = GeneralUtility::makeInstance('ApacheSolrForTypo3\\Solr\\Sorting', $this->configuration->getValueByPathOrDefaultValue('plugin.tx_solr.search.sorting.options.', array()));
     $configuredSortOptions = $sortHelper->getSortOptions();
     $sortParameters = array();
     foreach ($sortOptions as $sortOption) {
         if (isset($configuredSortOptions[$sortOption])) {
             $sortDirection = $this->configuration->getValueByPathOrDefaultValue('plugin.tx_solr.search.sorting.defaultOrder', 'asc');
             if (isset($configuredSortOptions[$sortOption]['fixedOrder'])) {
                 $sortDirection = $configuredSortOptions[$sortOption]['fixedOrder'];
             } elseif (isset($configuredSortOptions[$sortOption]['defaultOrder'])) {
                 $sortDirection = $configuredSortOptions[$sortOption]['defaultOrder'];
             }
             $sortParameter = $sortOption . ' ' . $sortDirection;
             foreach ($urlSortParameters as $urlSortParameter) {
                 $explodedUrlSortParameter = explode(' ', $urlSortParameter);
                 if ($explodedUrlSortParameter[0] == $sortOption) {
                     list($currentSortOption, $currentSortDirection) = $explodedUrlSortParameter;
                     break;
                 }
             }
             if ($currentSortOption == $sortOption) {
                 switch ($currentSortDirection) {
                     case 'asc':
                         $sortDirection = 'desc';
                         break;
                     case 'desc':
                         $sortDirection = 'asc';
                         break;
                 }
                 $fixedOrder = $this->configuration->getValueByPath('plugin.tx_solr.search.sorting.options.' . $sortOption . '.fixedOrder');
                 if (!is_null($fixedOrder)) {
                     $sortDirection = $fixedOrder;
                 }
                 $sortParameter = $sortOption . ' ' . $sortDirection;
             }
             $sortParameters[] = $sortParameter;
         }
     }
     $sortUrl = $this->queryLinkBuilder->getQueryUrl(array('sort' => implode(', ', $sortParameters)));
     return $sortUrl;
 }
 /**
  * Gets the sequence id for the next search entry.
  *
  * @return int The id to be used as the next sequence id for storing the last search keywords.
  */
 protected function getNextSequenceId()
 {
     $nextSequenceId = 0;
     $numberOfLastSearchesToLog = (int) $this->configuration->getSearchLastSearchesLimit();
     $row = $this->database->exec_SELECTgetRows('(sequence_id + 1) % ' . $numberOfLastSearchesToLog . ' as next_sequence_id', 'tx_solr_last_searches', '', '', 'tstamp DESC', 1);
     if (!empty($row)) {
         $nextSequenceId = $row[0]['next_sequence_id'];
     }
     return $nextSequenceId;
 }
Example #4
0
 /**
  * A factory method to get an indexer depending on an item's configuration.
  *
  * By default all items are indexed using the default indexer
  * (ApacheSolrForTypo3\Solr\IndexQueue\Indexer) coming with EXT:solr. Pages by default are
  * configured to be indexed through a dedicated indexer
  * (ApacheSolrForTypo3\Solr\IndexQueue\PageIndexer). In all other cases a dedicated indexer
  * can be specified through TypoScript if needed.
  *
  * @param string $indexingConfigurationName Indexing configuration name.
  * @throws \RuntimeException
  * @return Indexer An instance of ApacheSolrForTypo3\Solr\IndexQueue\Indexer or a sub class of it.
  */
 protected function getIndexerByItem($indexingConfigurationName)
 {
     $indexerClass = $this->configuration->getIndexQueueIndexerByConfigurationName($indexingConfigurationName);
     $indexerConfiguration = $this->configuration->getIndexQueueIndexerConfigurationByConfigurationName($indexingConfigurationName);
     $indexer = GeneralUtility::makeInstance($indexerClass, $indexerConfiguration);
     if (!$indexer instanceof Indexer) {
         throw new \RuntimeException('The indexer class "' . $indexerClass . '" for indexing configuration "' . $indexingConfigurationName . '" is not a valid indexer. Must be a subclass of ApacheSolrForTypo3\\Solr\\IndexQueue\\Indexer.', 1260463206);
     }
     return $indexer;
 }
Example #5
0
 /**
  * Activates the collapsing on the configured field, if collapsing was enabled.
  *
  * @return bool
  */
 protected function initializeCollapsingFromConfiguration()
 {
     // check collapsing
     if ($this->solrConfiguration->getSearchVariants()) {
         $collapseField = $this->solrConfiguration->getSearchVariantsField();
         $this->setVariantField($collapseField);
         $this->setCollapsing(true);
         return true;
     }
     return false;
 }
Example #6
0
 /**
  * Enables or disables spellchecking for the query.
  *
  * @param boolean $spellchecking Enables spellchecking when set to TRUE, deactivates spellchecking when set to FALSE, defaults to TRUE.
  */
 public function setSpellchecking($spellchecking = true)
 {
     if ($spellchecking) {
         $this->queryParameters['spellcheck'] = 'true';
         $this->queryParameters['spellcheck.collate'] = 'true';
         $maxCollationTries = $this->solrConfiguration->getSearchSpellcheckingNumberOfSuggestionsToTry();
         $this->addQueryParameter('spellcheck.maxCollationTries', $maxCollationTries);
     } else {
         unset($this->queryParameters['spellcheck']);
         unset($this->queryParameters['spellcheck.collate']);
         unset($this->queryParameters['spellcheck.maxCollationTries']);
     }
 }
 /**
  * Generates an array with terms and hits
  *
  * @return array Tags as array with terms and hits
  */
 public function getFrequentSearchTerms()
 {
     $frequentSearchConfiguration = $this->configuration->getSearchFrequentSearchesConfiguration();
     $terms = array();
     $identifier = $this->getCacheIdentifier($frequentSearchConfiguration);
     if ($this->cache->has($identifier)) {
         $terms = $this->cache->get($identifier);
     } else {
         $terms = $this->getFrequentSearchTermsFromStatistics($frequentSearchConfiguration);
         if ($frequentSearchConfiguration['sortBy'] == 'hits') {
             arsort($terms);
         } else {
             ksort($terms);
         }
         $lifetime = null;
         if (isset($frequentSearchConfiguration['cacheLifetime'])) {
             $lifetime = intval($frequentSearchConfiguration['cacheLifetime']);
         }
         $this->cache->set($identifier, $terms, array(), $lifetime);
     }
     return $terms;
 }
Example #8
0
 /**
  * @test
  */
 public function testAdditionalFiltersGetPassedToTheQuery()
 {
     $this->fakeRegisteredSearchComponents(array());
     $fakeResponse = $this->getDumbMock('\\Apache_Solr_Response');
     $this->assertOneSearchWillBeTriggeredWithQueryAndShouldReturnFakeResponse('test', 0, $fakeResponse);
     $this->configurationMock->expects($this->any())->method('getSearchQueryFilterConfiguration')->will($this->returnValue(array('type:pages')));
     $fakeRequest = new SearchRequest(array('q' => 'test'));
     $this->assertOneSearchWillBeTriggeredWithQueryAndShouldReturnFakeResponse('test', 0, $fakeResponse);
     $this->assertPerPageInSessionWillNotBeChanged();
     $resultSet = $this->searchResultSetService->search($fakeRequest);
     $this->assertSame($resultSet->getResponse(), $fakeResponse, 'Did not get the expected fakeResponse');
     $this->assertSame(count($resultSet->getUsedQuery()->getFilters()), 1, 'There should be one registered filter in the query');
 }
Example #9
0
 /**
  * This method is used to apply htmlspecialchars on all document fields that
  * are not configured to be secure. Secure mean that we know where the content is comming from.
  *
  * @param array $documents
  * @return \Apache_Solr_Document[]
  */
 protected function applyHtmlSpecialCharsOnAllFields(array $documents)
 {
     $trustedSolrFields = $this->configuration->getSearchTrustedFieldsArray();
     foreach ($documents as $key => $document) {
         $fieldNames = $document->getFieldNames();
         foreach ($fieldNames as $fieldName) {
             if (in_array($fieldName, $trustedSolrFields)) {
                 // we skip this field, since it was marked as secure
                 continue;
             }
             $document->{$fieldName} = $this->applyHtmlSpecialCharsOnSingleFieldValue($document->{$fieldName});
         }
         $documents[$key] = $document;
     }
     return $documents;
 }
Example #10
0
 /**
  * Adds filters specified through HTTP GET as filter query parameters to
  * the Solr query.
  *
  */
 protected function addFacetQueryFilters()
 {
     // todo refactor to use a request object
     $resultParameters = GeneralUtility::_GET('tx_solr');
     // format for filter URL parameter:
     // tx_solr[filter]=$facetName0:$facetValue0,$facetName1:$facetValue1,$facetName2:$facetValue2
     if (is_array($resultParameters['filter'])) {
         $filters = array_map('urldecode', $resultParameters['filter']);
         // $filters look like array('name:value1','name:value2','fieldname2:foo')
         $configuredFacets = $this->getConfiguredFacets();
         // first group the filters by facetName - so that we can
         // decide later whether we need to do AND or OR for multiple
         // filters for a certain facet/field
         // $filtersByFacetName look like array('name' => array ('value1', 'value2'), 'fieldname2' => array('foo'))
         $filtersByFacetName = array();
         foreach ($filters as $filter) {
             // only split by the first colon to allow using colons in the filter value itself
             list($filterFacetName, $filterValue) = explode(':', $filter, 2);
             if (in_array($filterFacetName, $configuredFacets)) {
                 $filtersByFacetName[$filterFacetName][] = $filterValue;
             }
         }
         foreach ($filtersByFacetName as $facetName => $filterValues) {
             $facetConfiguration = $this->allConfiguredFacets[$facetName . '.'];
             $filterEncoder = $this->facetRendererFactory->getFacetFilterEncoderByFacetName($facetName);
             $tag = '';
             if ($facetConfiguration['keepAllOptionsOnSelection'] == 1 || $this->configuration->getSearchFacetingKeepAllFacetsOnSelection()) {
                 $tag = '{!tag=' . addslashes($facetConfiguration['field']) . '}';
             }
             $filterParts = array();
             foreach ($filterValues as $filterValue) {
                 if (!is_null($filterEncoder)) {
                     $filterOptions = $facetConfiguration[$facetConfiguration['type'] . '.'];
                     if (empty($filterOptions)) {
                         $filterOptions = array();
                     }
                     $filterValue = $filterEncoder->decodeFilter($filterValue, $filterOptions);
                     $filterParts[] = $facetConfiguration['field'] . ':' . $filterValue;
                 } else {
                     $filterParts[] = $facetConfiguration['field'] . ':"' . addslashes($filterValue) . '"';
                 }
             }
             $operator = $facetConfiguration['operator'] == 'OR' ? ' OR ' : ' AND ';
             $this->facetFilters[] = $tag . '(' . implode($operator, $filterParts) . ')';
         }
     }
 }
 /**
  * @test
  */
 public function testExpandedDocumentsGetAddedWhenVariantsAreConfigured()
 {
     // we fake that collapsing is enabled
     $this->configurationMock->expects($this->atLeastOnce())->method('getSearchVariants')->will($this->returnValue(true));
     // in this case we collapse on the type field
     $this->configurationMock->expects($this->atLeastOnce())->method('getSearchVariantsField')->will($this->returnValue('type'));
     $this->fakeRegisteredSearchComponents(array());
     $fakedSolrResponse = $this->getFixtureContent('fakeCollapsedResponse.json');
     $fakeHttpResponse = $this->getDumbMock('\\Apache_Solr_HttpTransport_Response');
     $fakeHttpResponse->expects($this->once())->method('getBody')->will($this->returnValue($fakedSolrResponse));
     $fakeResponse = new \Apache_Solr_Response($fakeHttpResponse);
     $this->assertOneSearchWillBeTriggeredWithQueryAndShouldReturnFakeResponse('variantsSearch', 0, $fakeResponse);
     $fakeRequest = new SearchRequest(array('q' => 'variantsSearch'));
     $resultSet = $this->searchResultSetService->search($fakeRequest);
     $this->assertSame(1, count($resultSet->getResponse()->response->docs), 'Unexpected amount of document');
     /** @var  $fistResult SearchResult */
     $fistResult = $resultSet->getResponse()->response->docs[0];
     $this->assertSame(5, count($fistResult->getVariants()), 'Unexpected amount of expanded result');
 }
Example #12
0
 /**
  * Retrieves the name of the  Indexing Queue Configuration for a record
  *
  * @param string $recordTable Table to read from
  * @param int $recordUid Id of the record
  * @return string Name of indexing configuration
  */
 protected function getIndexingConfigurationName($recordTable, $recordUid)
 {
     $name = $recordTable;
     $indexingConfigurations = $this->solrConfiguration->getEnabledIndexQueueConfigurationNames();
     foreach ($indexingConfigurations as $indexingConfigurationName) {
         if (!$this->solrConfiguration->getIndexQueueConfigurationIsEnabled($indexingConfigurationName)) {
             // ignore disabled indexing configurations
             continue;
         }
         $tableToIndex = $this->solrConfiguration->getIndexQueueTableNameOrFallbackToConfigurationName($indexingConfigurationName);
         if ($tableToIndex === $recordTable) {
             $recordWhereClause = $this->solrConfiguration->getIndexQueueAdditionalWhereClauseByConfigurationName($indexingConfigurationName);
             $record = BackendUtility::getRecord($recordTable, $recordUid, '*', $recordWhereClause);
             if (!empty($record)) {
                 // we found a record which matches the conditions
                 $name = $indexingConfigurationName;
                 // FIXME currently returns after the first configuration match
                 break;
             }
         }
     }
     return $name;
 }
Example #13
0
 /**
  * Gets the target page Id for links. Might have been set through either
  * flexform or TypoScript. If none is set, TSFE->id is used.
  *
  * @return int The page Id to be used for links
  */
 public function getLinkTargetPageId()
 {
     return $this->typoScriptConfiguration->getSearchTargetPage();
 }
 /**
  * @test
  */
 public function canGetIndexQueuePagesExcludeContentByClassArray()
 {
     $fakeConfigurationArray['plugin.']['tx_solr.'] = array('index.' => array('queue.' => array('pages.' => array('excludeContentByClass' => 'excludeClass'))));
     $configuration = new TypoScriptConfiguration($fakeConfigurationArray);
     $excludeClasses = $configuration->getIndexQueuePagesExcludeContentByClassArray();
     $this->assertEquals(array('excludeClass'), $excludeClasses, 'Can not get exclude patterns from configuration');
 }
 /**
  * @test
  */
 public function removePageSectionFilterIsKeepingOtherFilters()
 {
     $fakeConfigurationArray['plugin.']['tx_solr.'] = array('search.' => array('query.' => array('filter.' => array('type:pages', '__pageSections' => '1,2,3'))));
     $configuration = new TypoScriptConfiguration($fakeConfigurationArray);
     $this->assertEquals(['type:pages', '__pageSections' => '1,2,3'], $configuration->getSearchQueryFilterConfiguration());
     $configuration->removeSearchQueryFilterForPageSections();
     $this->assertEquals(['type:pages'], $configuration->getSearchQueryFilterConfiguration());
 }
 /**
  *
  */
 public function __construct()
 {
     $this->configuration = Util::getSolrConfiguration();
     $this->highlightFields = $this->configuration->getSearchResultsHighlightingFieldsAsArray();
     $this->fragmentSeparator = $this->configuration->getSearchResultsHighlightingFragmentSeparator();
 }
 /**
  * @param TypoScriptConfiguration $configuration
  */
 public function __construct(TypoScriptConfiguration $configuration = null)
 {
     $this->configuration = $configuration == null ? Util::getSolrConfiguration() : $configuration;
     $this->pageIndexingConfiguration = $this->configuration->getIndexQueueFieldsConfigurationByConfigurationName('pages');
     $this->mappedFieldNames = $this->configuration->getIndexQueueMappedFieldsByConfigurationName('pages');
 }
 public function setUp()
 {
     $this->typoScripConfigurationMock = $this->getDumbMock('ApacheSolrForTypo3\\Solr\\System\\Configuration\\TypoScriptConfiguration');
     $this->typoScripConfigurationMock->expects($this->once())->method('getIndexQueuePagesExcludeContentByClassArray')->will($this->returnValue(array('typo3-search-exclude')));
 }
 /**
  * @param int $limit
  */
 protected function fakeLastSearchLimit($limit)
 {
     $this->configurationMock->expects($this->once())->method('getSearchLastSearchesLimit')->will($this->returnValue($limit));
 }