protected function Database::getFacets in Search API 8
Computes facets for a search query.
Parameters
\Drupal\search_api\Query\QueryInterface $query: The search query for which facets should be computed.
\Drupal\Core\Database\Query\SelectInterface $db_query: A database select query which returns all results of that search query.
int|null $result_count: (optional) The total number of results of the search query, if known.
Return value
array An array of facets, as specified by the search_api_facets feature.
1 call to Database::getFacets()
- Database::search in modules/
search_api_db/ src/ Plugin/ search_api/ backend/ Database.php - Executes a search on this server.
File
- modules/
search_api_db/ src/ Plugin/ search_api/ backend/ Database.php, line 2454
Class
- Database
- Indexes and searches items using the database.
Namespace
Drupal\search_api_db\Plugin\search_api\backendCode
protected function getFacets(QueryInterface $query, SelectInterface $db_query, $result_count = NULL) {
$fields = $this
->getFieldInfo($query
->getIndex());
$ret = [];
foreach ($query
->getOption('search_api_facets') as $key => $facet) {
if (empty($fields[$facet['field']])) {
$msg = $this
->t('Unknown facet field @field.', [
'@field' => $facet['field'],
]);
$this->warnings[(string) $msg] = 1;
continue;
}
$field = $fields[$facet['field']];
if (($facet['operator'] ?? 'and') != 'or') {
// First, check whether this can even possibly have any results.
if ($result_count !== NULL && $result_count < $facet['min_count']) {
continue;
}
// All the AND facets can use the main query. If we didn't yet create a
// temporary table for them yet, do so now.
if (!isset($table)) {
$table = $this
->getTemporaryResultsTable($db_query);
}
if ($table) {
$select = $this->database
->select($table, 't');
}
else {
// If no temporary table could be created (most likely due to a
// missing permission), use a nested query instead.
$select = $this->database
->select(clone $db_query, 't');
}
// In case we didn't get the result count passed to the method, we can
// get it now. (This allows us to skip AND facets with a min_count
// higher than the result count.)
if ($result_count === NULL) {
$result_count = $select
->countQuery()
->execute()
->fetchField();
if ($result_count < $facet['min_count']) {
continue;
}
}
}
else {
// For OR facets, we need to build a different base query that excludes
// the facet filters applied to the facet.
$or_query = clone $query;
$conditions =& $or_query
->getConditionGroup()
->getConditions();
$tag = 'facet:' . $facet['field'];
foreach ($conditions as $i => $condition) {
if ($condition instanceof ConditionGroupInterface && $condition
->hasTag($tag)) {
unset($conditions[$i]);
}
}
try {
$or_db_query = $this
->createDbQuery($or_query, $fields);
} catch (SearchApiException $e) {
$this
->logException($e, '%type while trying to create a facets query: @message in %function (line %line of %file).');
continue;
}
$select = $this->database
->select($or_db_query, 't');
}
// If "Include missing facet" is disabled, we use an INNER JOIN and add IS
// NOT NULL for shared tables.
$is_text_type = $this
->getDataTypeHelper()
->isTextType($field['type']);
$alias = $this
->getTableAlias($field, $select, TRUE, $facet['missing'] ? 'leftJoin' : 'innerJoin');
$select
->addField($alias, $is_text_type ? 'word' : 'value', 'value');
if ($is_text_type) {
$select
->condition($alias . '.field_name', $this
->getTextFieldName($facet['field']));
}
if (!$facet['missing'] && !$is_text_type) {
$select
->isNotNull($alias . '.value');
}
$select
->addExpression('COUNT(DISTINCT t.item_id)', 'num');
$select
->groupBy('value');
$select
->orderBy('num', 'DESC');
$select
->orderBy('value', 'ASC');
$limit = $facet['limit'];
if ((int) $limit > 0) {
$select
->range(0, $limit);
}
if ($facet['min_count'] > 1) {
$select
->having('COUNT(DISTINCT t.item_id) >= :count', [
':count' => $facet['min_count'],
]);
}
$terms = [];
$values = [];
$has_missing = FALSE;
foreach ($select
->execute() as $row) {
$terms[] = [
'count' => $row->num,
'filter' => $row->value !== NULL ? '"' . $row->value . '"' : '!',
];
if ($row->value !== NULL) {
$values[] = $row->value;
}
else {
$has_missing = TRUE;
}
}
// If 'Minimum facet count' is set to 0 in the display options for this
// facet, we need to retrieve all facets, even ones that aren't matched in
// our search result set above. Here we SELECT all DISTINCT facets, and
// add in those facets that weren't added above.
if ($facet['min_count'] < 1) {
$select = $this->database
->select($field['table'], 't');
$select
->addField('t', 'value', 'value');
$select
->distinct();
if ($values) {
$select
->condition('value', $values, 'NOT IN');
}
$select
->isNotNull('value');
foreach ($select
->execute() as $row) {
$terms[] = [
'count' => 0,
'filter' => '"' . $row->value . '"',
];
}
if ($facet['missing'] && !$has_missing) {
$terms[] = [
'count' => 0,
'filter' => '!',
];
}
}
$ret[$key] = $terms;
}
return $ret;
}