Skip to content

Commit

Permalink
Issue #3402991 by joshi.rohit100, mkalkbrenner: Allow users to check …
Browse files Browse the repository at this point in the history
…for query and Index analysis of solr field
  • Loading branch information
Rohit Joshi authored and mkalkbrenner committed Nov 27, 2023
1 parent d2e148c commit 7e28ae0
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 0 deletions.
8 changes: 8 additions & 0 deletions modules/search_api_solr_admin/css/solr_field_analysis.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#analysis-result > div {
display: table;
border-spacing: 10px;
border-width: 2px;
border-style: solid;
border-color: black;
margin-bottom: 10px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
solr_field_analysis:
version: VERSION
css:
layout:
css/solr_field_analysis.css: {}
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ search_api_solr_admin.solr_reload_core_form:
title: 'Reload Core'
appears_on:
- entity.search_api_server.canonical

search_api_solr_admin.field_analysis:
route_name: search_api_solr_admin.field_analysis
title: 'Field Analysis'
appears_on:
- entity.search_api_server.canonical
14 changes: 14 additions & 0 deletions modules/search_api_solr_admin/search_api_solr_admin.module
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\search_api_solr\SolrBackendInterface;

/**
* Implements hook_theme().
*/
function search_api_solr_admin_theme($existing, $type, $theme, $path) {
return [
'solr_field_analysis' => [
'variables' => [
'data' => [],
'title' => NULL,
],
],
];
}

/**
* Implements hook_form_FORM_ID_alter().
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
'execute solr admin task':
title: 'Execute Solr admin task'
description: 'Access to execute Solr administrative tasks.'

'perform field analysis':
title: 'Perform field analysis'
description: 'Access to perform Solr field analysis.'
13 changes: 13 additions & 0 deletions modules/search_api_solr_admin/search_api_solr_admin.routing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,16 @@ search_api_solr_admin.solr_delete_collection_form:
requirements:
_search_api_solr_admin_cloud_access_check: 'TRUE'
_permission: 'execute solr admin task'

search_api_solr_admin.field_analysis:
path: '/admin/config/search/search-api/server/{search_api_server}/solr-admin/field-analysis'
defaults:
_form: '\Drupal\search_api_solr_admin\Form\SolrFieldAnalysisForm'
options:
parameters:
search_api_server:
type: entity:search_api_server
with_config_overrides: TRUE
requirements:
_search_api_solr_local_action_access_check: 'TRUE'
_permission: 'perform field analysis'
222 changes: 222 additions & 0 deletions modules/search_api_solr_admin/src/Form/SolrFieldAnalysisForm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
<?php

namespace Drupal\search_api_solr_admin\Form;

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\search_api\ServerInterface;
use Solarium\Core\Query\Result\ResultInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Form for solr field analysis.
*/
class SolrFieldAnalysisForm extends FormBase {

/**
* Search api server.
*
* @var \Drupal\search_api\ServerInterface
*/
protected $server;

/**
* Entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;

/**
* Constructor for SolrFieldAnalysisForm.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
);
}

/**
* {@inheritdoc}
*/
public function getFormId() {
return 'solr_field_analysis_form';
}

/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Nothing for submission. This is only because this method is abstract.
}

/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ServerInterface $search_api_server = NULL) {
$this->server = $search_api_server;

$form['index_query_details'] = [
'#type' => 'details',
'#title' => $this->t('Field values'),
'#open' => TRUE,
];

$form['index_query_details']['index_value'] = [
'#type' => 'textarea',
'#title' => $this->t('Field index value'),
];

$form['index_query_details']['query_value'] = [
'#type' => 'textfield',
'#title' => $this->t('Field query value'),
];

// Get solr field lists.
$list_builder = $this->entityTypeManager->getListBuilder('solr_field_type');
$list_builder->setServer($search_api_server);
$solr_field_types = $list_builder->load();

$solr_fields = [];
foreach ($solr_field_types as $solr_field_type) {
$solr_fields[$solr_field_type->getFieldTypeName()] = $solr_field_type->label();
}

$form['analysis_field'] = [
'#type' => 'select',
'#options' => $solr_fields,
'#required' => TRUE,
'#title' => $this->t('Solr field type'),
];

$form['#attached']['library'][] = 'search_api_solr_admin/solr_field_analysis';

$form['submit'] = [
'#type' => 'button',
'#value' => $this->t('Perform Analysis'),
'#ajax' => [
'callback' => '::ajaxFieldAnalysis',
],
];

$form['analysis_result'] = [
'#markup' => '<div id="analysis-result"></div>',
];

return $form;
}

/**
* Ajax callback for field analysis submission.
*
* @param array $form
* Form object.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state object.
*
* @return \Drupal\Core\Ajax\AjaxResponse
* Ajax Response.
*
* @throws \Drupal\search_api_solr\SearchApiSolrException
*/
public function ajaxFieldAnalysis(array $form, FormStateInterface $form_state): AjaxResponse {
$field = $form_state->getValue('analysis_field');
$index_value = trim($form_state->getValue('index_value'));
$query_value = trim($form_state->getValue('query_value'));

$build = [];

if ($field && ($index_value || $query_value)) {
/** @var \Drupal\search_api_solr\SolrBackendInterface $backend */
$backend = $this->server->getBackend();
$connector = $backend->getSolrConnector();
$analysisQueryField = $connector->getAnalysisQueryField();
$analysisQueryField->setFieldType($field);
if ($index_value) {
$analysisQueryField->setFieldValue($index_value);
}
if ($query_value) {
$analysisQueryField->setQuery($query_value);
}
$analysisQueryField->setShowMatch(TRUE);

$results = $connector->analyze($analysisQueryField);

if ($index_value) {
// Prepare index analysis rendering data.
$index_processed_data = $this->getIndexDataFromResult($results, 'index');
$build[] = [
'#theme' => 'solr_field_analysis',
'#title' => $this->t('Index Analysis'),
'#data' => $index_processed_data,
];
}

if ($query_value) {
// Prepare query analysis rendering data.
$query_processed_data = $this->getIndexDataFromResult($results, 'query');
$build[] = [
'#theme' => 'solr_field_analysis',
'#title' => $this->t('Query Analysis'),
'#data' => $query_processed_data,
];
}
}

$ajax_response = new AjaxResponse();
$ajax_response->addCommand(new HtmlCommand('#analysis-result', $build));

return $ajax_response;
}

/**
* Prepare the Index data result set for rendering.
*
* @param \Solarium\Core\Query\Result\ResultInterface $results
* Analyse query result.
* @param string $type
* Type of analysis - 'index' or 'query'.
*/
protected function getIndexDataFromResult(ResultInterface $results, string $type): array {
$data = [];
foreach ($results as $result) {
foreach ($result as $item) {
if ($type === 'query') {
$indexAnalysis = $item->getQueryAnalysis();
}
else {
$indexAnalysis = $item->getIndexAnalysis();
}

if (!empty($indexAnalysis)) {
foreach ($indexAnalysis as $classes) {
$class_name = $classes->getName();
$exploded_name = explode('.', $class_name);
$class_name = end($exploded_name);
$data[$class_name] = [];
foreach ($classes as $class) {
$data[$class_name][] = [
'text' => $class->getText(),
'raw_text' => $class->getRawText(),
'matches' => $class->getMatch(),
];
}
}
}
}
}

return $data;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<div>
<h2>{{ title }}</h2>
<table>
{% for class, texts in data %}
<tr>
<th>{{ class }}</th>
{% for text in texts %}
<td>
{% if text.matches %}
<mark>{{ text.text }}</mark>
{% else %}
{{ text.text }}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
25 changes: 25 additions & 0 deletions src/SolrConnector/SolrConnectorPluginBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
use Solarium\Core\Client\Response;
use Solarium\Core\Query\QueryInterface;
use Solarium\Exception\HttpException;
use Solarium\QueryType\Analysis\Query\AbstractQuery;
use Solarium\QueryType\Analysis\Query\Field;
use Solarium\QueryType\Extract\Result as ExtractResult;
use Solarium\QueryType\Select\Query\Query;
use Solarium\QueryType\Update\Query\Query as UpdateQuery;
Expand Down Expand Up @@ -893,6 +895,14 @@ public function getExtractQuery() {
return $this->solr->createExtract();
}

/**
* {@inheritdoc}
*/
public function getAnalysisQueryField(): Field {
$this->connect();
return $this->solr->createAnalysisField();
}

/**
* Creates a CustomizeRequest object.
*
Expand Down Expand Up @@ -1001,6 +1011,21 @@ public function autocomplete(AutocompleteQuery $query, ?Endpoint $endpoint = NUL
return $this->execute($query, $endpoint);
}

/**
* {@inheritdoc}
*/
public function analyze(AbstractQuery $query, ?Endpoint $endpoint = NULL) {
$this->connect();

if (!$endpoint) {
$endpoint = $this->solr->getEndpoint();
}

$this->useTimeout(self::QUERY_TIMEOUT, $endpoint);

return $this->execute($query, $endpoint);
}

/**
* {@inheritdoc}
*/
Expand Down
Loading

0 comments on commit 7e28ae0

Please sign in to comment.