Skip to content
Snippets Groups Projects

Issue #2877319: Integrate database backend with Search API Location

1 file
+ 81
0
Compare changes
  • Side-by-side
  • Inline
@@ -16,11 +16,13 @@ use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\search_api\Backend\BackendPluginBase;
use Drupal\search_api\Contrib\AutocompleteBackendInterface;
use Drupal\search_api\DataType\DataTypePluginManager;
use Drupal\search_api\Entity\Index;
use Drupal\search_api\IndexInterface;
use Drupal\search_api\Item\Field;
use Drupal\search_api\Item\FieldInterface;
use Drupal\search_api\Item\ItemInterface;
use Drupal\search_api\Plugin\PluginFormTrait;
@@ -602,6 +604,13 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface
];
}
/**
* {@inheritdoc}
*/
public function supportsDataType($type) {
return $type === 'location';
}
/**
* {@inheritdoc}
*/
@@ -919,6 +928,18 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface
case 'boolean':
return ['type' => 'int', 'size' => 'tiny'];
case 'location':
// @todo #2877319 Find good way to index geo data so it can later be
// queried. Could also depend on DBMS used – in that case, add to
// \Drupal\search_api_db\DatabaseCompatibility\DatabaseCompatibilityHandlerInterface.
// E.g., for MySQL, see
// https://www.percona.com/sites/default/files/presentations/webinar-gis-03-15.pdf.
// If you need two columns (which is likely, I guess), you'll need to
// do some refactoring, though, or come up with another clever
// solution. But you could also add conditional support based on DBMS
// (and version).
return ['type' => 'varchar', 'length' => 255];
default:
throw new SearchApiException("Unknown field type '$type'.");
}
@@ -1539,6 +1560,8 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface
case 'boolean':
return $value ? 1 : 0;
case 'location':
return $value;
default:
throw new SearchApiException("Unknown field type '$type'.");
}
@@ -1784,6 +1807,12 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface
$condition_group = $query->getConditionGroup();
$this->addLanguageConditions($condition_group, $query);
// @todo #2877319 Maybe, like addLanguageConditions(), add a method here
// checking for the "search_api_location" option on the query and, if
// present, add new conditions based on the option(s) to the query.
// Or place the conditions directly on the query below this if block -
// in that case, you might need to remove any conditions on those fields
// from the query.
if ($condition_group->getConditions()) {
$condition = $this->createDbCondition($condition_group, $fields, $db_query, $query->getIndex());
if ($condition) {
@@ -2260,6 +2289,11 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface
$method = $not_equals ? 'isNotNull' : 'isNull';
$db_condition->$method($column);
}
elseif ($field_info['type'] === 'location') {
// @todo #2877319 Place filter on query, possibly also taking the
// options into account. Or, if this is handled somewhere else,
// simply ignore this condition.
}
elseif ($not_between) {
$nested_condition = $db_query->conditionGroupFactory('OR');
$nested_condition->condition($column, $value[0], '<');
@@ -2425,6 +2459,8 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface
}
$index_table = $this->getIndexDbInfo($query->getIndex())['index_table'];
$alias = $this->getTableAlias(['table' => $index_table], $db_query);
// @todo #2877319 Add special case for sorts on location fields, to sort
// by distance.
$db_query->orderBy($alias . '.' . $fields[$field_name]['column'], $order);
// PostgreSQL automatically adds a field to the SELECT list when
// sorting on it. Therefore, if we have aggregations present we also
@@ -2473,6 +2509,9 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface
}
$field = $fields[$facet['field']];
// @todo #2877319 This would most likely also need a special case for
// location fields.
if (($facet['operator'] ?? 'and') != 'or') {
// First, check whether this can even possibly have any results.
if ($result_count !== NULL && $result_count < $facet['min_count']) {
@@ -2841,6 +2880,48 @@ class Database extends BackendPluginBase implements AutocompleteBackendInterface
return $db_info;
}
/**
* {@inheritdoc}
*
* @throws \Drupal\search_api\SearchApiException
*/
public function getBackendDefinedFields(IndexInterface $index) {
$backend_defined_fields = [];
foreach ($index->getFields() as $field) {
if ($field->getType() === 'location') {
$distance_field_name = $field->getFieldIdentifier() . '__distance';
$property_path_name = $field->getPropertyPath() . '__distance';
$distance_field = new Field($index, $distance_field_name);
$distance_field->setLabel($field->getLabel() . ' (distance)');
$distance_field->setDataDefinition(DataDefinition::create('decimal'));
$distance_field->setType('decimal');
$distance_field->setDatasourceId($field->getDatasourceId());
$distance_field->setPropertyPath($property_path_name);
$backend_defined_fields[$distance_field_name] = $distance_field;
}
}
// @todo #2877319 fix why getType() return 'string' and not 'location'?
foreach ($index->getFields() as $field) {
if ($field->getFieldIdentifier() === 'field_geofield') {
$distance_field_name = $field->getFieldIdentifier() . '__distance';
$property_path_name = $field->getPropertyPath() . '__distance';
$distance_field = new Field($index, $distance_field_name);
$distance_field->setLabel($field->getLabel() . ' (distance)');
$distance_field->setDataDefinition(DataDefinition::create('decimal'));
$distance_field->setType('decimal');
$distance_field->setDatasourceId($field->getDatasourceId());
$distance_field->setPropertyPath($property_path_name);
$backend_defined_fields[$distance_field_name] = $distance_field;
}
}
return $backend_defined_fields;
}
/**
* Implements the magic __sleep() method.
*
Loading