Skip to content

Commit

Permalink
Add spatial rpt functionalities
Browse files Browse the repository at this point in the history
  • Loading branch information
dbjpanda committed Aug 23, 2017
1 parent cf67096 commit c0590ca
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 8 deletions.
73 changes: 71 additions & 2 deletions src/Plugin/search_api/backend/SearchApiSolrBackend.php
Expand Up @@ -474,6 +474,7 @@ public function getSupportedFeatures() {
public function supportsDataType($type) {
return in_array($type, [
'location',
'rpt',
'solr_string_storage',
'solr_text_ngram',
'solr_text_phonetic',
Expand Down Expand Up @@ -980,6 +981,17 @@ public function search(QueryInterface $query) {
$this->setSpatial($solarium_query, $options['search_api_location'], $field_names);
}

// Handle spatial filters.
if (isset($options['search_api_rpt'])) {
if (version_compare($connector->getSolrVersion(), 5.1, '>=')) {
$this->setRpt($solarium_query, $options['search_api_rpt'], $field_names);
}
else {
\Drupal::logger('search_api_solr')->error('Rpt data type feature is only supported by Solr version 5.1 or higher.');
}

}

// Handle field collapsing / grouping.
$grouping_options = $query->getOption('search_api_grouping');
if (!empty($grouping_options['use_grouping'])) {
Expand Down Expand Up @@ -1562,14 +1574,36 @@ protected function extractFacets(QueryInterface $query, Result $resultset, array
}
$facet = $extract_facets[$matches[1]];
if ($count >= $facet['min_count']) {
$facets[$matches[1]][] = array(
$facets[$matches[1]][] = [
'filter' => "[{$matches[2]} {$matches[3]}]",
'count' => $count,
);
];
}
}
}
}
// Extract heatmaps.
if (isset($result_data['facet_counts']['facet_heatmaps'])) {
if ($spatials = $query->getOption('search_api_rpt')) {
foreach ($result_data['facet_counts']['facet_heatmaps'] as $key => $value) {
if (!preg_match('/^rpts_(.*)$/', $key, $matches)) {
continue;
}
if (empty($extract_facets[$matches[1]])) {
continue;
}
$heatmaps = array_slice($value, 15);
array_walk_recursive($heatmaps, function ($heatmaps) use (&$heatmap) {
$heatmap[] = $heatmaps;
});
$count = array_sum($heatmap);
$facets[$matches[1]][] = [
'filter' => $value,
'count' => $count,
];
}
}
}

return $facets;
}
Expand Down Expand Up @@ -2592,6 +2626,41 @@ protected function setSpatial(Query $solarium_query, array $spatial_options, $fi
}
}

/**
* Adds rpt spatial features to the search query.
*
* @param \Solarium\QueryType\Select\Query\Query $solarium_query
* The solr query.
* @param array $rpt_options
* The rpt spatial options to add.
* @param array $field_names
* The field names, to add the rpt spatial options for.
*
* @throws \Drupal\search_api_solr\SearchApiSolrException
* Thrown when more than one rpt spatial searches are added.
*/
protected function setRpt(Query $solarium_query, array $rpt_options, $field_names = array()) {
// Add location filter
if (count($rpt_options) > 1) {
throw new SearchApiSolrException('Only one spatial search can be handled per query.');
}

$rpt = reset($rpt_options);
$solr_field = $field_names[$rpt['field']];
$rpt['geom'] = isset($rpt['geom']) ? $rpt['geom'] : '["-180 -90" TO "180 90"]';

// Add location filter
$solarium_query->createFilterQuery($solr_field)->setQuery($solr_field . ':' . $rpt['geom']);

// Add Solr Query params
$solarium_query->addParam('facet', 'on');
$solarium_query->addParam('facet.heatmap', $solr_field);
$solarium_query->addParam('facet.heatmap.geom', $rpt['geom']);
$solarium_query->addParam('facet.heatmap.format', $rpt['format']);
$solarium_query->addParam('facet.heatmap.maxCells', $rpt['maxCells']);
$solarium_query->addParam('facet.heatmap.gridLevel', $rpt['gridLevel']);
}

/**
* Sets sorting for the query.
*/
Expand Down
4 changes: 4 additions & 0 deletions src/Utility/Utility.php
Expand Up @@ -87,6 +87,10 @@ public static function getDataTypeInfo($type = NULL) {
'geohash' => array(
'prefix' => 'geo',
),
// Provided by Search API Location module.
'rpt' => [
'prefix' => 'rpt',
],
);

// For the extra types, only add our extra info if it's already been
Expand Down
29 changes: 29 additions & 0 deletions tests/modules/search_api_solr_test/src/Logger/InMemoryLogger.php
@@ -0,0 +1,29 @@
<?php

namespace Drupal\search_api_solr_test\Logger;

use Psr\Log\AbstractLogger;


/**
* A simple in memory logger.
*/
class InMemoryLogger extends AbstractLogger {

private $messages = [];

/**
* {@inheritdoc}
*/
public function log($level, $message, array $context = []) {
$this->messages[] = [
'level' => $level,
'message' => $message,
'context' => $context,
];
}

public function getLastMessage() {
return end($this->messages);
}
}
144 changes: 138 additions & 6 deletions tests/src/Kernel/SearchApiSolrLocationTest.php
Expand Up @@ -2,10 +2,11 @@

namespace Drupal\Tests\search_api_solr\Kernel;

use Drupal\search_api\Entity\Index;
use Drupal\search_api\Entity\Server;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\search_api\Entity\Index;
use Drupal\search_api\Entity\Server;
use Drupal\search_api_solr_test\Logger\InMemoryLogger;
use Drupal\Tests\search_api\Kernel\BackendTestBase;

/**
Expand Down Expand Up @@ -53,6 +54,11 @@ class SearchApiSolrLocationTest extends BackendTestBase {
*/
protected $waitForCommit = 2;

/**
* @var \Drupal\search_api_solr_test\Logger\InMemoryLogger
*/
protected $logger;

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -91,15 +97,24 @@ protected function commonSolrBackendSetUp() {
/** @var \Drupal\search_api\Entity\Index $index */
$index = Index::load($this->indexId);

$info = [
$location_info = [
'datasource_id' => 'entity:entity_test_mulrev_changed',
'property_path' => 'location',
'type' => 'location',
];

$rpt_info = [
'datasource_id' => 'entity:entity_test_mulrev_changed',
'property_path' => 'location',
'type' => 'rpt',
];
$fieldsHelper = $this->container->get('search_api.fields_helper');

$index->addField($fieldsHelper->createField($index, 'location', $info));
// Index location coordinates as location data type.
$index->addField($fieldsHelper->createField($index, 'location', $location_info));

// Index location coordinates as rpt data type.
$index->addField($fieldsHelper->createField($index, 'rpt', $rpt_info));

$index->save();

/** @var \Drupal\search_api\Entity\Server $server */
Expand All @@ -111,6 +126,18 @@ protected function commonSolrBackendSetUp() {
$server->save();

$this->indexItems($this->indexId);

/** @var \Drupal\Core\Logger\LoggerChannelFactoryInterface $loggerFactory */
$loggerFactory = \Drupal::service('logger.factory');
$loggerFactory->addLogger(new InMemoryLogger());
$this->logger = $loggerFactory->get('search_api_solr');
}

protected function assertLogMessage($level, $message, array $context = []) {
$last_message = $this->logger->getLastMessage();
$this->assertEquals($level, $last_message['level']);
$this->assertEquals($message, $last_message['message']);
$this->assertEquals($context, $last_message['context']);
}

/**
Expand Down Expand Up @@ -269,7 +296,7 @@ public function testBackend() {
];

$query = $this->buildSearch(NULL, [], NULL, FALSE)
->addCondition('location',['100', '1000'], 'BETWEEN')
->addCondition('location', ['100', '1000'], 'BETWEEN')
->sort('location__distance');

$query->setOption('search_api_location', $location_options);
Expand All @@ -286,6 +313,111 @@ public function testBackend() {
];

$this->assertEquals($expected, $facets, 'The correct location facets are returned');

// Tests the RPT data type of SearchApiSolrBackend.
$query = $this->buildSearch(NULL, [], NULL, FALSE);
$options = &$query->getOptions();
$options['search_api_facets']['rpt'] = [
'field' => 'rpt',
'limit' => 3,
'operator' => 'and',
'min_count' => 1,
'missing' => FALSE,
];
$options['search_api_rpt']['rpt'] = [
'field' => 'rpt',
'geom' => '["-180 -90" TO "180 90"]',
'gridLevel' => '2',
'maxCells' => '35554432',
'distErrPct' => '',
'distErr' => '',
'format' => 'ints2D',
];
$expected = [
[
'filter' => [
"gridLevel",
2,
"columns",
32,
"rows",
32,
"minX",
-180.0,
"maxX",
180.0,
"minY",
-90.0,
"maxY",
90.0,
"counts_ints2D",
[NULL, NULL, NULL, NULL, NULL, NULL, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], NULL, [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL],
],
'count' => 3,
],
];

$result = $query->execute();
$solr_version = $this->getServer()->getBackend()->getSolrConnector()->getSolrVersion();
if (version_compare($solr_version, 5.1, '>=')) {
$facets = $result->getExtraData('search_api_facets', [])['rpt'];
$this->assertEquals($expected, $facets, 'The correct location facets are returned');
}
else {
$this->assertLogMessage('error', 'Rpt data type feature is only supported by Solr version 5.1 or higher.');
}

$query = $this->buildSearch(NULL, [], NULL, FALSE);
$options = &$query->getOptions();
$options['search_api_facets']['rpt'] = [
'field' => 'rpt',
'limit' => 4,
'operator' => 'or',
'min_count' => 1,
'missing' => FALSE,
];
$options['search_api_rpt']['rpt'] = [
'field' => 'rpt',
'geom' => '["-60 -85" TO "130 70"]',
'gridLevel' => '2',
'maxCells' => '35554432',
'distErrPct' => '',
'distErr' => '',
'format' => 'ints2D',
];
$expected = [
[
'filter' => [
"gridLevel",
2,
"columns",
18,
"rows",
29,
"minX",
-67.5,
"maxX",
135.0,
"minY",
-90.0,
"maxY",
73.125,
"counts_ints2D",
[NULL, NULL, NULL, [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL],
],
'count' => 2,
],
];

$result = $query->execute();
if (version_compare($solr_version, 5.1, '>=')) {
$facets = $result->getExtraData('search_api_facets', [])['rpt'];
$this->assertEquals($expected, $facets, 'The correct location facets are returned');
}
else {
$this->assertLogMessage('error', 'Rpt data type feature is only supported by Solr version 5.1 or higher.');
}

}

/**
Expand Down

0 comments on commit c0590ca

Please sign in to comment.