/** * Adds facet queries to $query from setup in TypoScript. * Provides the facet setup enriched with the default values when no configuration * is present in the »facets« template variable. */ protected function addFacetQueries() { $facetConfiguration = $this->settings['facets']; if ($facetConfiguration) { $facetSet = $this->query->getFacetSet(); foreach ($facetConfiguration as $key => $facet) { if (array_key_exists('id', $facet)) { $facetID = $facet['id']; // start with defaults and overwrite with specific facet configuration $facet = array_merge($this->settings['facetDefaults'], $facet); $facetConfiguration[$key] = $facet; $queryForFacet = null; if (array_key_exists('facetQuery', $facet)) { $queryForFacet = $facetSet->createFacetMultiQuery($facetID); foreach ($facet['facetQuery'] as $facetQueryIndex => $facetQuery) { if (array_key_exists('id', $facetQuery) && array_key_exists('query', $facetQuery)) { $queryForFacet->createQuery($facetQuery['id'], $facetQuery['query']); } else { LoggerUtility::logError(sprintf('find: TypoScript facet »%s«, facetQuery %s does not have the required keys »id« and »query«. Ignoring this facetQuery.', $facetID, $facetQueryIndex), ['facetQuery' => $facetQuery, 'facetConfiguration' => $facetConfiguration]); } } } else { $queryForFacet = $facetSet->createFacetField($facetID); $queryForFacet->setField($facet['field'] ? $facet['field'] : $facetID)->setMinCount($facet['fetchMinimum'])->setLimit($facet['fetchMaximum'])->setSort($facet['sortOrder']); } if ($facet['excludeOwnFilter'] == 1) { $queryForFacet->addExclude($this->tagForFacet($facetID)); } } else { LoggerUtility::logWarning(sprintf('find: TypoScript facet %s does not have the required key »id«. Ignoring this facet.', $key), ['facet' => $facet, 'facetConfiguration' => $facetConfiguration]); } } } $this->setConfigurationValue('facets', $facetConfiguration); }
public function testParseWithoutNumFound() { $data = array('response' => array('docs' => array(array('fieldA' => 1, 'fieldB' => 'Test'), array('fieldA' => 2, 'fieldB' => 'Test2'))), 'responseHeader' => array('status' => 1, 'QTime' => 13)); $query = new Query(array('documentclass' => 'Solarium\\QueryType\\Update\\Query\\Document\\Document')); $query->getFacetSet(); $resultStub = $this->getMock('Solarium\\QueryType\\Select\\Result\\Result', array(), array(), '', false); $resultStub->expects($this->once())->method('getData')->will($this->returnValue($data)); $resultStub->expects($this->once())->method('getQuery')->will($this->returnValue($query)); $parser = new ResponseParser(); $result = $parser->parse($resultStub); $this->assertEquals(1, $result['status']); $this->assertEquals(13, $result['queryTime']); $this->assertEquals(null, $result['numfound']); }
public function testConfigMode() { $config = array('query' => 'text:mykeyword', 'sort' => array('score' => 'asc'), 'fields' => array('id', 'title', 'category'), 'rows' => 100, 'start' => 200, 'filterquery' => array(array('key' => 'pub', 'tag' => array('pub'), 'query' => 'published:true'), 'online' => array('tag' => 'onl', 'query' => 'online:true')), 'component' => array('facetset' => array('facet' => array(array('type' => 'field', 'key' => 'categories', 'field' => 'category'), 'category13' => array('type' => 'query', 'query' => 'category:13')))), 'resultclass' => 'MyResultClass', 'documentclass' => 'MyDocumentClass', 'tag' => array('t1', 't2')); $query = new Query($config); $this->assertEquals($config['query'], $query->getQuery()); $this->assertEquals($config['sort'], $query->getSorts()); $this->assertEquals($config['fields'], $query->getFields()); $this->assertEquals($config['rows'], $query->getRows()); $this->assertEquals($config['start'], $query->getStart()); $this->assertEquals($config['documentclass'], $query->getDocumentClass()); $this->assertEquals($config['resultclass'], $query->getResultClass()); $this->assertEquals('published:true', $query->getFilterQuery('pub')->getQuery()); $this->assertEquals('online:true', $query->getFilterQuery('online')->getQuery()); $facets = $query->getFacetSet()->getFacets(); $this->assertEquals('category', $facets['categories']->getField()); $this->assertEquals('category:13', $facets['category13']->getQuery()); $components = $query->getComponents(); $this->assertEquals(1, count($components)); $this->assertThat(array_pop($components), $this->isInstanceOf('Solarium\\QueryType\\Select\\Query\\Component\\FacetSet')); $this->assertEquals(array('t1', 't2'), $query->getTags()); }
public static function addNoExcludeFacet(Query $query, $key) { return $query->getFacetSet()->createFacetField(['field' => $key, 'key' => $key . self::NO_EXCLUDE_SUFFIX]); }
/** * Add facet fields to the solarium query * * @param Query $solarium_query * @param array $field_names * @param int $max_nb_items_by_facet Maximum items by facet * @param int $min_count_by_facet Do not return facet elements with less than this minimum count */ public function add_facet_fields(Query $solarium_query, $facets_parameters) { // Field names $field_names = isset($facets_parameters[self::PARAMETER_FACET_FIELD_NAMES]) ? $facets_parameters[self::PARAMETER_FACET_FIELD_NAMES] : array(); // Limit $limit = isset($facets_parameters[self::PARAMETER_FACET_LIMIT]) ? $facets_parameters[self::PARAMETER_FACET_LIMIT] : self::DEFAULT_MAX_NB_ITEMS_BY_FACET; // Min count $min_count = isset($facets_parameters[self::PARAMETER_FACET_MIN_COUNT]) ? $facets_parameters[self::PARAMETER_FACET_MIN_COUNT] : self::DEFAULT_MIN_COUNT_BY_FACET; if (count($field_names)) { $facetSet = $solarium_query->getFacetSet(); // Only display facets that contain data $facetSet->setMinCount($min_count); foreach ($field_names as $facet) { $fact = strtolower($facet); // Field 'categories' are now treated as other fields (dynamic string type) if (WpSolrSchema::_FIELD_NAME_CATEGORIES === $fact) { $fact = WpSolrSchema::_FIELD_NAME_CATEGORIES_STR; } // Add the facet $facetSet->createFacetField("{$fact}")->setField("{$fact}")->setLimit($limit); } } }
/** * Adds spatial features to the search query. */ public function setSpatial(Query $solarium_query, QueryInterface $query, $spatial_options = array(), $field_names) { foreach ($spatial_options as $i => $spatial) { // reset radius for each option unset($radius); if (empty($spatial['field']) || empty($spatial['lat']) || empty($spatial['lon'])) { continue; } $field = $field_names[$spatial['field']]; $escaped_field = SearchApiSolrUtility::escapeFieldName($field); $point = (double) $spatial['lat'] . ',' . (double) $spatial['lon']; // Prepare the filter settings. if (isset($spatial['radius'])) { $radius = (double) $spatial['radius']; } $spatial_method = 'geofilt'; if (isset($spatial['method']) && in_array($spatial['method'], array('geofilt', 'bbox'))) { $spatial_method = $spatial['method']; } $filter_queries = $solarium_query->getFilterQueries(); // Change the fq facet ranges to the correct fq. foreach ($filter_queries as $key => $filter_query) { // If the fq consists only of a filter on this field, replace it with // a range. $preg_field = preg_quote($escaped_field, '/'); if (preg_match('/^' . $preg_field . ':\\["?(\\*|\\d+(?:\\.\\d+)?)"? TO "?(\\*|\\d+(?:\\.\\d+)?)"?\\]$/', $filter_query, $matches)) { unset($filter_queries[$key]); if ($matches[1] && is_numeric($matches[1])) { $min_radius = isset($min_radius) ? max($min_radius, $matches[1]) : $matches[1]; } if (is_numeric($matches[2])) { // Make the radius tighter accordingly. $radius = isset($radius) ? min($radius, $matches[2]) : $matches[2]; } } } // If either a radius was given in the option, or a filter was // encountered, set a filter for the lowest value. If a lower boundary // was set (too), we can only set a filter for that if the field name // doesn't contains any colons. if (isset($min_radius) && strpos($field, ':') === FALSE) { $upper = isset($radius) ? " u={$radius}" : ''; $solarium_query->createFilterQuery($field)->setQuery("{!frange l={$min_radius}{$upper}}geodist({$field},{$point})"); } elseif (isset($radius)) { $solarium_query->createFilterQuery($field)->setQuery("{!{$spatial_method} pt={$point} sfield={$field} d={$radius}}"); } // @todo: Check if this object returns the correct value $sorts = $solarium_query->getSorts(); // Change sort on the field, if set (and not already changed). if (isset($sorts[$spatial['field']]) && substr($sorts[$spatial['field']], 0, strlen($field)) === $field) { $sorts[$spatial['field']] = str_replace($field, "geodist({$field},{$point})", $sorts[$spatial['field']]); } // Change the facet parameters for spatial fields to return distance // facets. $facets = $solarium_query->getFacetSet(); // @todo: Fix this so it takes it from the solarium query if (!empty($facets)) { if (!empty($facet_params['facet.field'])) { $facet_params['facet.field'] = array_diff($facet_params['facet.field'], array($field)); } foreach ($facets as $delta => $facet) { if ($facet['field'] != $spatial['field']) { continue; } $steps = $facet['limit'] > 0 ? $facet['limit'] : 5; $step = (isset($radius) ? $radius : 100) / $steps; for ($k = $steps - 1; $k > 0; --$k) { $distance = $step * $k; $key = "spatial-{$delta}-{$distance}"; $facet_params['facet.query'][] = "{!{$spatial_method} pt={$point} sfield={$field} d={$distance} key={$key}}"; } foreach (array('limit', 'mincount', 'missing') as $setting) { unset($facet_params["f.{$field}.facet.{$setting}"]); } } } } // Normal sorting on location fields isn't possible. foreach (array_keys($solarium_query->getSorts()) as $sort) { if (substr($sort, 0, 3) === 'loc') { $solarium_query->removeSort($sort); } } }
/** * Helper method for creating the facet field parameters. */ protected function setFacets(array $facets, array $field_names, Query $solarium_query) { $fq = array(); if (!$facets) { return; } $facet_set = $solarium_query->getFacetSet(); $facet_set->setSort('count'); $facet_set->setLimit(10); $facet_set->setMinCount(1); $facet_set->setMissing(FALSE); $taggedFields = array(); foreach ($facets as $info) { if (empty($field_names[$info['field']])) { continue; } // String fields have their own corresponding facet fields. $field = $field_names[$info['field']]; // Check for the "or" operator. if (isset($info['operator']) && $info['operator'] === 'or') { // Remember that filters for this field should be tagged. $escaped = SearchApiSolrUtility::escapeFieldName($field_names[$info['field']]); $taggedFields[$escaped] = "{!tag={$escaped}}"; // Add the facet field. $facet_field = $facet_set->createFacetField($field)->setField("{!ex={$escaped}}{$field}"); } else { // Add the facet field. $facet_field = $facet_set->createFacetField($field)->setField($field); } // Set limit, unless it's the default. if ($info['limit'] != 10) { $limit = $info['limit'] ? $info['limit'] : -1; $facet_field->setLimit($limit); } // Set mincount, unless it's the default. if ($info['min_count'] != 1) { $facet_field->setMinCount($info['min_count']); } // Set missing, if specified. if ($info['missing']) { $facet_field->setMissing(TRUE); } } // Tag filters of fields with "OR" facets. foreach ($taggedFields as $field => $tag) { $regex = '#(?<![^( ])' . preg_quote($field, '#') . ':#'; foreach ($fq as $i => $conditions) { // Solr can't handle two tags on the same filter, so we don't add two. // Another option here would even be to remove the other tag, too, // since we can be pretty sure that this filter does not originate from // a facet – however, wrong results would still be possible, and this is // definitely an edge case, so don't bother. if (preg_match($regex, $conditions) && substr($conditions, 0, 6) != '{!tag=') { $fq[$i] = $tag . $conditions; } } } foreach ($fq as $key => $conditions_query) { $solarium_query->createFilterQuery('facets_' . $key)->setQuery($conditions_query); } }
/** * Add facet fields to the solarium query * * @param Query $solarium_query * @param array $field_names * @param int $max_nb_items_by_facet Maximum items by facet * @param int $min_count_by_facet Do not return facet elements with less than this minimum count */ public function add_facet_fields(Query $solarium_query, $facets_parameters) { // Field names $field_names = isset($facets_parameters[self::PARAMETER_FACET_FIELD_NAMES]) ? $facets_parameters[self::PARAMETER_FACET_FIELD_NAMES] : array(); // Limit $limit = isset($facets_parameters[self::PARAMETER_FACET_LIMIT]) ? $facets_parameters[self::PARAMETER_FACET_LIMIT] : self::DEFAULT_MAX_NB_ITEMS_BY_FACET; // Min count $min_count = isset($facets_parameters[self::PARAMETER_FACET_MIN_COUNT]) ? $facets_parameters[self::PARAMETER_FACET_MIN_COUNT] : self::DEFAULT_MIN_COUNT_BY_FACET; if (count($field_names)) { $facetSet = $solarium_query->getFacetSet(); // Only display facets that contain data $facetSet->setMinCount($min_count); foreach ($field_names as $facet) { $fact = $this->get_facet_hierarchy_name(WpSolrSchema::_FIELD_NAME_FLAT_HIERARCHY, $facet); // Add the facet $facetSet->createFacetField(array('exclude' => $facet, 'key' => "{$fact}"))->setField("{$fact}"); if (!empty($limit)) { $facetSet->setLimit($limit); } } } }